// // AppDelegate.m // 速达驾考 // // Created by 张嵘 on 2021/7/23. // #import "AppDelegate.h" #import "DOUAudioStreamer+Options.h" #import "WXApi.h" #import // GetuiSdk头文件 #import #import #import @interface AppDelegate () /// APP管理的导航栏的堆栈 @property (nonatomic, readwrite, strong) RQNavigationControllerStack *navigationControllerStack; /// APP的服务层 @property (nonatomic, readwrite, strong) RQViewModelServicesImpl *services; @end @implementation AppDelegate @synthesize window; #pragma mark- 获取appDelegate + (AppDelegate *)sharedDelegate { return (AppDelegate *)[[UIApplication sharedApplication] delegate]; } //程序开始 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // [super application:application didFinishLaunchingWithOptions:launchOptions]; /// 初始化UI之前配置 [self rq_configureApplication:application initialParamsBeforeInitUI:launchOptions]; // Config Service self.services = [[RQViewModelServicesImpl alloc] init]; // Config Nav Stack self.navigationControllerStack = [[RQNavigationControllerStack alloc] initWithServices:self.services]; // Configure Window self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] currentBounds]]; self.window.backgroundColor = [UIColor whiteColor]; self.window.qmui_capturesStatusBarAppearance = NO; // 重置rootViewController [self.services resetRootViewModel:[self rq_createInitialViewModel]]; // 让窗口可见 [self.window makeKeyAndVisible]; // 初始化UI后配置 [self rq_configureApplication:application initialParamsAfterInitUI:launchOptions]; #if defined(DEBUG)||defined(_DEBUG) /// 调试模式 [self rq_configDebugModelTools]; #endif // Save the application version info. must write last [[NSUserDefaults standardUserDefaults] setValue:RQ_APP_VERSION forKey:RQApplicationVersionKey]; [[NSUserDefaults standardUserDefaults] synchronize]; return YES; } #pragma mark - 在初始化UI之前配置 - (void)rq_configureApplication:(UIApplication *)application initialParamsBeforeInitUI:(NSDictionary *)launchOptions { /// 检测网络权限 [RQ_HTTP_Service networkAuthStatus]; // 配置键盘 [self rq_configureKeyboardManager]; // 配置文件夹 [self rq_configureApplicationDirectory]; // 配置FMDB [self rq_configureFMDB]; // 配置流音乐播放 [DOUAudioStreamer setOptions:[DOUAudioStreamer options] | DOUAudioStreamerRequireSHA256]; // 配置微信 [self rq_configureWechat]; // 配置个推 [self rq_configureGeTui]; // 配置Bugly [self rq_configureBugly]; // 获取视频 [self rq_configureVideoes]; // 监控自动读题 [self rq_autoRead]; /// 配置抖音 [RQ_DOUYIN_MANAGER initDouYinManagerWithApplication:application didFinishLaunchingWithOptions:launchOptions]; } // 配置键盘管理器 - (void)rq_configureKeyboardManager { IQKeyboardManager.sharedManager.enable = YES; IQKeyboardManager.sharedManager.enableAutoToolbar = NO; IQKeyboardManager.sharedManager.shouldResignOnTouchOutside = YES; } // 配置文件夹 - (void)rq_configureApplicationDirectory { /// 创建doc [RQFileManager createDirectoryAtPath:RQWeChatDocDirPath()]; /// 创建cache [RQFileManager createDirectoryAtPath:RQWeChatCacheDirPath()]; // NSLog(@"RQWeChatDocDirPath is [ %@ ] \n RQWeChatCacheDirPath is [ %@ ]" , RQWeChatDocDirPath() , RQWeChatCacheDirPath()); } /// 配置FMDB - (void)rq_configureFMDB { // FMDatabaseQueue *databaseQueue = [[FMDatabaseQueue alloc] init]; // [databaseQueue inDatabase:^(FMDatabase *db) { // NSString *version = [[NSUserDefaults standardUserDefaults] valueForKey:RQApplicationVersionKey]; // if (![version isEqualToString:RQ_APP_VERSION]) { // NSString *path = [[NSBundle mainBundle] pathForResource:@"question20211105" ofType:@"db"]; // NSString *sql = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; // /// 执行文件 // if (![db executeStatements:sql]) { // RQLogLastError(db); // } // } // }]; NSFileManager *fileManager =[NSFileManager defaultManager]; NSError*error; if ([fileManager fileExistsAtPath:[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"question.db"]]]== NO) { NSString*resourcePath =[[NSBundle mainBundle] pathForResource:@"question" ofType:@"db"]; [fileManager copyItemAtPath:resourcePath toPath:[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"question.db"]] error:&error]; } if (RQ_USER_MANAGER.isLogin) { // [RQ_UPDATE_LOCALDATA_MANAGER updateLocalWrongAndCollectionWithComplete:nil]; [RQ_USER_MANAGER isVipWithComplete:^(NSInteger isVip) {}]; } } - (void)rq_configureWechat { /// 向微信注册 [RQ_WECHAT_MANAGER initWechatManager]; } - (void)rq_configureGeTui { // 通过个推平台分配的appId、 appKey 、appSecret 启动SDK,注:该方法需要在主线程中调用 [GeTuiSdk startSdkWithAppId:@"yeBmFCmPHu8Up6cFXonxU1" appKey:@"0Mp7hV8p6SAyz3ylcGOmo4" appSecret:@"F3oofsCQJT7vPo4zcYmRV4" delegate:self launchingOptions:nil]; // 注册远程通知 if (@available(iOS 10.0, *)) { [GeTuiSdk registerRemoteNotification: (UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge)]; } else { // Fallback on earlier versions } } - (void)rq_configureBugly { [Bugly startWithAppId:@"713fbad4c6"]; } - (void)rq_configureVideoes { [[RQ_HTTP_Service getTreeList] subscribeNext:^(NSArray * dataArr) { if (!RQObjectIsNil(dataArr)) { RQ_COMMON_MANAGER.treeListArr = dataArr; } }]; [[RQ_HTTP_Service getDspList] subscribeNext:^(NSArray * videosItemArr) { if (!RQObjectIsNil(videosItemArr)) { RQ_COMMON_MANAGER.dspListArr = videosItemArr; } }]; } - (void)rq_autoRead { [RQ_Exercise_Module rq_RACIsAutoRead]; } #pragma mark - 在初始化UI之后配置 - (void)rq_configureApplication:(UIApplication *)application initialParamsAfterInitUI:(NSDictionary *)launchOptions { [RQ_CACHE_MANAGER removeAllCache]; // [RQ_AD_MANAGER loadAdWithAdType:RQADType_InterstitialSplash customView:nil]; /// 配置ActionSheet [LCActionSheet rq_configureActionSheet]; /// 预先配置平台信息 // [SBUMengService configureUMengPreDefinePlatforms]; @weakify(self); /// 监听切换根控制器的通知 [[RQNotificationCenter rac_addObserverForName:RQSwitchRootViewControllerNotification object:nil] subscribeNext:^(NSNotification * note) { /// 这里切换根控制器 @strongify(self); // 重置rootViewController RQSwitchRootViewControllerFromType fromType = [note.userInfo[RQSwitchRootViewControllerUserInfoKey] integerValue]; NSLog(@"fromType is %zd" , fromType); /// 切换根控制器 [self.services resetRootViewModel:[self rq_createInitialViewModel]]; /// 切换了根控制器,切记需要将指示器 移到window的最前面 #if defined(DEBUG)||defined(_DEBUG) // [self.window bringSubviewToFront:[RQDebugTouchView sharedInstance]]; #endif }]; /// 配置H5 // [SBConfigureManager configure]; if (@available(iOS 15.0, *)) { [UITableView appearance].sectionHeaderTopPadding = 0; } [RQ_CACHE_MANAGER removeAllCache]; [RQ_COMMON_MANAGER checkAppStoreVersionWithComplete:^(BOOL isNeedUpdate) { if (isNeedUpdate) { [RQ_Question_Module rq_showAppUpdateView]; } }]; /// 更新题库 [RQ_Question_Module rq_updateQuestion]; [[RQ_HTTP_Service getConfigKeyWithPath:RQSDJKAPPICONNEEDCHANGEPath] subscribeNext:^(id _Nullable x) { RQ_COMMON_MANAGER.SDJK_APP_ICON_NEED_CHANGE = [x boolValue]; }]; [[RQ_HTTP_Service getDictTypeWithPath:RQVipTypePath] subscribeNext:^(id _Nullable x) { NSArray *a = RQObjectIsNil(x)? @[] : x; RQ_COMMON_MANAGER.vipTypeListArr = a; }]; [RQ_AD_MANAGER loadAdWithAdType:RQADType_Splash customView:nil]; } #pragma mark - 调试(DEBUG)模式下的工具条 - (void)rq_configDebugModelTools { /// 显示FPS [[JPFPSStatus sharedInstance] open]; [QMUIConsole sharedInstance].canShow = YES; /// 打开调试按钮 [RQDebugTouchView sharedInstance]; /// RQ Fixed: 切换了根控制器,切记需要将指示器 移到window的最前面 [self.window bringSubviewToFront:[RQDebugTouchView sharedInstance]]; } #pragma mark - 创建根控制器 - (RQBaseViewModel *)rq_createInitialViewModel { // The user has logged-in. // NSString *version = [[NSUserDefaults standardUserDefaults] valueForKey:RQApplicationVersionKey]; /// 版本不一样就先走 新特性界面 // if (![version isEqualToString:RQ_APP_VERSION]){ // return [[RQNewFeatureViewModel alloc] initWithServices:self.services params:nil]; // }else{ /// 这里判断一下 // if ([SAMKeychain rawLogin] && self.services.client.currentUser) { /// 有账号+有用户数据 /// 已经登录,跳转到主页 return [[RQMainTabBarViewModel alloc] initWithServices:self.services params:nil]; // }else if(self.services.client.currentUser){ /// 没账号+有用户数据 /// 跳转到账户登录页面 // return [[RQAccountLoginViewModel alloc] initWithServices:self.services params:nil]; // }else{ /// 第一次使用微信 // return [[RQBootLoginViewModel alloc] initWithServices:self.services params:nil]; // } // } } /** 对文件重命名 @param filePath 旧路径 @return 新路径 */ - (NSString *)p_setupFileRename:(NSString *)filePath { NSString *lastPathComponent = [NSString new]; //获取文件名: 视频.MP4 lastPathComponent = [filePath lastPathComponent]; //获取后缀:MP4 NSString *pathExtension = [filePath pathExtension]; //用传过来的路径创建新路径 首先去除文件名 NSString *pathNew = [filePath stringByReplacingOccurrencesOfString:lastPathComponent withString:@""]; //然后拼接新文件名:新文件名为当前的:年月日时分秒 yyyyMMddHHmmss NSString *moveToPath = [NSString stringWithFormat:@"%@%@.%@",pathNew,[self htmi_getCurrentTime],pathExtension]; NSFileManager *fileManager = [NSFileManager defaultManager]; //通过移动该文件对文件重命名 BOOL isSuccess = [fileManager moveItemAtPath:filePath toPath:moveToPath error:nil]; if (isSuccess) { NSLog(@"rename success"); }else{ NSLog(@"rename fail"); } return moveToPath; } /** 获取当地时间 @return 获取当地时间 */ - (NSString *)htmi_getCurrentTime { NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"yyyyMMddHHmmss"]; NSString *dateTime = [formatter stringFromDate:[NSDate date]]; return dateTime; } - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray> * __nullable restorableObjects))restorationHandler { return [WXApi handleOpenUniversalLink:userActivity delegate:RQ_WECHAT_MANAGER]; } //- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { // return [WXApi handleOpenURL:url delegate:RQ_WECHAT_MANAGER]; //} #pragma mark - 推送(个推) /// 通知展示(iOS10及以上版本) /// @param center center /// @param notification notification /// @param completionHandler completionHandler - (void)GeTuiSdkNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification completionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler API_AVAILABLE(ios(10.0)){ NSString *msg = [NSString stringWithFormat:@"[ TestDemo ] [APNs] %@ \n%@", NSStringFromSelector(_cmd), notification.request.content.userInfo]; NSLog(@"-----个推-----通知展示----%@",msg); // [ 参考代码,开发者注意根据实际需求自行修改 ] 根据APP需要,判断是否要提示用户Badge、Sound、Alert等 //completionHandler(UNNotificationPresentationOptionNone); 若不显示通知,则无法点击通知 completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert); } /// 收到通知信息 /// @param userInfo apns通知内容 /// @param center UNUserNotificationCenter(iOS10及以上版本) /// @param response UNNotificationResponse(iOS10及以上版本) /// @param completionHandler 用来在后台状态下进行操作(iOS10以下版本) - (void)GeTuiSdkDidReceiveNotification:(NSDictionary *)userInfo notificationCenter:(UNUserNotificationCenter *)center response:(UNNotificationResponse *)response fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler API_AVAILABLE(ios(10.0)){ NSString *msg = [NSString stringWithFormat:@"[ TestDemo ] [APNs] %@ \n%@", NSStringFromSelector(_cmd), userInfo]; NSLog(@"-----个推-----收到通知信息----%@",msg); if(completionHandler) { // [ 参考代码,开发者注意根据实际需求自行修改 ] 根据APP需要自行修改参数值 completionHandler(UIBackgroundFetchResultNoData); } } /// 收到透传消息 /// @param userInfo 推送消息内容 /// @param fromGetui YES: 个推通道 NO:苹果apns通道 /// @param offLine 是否是离线消息,YES.是离线消息 /// @param appId 应用的appId /// @param taskId 推送消息的任务id /// @param msgId 推送消息的messageid /// @param completionHandler 用来在后台状态下进行操作(通过苹果apns通道的消息 才有此参数值) - (void)GeTuiSdkDidReceiveSlience:(NSDictionary *)userInfo fromGetui:(BOOL)fromGetui offLine:(BOOL)offLine appId:(NSString *)appId taskId:(NSString *)taskId msgId:(NSString *)msgId fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { // [ GTSDK ]:汇报个推自定义事件(反馈透传消息),开发者可以根据项目需要决定是否使用, 非必须 // [GeTuiSdk sendFeedbackMessage:90001 andTaskId:taskId andMsgId:msgId]; NSString *msg = [NSString stringWithFormat:@"[ TestDemo ] [APN] %@ \nReceive Slience: fromGetui:%@ appId:%@ offLine:%@ taskId:%@ msgId:%@ userInfo:%@ ", NSStringFromSelector(_cmd), fromGetui ? @"个推消息" : @"APNs消息", appId, offLine ? @"离线" : @"在线", taskId, msgId, userInfo]; NSLog(@"-----个推-----收到透传消息----%@",msg); if(completionHandler) { // [ 参考代码,开发者注意根据实际需求自行修改 ] 根据APP需要自行修改参数值 completionHandler(UIBackgroundFetchResultNoData); } } - (void)GeTuiSdkNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(UNNotification *)notification API_AVAILABLE(ios(10.0)){ // [ 参考代码,开发者注意根据实际需求自行修改 ] 根据APP需要自行修改参数值 } - (void)GeTuiSdkDidOccurError:(NSError *)error { NSString *msg = [NSString stringWithFormat:@"[ TestDemo ] [GeTuiSdk GeTuiSdkDidOccurError]:%@\n\n",error.localizedDescription]; // SDK发生错误时,回调异常错误信息 NSLog(@"%@", msg); } /** SDK启动成功返回cid */ - (void)GeTuiSdkDidRegisterClient:(NSString *)clientId { //个推SDK已注册,返回clientId NSLog(@"\n>>>[GeTuiSdk RegisterClient]:%@\n\n", clientId); } #pragma mark - DouyinOpenSDKLogDelegate - (void)onLog:(nonnull NSString *)logInfo { } - (BOOL)application:(UIApplication *)application openURL:(nonnull NSURL *)url options:(nonnull NSDictionary *)options { if ([[DouyinOpenSDKApplicationDelegate sharedInstance] application:application openURL:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] annotation:options[UIApplicationOpenURLOptionsAnnotationKey]] ) { return YES; } return [WXApi handleOpenURL:url delegate:RQ_WECHAT_MANAGER]; } /// 在这里写支持的旋转方向,为了防止横屏方向,应用启动时候界面变为横屏模式 //- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { // if (self.allowOrentitaionRotation) { // return UIInterfaceOrientationMaskAllButUpsideDown; // } // return UIInterfaceOrientationMaskPortrait; //} - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { ZFInterfaceOrientationMask orientationMask = [ZFLandscapeRotationManager supportedInterfaceOrientationsForWindow:window]; if (orientationMask != ZFInterfaceOrientationMaskUnknow) { return (UIInterfaceOrientationMask)orientationMask; } /// 这里是非播放器VC支持的方向 return UIInterfaceOrientationMaskPortrait; } @end