AppDelegate.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. //
  2. // AppDelegate.m
  3. // 速达驾考
  4. //
  5. // Created by 张嵘 on 2021/7/23.
  6. //
  7. #import "AppDelegate.h"
  8. #import "DOUAudioStreamer+Options.h"
  9. #import "WXApi.h"
  10. #import <GTSDK/GeTuiSdk.h> // GetuiSdk头文件
  11. #import <Bugly/Bugly.h>
  12. #import <DouyinOpenSDK/DouyinOpenSDKAuth.h>
  13. #import <ZFPlayer/ZFLandscapeRotationManager.h>
  14. @interface AppDelegate () <WXApiDelegate, GeTuiSdkDelegate>
  15. /// APP管理的导航栏的堆栈
  16. @property (nonatomic, readwrite, strong) RQNavigationControllerStack *navigationControllerStack;
  17. /// APP的服务层
  18. @property (nonatomic, readwrite, strong) RQViewModelServicesImpl *services;
  19. @end
  20. @implementation AppDelegate
  21. @synthesize window;
  22. #pragma mark- 获取appDelegate
  23. + (AppDelegate *)sharedDelegate {
  24. return (AppDelegate *)[[UIApplication sharedApplication] delegate];
  25. }
  26. //程序开始
  27. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  28. // [super application:application didFinishLaunchingWithOptions:launchOptions];
  29. /// 初始化UI之前配置
  30. [self rq_configureApplication:application initialParamsBeforeInitUI:launchOptions];
  31. // Config Service
  32. self.services = [[RQViewModelServicesImpl alloc] init];
  33. // Config Nav Stack
  34. self.navigationControllerStack = [[RQNavigationControllerStack alloc] initWithServices:self.services];
  35. // Configure Window
  36. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] currentBounds]];
  37. self.window.backgroundColor = [UIColor whiteColor];
  38. self.window.qmui_capturesStatusBarAppearance = NO;
  39. // 重置rootViewController
  40. [self.services resetRootViewModel:[self rq_createInitialViewModel]];
  41. // 让窗口可见
  42. [self.window makeKeyAndVisible];
  43. // 初始化UI后配置
  44. [self rq_configureApplication:application initialParamsAfterInitUI:launchOptions];
  45. #if defined(DEBUG)||defined(_DEBUG)
  46. /// 调试模式
  47. [self rq_configDebugModelTools];
  48. #endif
  49. // Save the application version info. must write last
  50. [[NSUserDefaults standardUserDefaults] setValue:RQ_APP_VERSION forKey:RQApplicationVersionKey];
  51. [[NSUserDefaults standardUserDefaults] synchronize];
  52. return YES;
  53. }
  54. #pragma mark - 在初始化UI之前配置
  55. - (void)rq_configureApplication:(UIApplication *)application initialParamsBeforeInitUI:(NSDictionary *)launchOptions {
  56. /// 检测网络权限
  57. [RQ_HTTP_Service networkAuthStatus];
  58. // 配置键盘
  59. [self rq_configureKeyboardManager];
  60. // 配置文件夹
  61. [self rq_configureApplicationDirectory];
  62. // 配置FMDB
  63. [self rq_configureFMDB];
  64. // 配置流音乐播放
  65. [DOUAudioStreamer setOptions:[DOUAudioStreamer options] | DOUAudioStreamerRequireSHA256];
  66. // 配置微信
  67. [self rq_configureWechat];
  68. // 配置个推
  69. [self rq_configureGeTui];
  70. // 配置Bugly
  71. [self rq_configureBugly];
  72. // 获取视频
  73. [self rq_configureVideoes];
  74. // 监控自动读题
  75. [self rq_autoRead];
  76. /// 配置抖音
  77. [RQ_DOUYIN_MANAGER initDouYinManagerWithApplication:application didFinishLaunchingWithOptions:launchOptions];
  78. }
  79. // 配置键盘管理器
  80. - (void)rq_configureKeyboardManager {
  81. IQKeyboardManager.sharedManager.enable = YES;
  82. IQKeyboardManager.sharedManager.enableAutoToolbar = NO;
  83. IQKeyboardManager.sharedManager.shouldResignOnTouchOutside = YES;
  84. }
  85. // 配置文件夹
  86. - (void)rq_configureApplicationDirectory {
  87. /// 创建doc
  88. [RQFileManager createDirectoryAtPath:RQWeChatDocDirPath()];
  89. /// 创建cache
  90. [RQFileManager createDirectoryAtPath:RQWeChatCacheDirPath()];
  91. // NSLog(@"RQWeChatDocDirPath is [ %@ ] \n RQWeChatCacheDirPath is [ %@ ]" , RQWeChatDocDirPath() , RQWeChatCacheDirPath());
  92. }
  93. /// 配置FMDB
  94. - (void)rq_configureFMDB {
  95. // FMDatabaseQueue *databaseQueue = [[FMDatabaseQueue alloc] init];
  96. // [databaseQueue inDatabase:^(FMDatabase *db) {
  97. // NSString *version = [[NSUserDefaults standardUserDefaults] valueForKey:RQApplicationVersionKey];
  98. // if (![version isEqualToString:RQ_APP_VERSION]) {
  99. // NSString *path = [[NSBundle mainBundle] pathForResource:@"question20211105" ofType:@"db"];
  100. // NSString *sql = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
  101. // /// 执行文件
  102. // if (![db executeStatements:sql]) {
  103. // RQLogLastError(db);
  104. // }
  105. // }
  106. // }];
  107. NSFileManager *fileManager =[NSFileManager defaultManager];
  108. NSError*error;
  109. if ([fileManager fileExistsAtPath:[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"question.db"]]]== NO) {
  110. NSString*resourcePath =[[NSBundle mainBundle] pathForResource:@"question" ofType:@"db"];
  111. [fileManager copyItemAtPath:resourcePath toPath:[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"question.db"]] error:&error];
  112. }
  113. if (RQ_USER_MANAGER.isLogin) {
  114. // [RQ_UPDATE_LOCALDATA_MANAGER updateLocalWrongAndCollectionWithComplete:nil];
  115. [RQ_USER_MANAGER isVipWithComplete:^(NSInteger isVip) {}];
  116. }
  117. }
  118. - (void)rq_configureWechat {
  119. /// 向微信注册
  120. [RQ_WECHAT_MANAGER initWechatManager];
  121. }
  122. - (void)rq_configureGeTui {
  123. // 通过个推平台分配的appId、 appKey 、appSecret 启动SDK,注:该方法需要在主线程中调用
  124. [GeTuiSdk startSdkWithAppId:@"yeBmFCmPHu8Up6cFXonxU1" appKey:@"0Mp7hV8p6SAyz3ylcGOmo4" appSecret:@"F3oofsCQJT7vPo4zcYmRV4" delegate:self launchingOptions:nil];
  125. // 注册远程通知
  126. if (@available(iOS 10.0, *)) {
  127. [GeTuiSdk registerRemoteNotification: (UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge)];
  128. } else {
  129. // Fallback on earlier versions
  130. }
  131. }
  132. - (void)rq_configureBugly {
  133. [Bugly startWithAppId:@"713fbad4c6"];
  134. }
  135. - (void)rq_configureVideoes {
  136. [[RQ_HTTP_Service getTreeList] subscribeNext:^(NSArray <RQTreeListModel *> * dataArr) {
  137. if (!RQObjectIsNil(dataArr)) {
  138. RQ_COMMON_MANAGER.treeListArr = dataArr;
  139. }
  140. }];
  141. [[RQ_HTTP_Service getDspList] subscribeNext:^(NSArray <VideosItem *> * videosItemArr) {
  142. if (!RQObjectIsNil(videosItemArr)) {
  143. RQ_COMMON_MANAGER.dspListArr = videosItemArr;
  144. }
  145. }];
  146. }
  147. - (void)rq_autoRead {
  148. [RQ_Exercise_Module rq_RACIsAutoRead];
  149. }
  150. #pragma mark - 在初始化UI之后配置
  151. - (void)rq_configureApplication:(UIApplication *)application initialParamsAfterInitUI:(NSDictionary *)launchOptions {
  152. [RQ_CACHE_MANAGER removeAllCache];
  153. // [RQ_AD_MANAGER loadAdWithAdType:RQADType_InterstitialSplash customView:nil];
  154. /// 配置ActionSheet
  155. [LCActionSheet rq_configureActionSheet];
  156. /// 预先配置平台信息
  157. // [SBUMengService configureUMengPreDefinePlatforms];
  158. @weakify(self);
  159. /// 监听切换根控制器的通知
  160. [[RQNotificationCenter rac_addObserverForName:RQSwitchRootViewControllerNotification object:nil] subscribeNext:^(NSNotification * note) {
  161. /// 这里切换根控制器
  162. @strongify(self);
  163. // 重置rootViewController
  164. RQSwitchRootViewControllerFromType fromType = [note.userInfo[RQSwitchRootViewControllerUserInfoKey] integerValue];
  165. NSLog(@"fromType is %zd" , fromType);
  166. /// 切换根控制器
  167. [self.services resetRootViewModel:[self rq_createInitialViewModel]];
  168. /// 切换了根控制器,切记需要将指示器 移到window的最前面
  169. #if defined(DEBUG)||defined(_DEBUG)
  170. // [self.window bringSubviewToFront:[RQDebugTouchView sharedInstance]];
  171. #endif
  172. }];
  173. /// 配置H5
  174. // [SBConfigureManager configure];
  175. if (@available(iOS 15.0, *)) {
  176. [UITableView appearance].sectionHeaderTopPadding = 0;
  177. }
  178. [RQ_CACHE_MANAGER removeAllCache];
  179. [RQ_COMMON_MANAGER checkAppStoreVersionWithComplete:^(BOOL isNeedUpdate) {
  180. if (isNeedUpdate) {
  181. [RQ_Question_Module rq_showAppUpdateView];
  182. }
  183. }];
  184. /// 更新题库
  185. [RQ_Question_Module rq_updateQuestion];
  186. [[RQ_HTTP_Service getConfigKeyWithPath:RQSDJKAPPICONNEEDCHANGEPath] subscribeNext:^(id _Nullable x) {
  187. RQ_COMMON_MANAGER.SDJK_APP_ICON_NEED_CHANGE = [x boolValue];
  188. }];
  189. [[RQ_HTTP_Service getDictTypeWithPath:RQVipTypePath] subscribeNext:^(id _Nullable x) {
  190. NSArray *a = RQObjectIsNil(x)? @[] : x;
  191. RQ_COMMON_MANAGER.vipTypeListArr = a;
  192. }];
  193. [RQ_AD_MANAGER loadAdWithAdType:RQADType_Splash customView:nil];
  194. }
  195. #pragma mark - 调试(DEBUG)模式下的工具条
  196. - (void)rq_configDebugModelTools {
  197. /// 显示FPS
  198. [[JPFPSStatus sharedInstance] open];
  199. [QMUIConsole sharedInstance].canShow = YES;
  200. /// 打开调试按钮
  201. [RQDebugTouchView sharedInstance];
  202. /// RQ Fixed: 切换了根控制器,切记需要将指示器 移到window的最前面
  203. [self.window bringSubviewToFront:[RQDebugTouchView sharedInstance]];
  204. }
  205. #pragma mark - 创建根控制器
  206. - (RQBaseViewModel *)rq_createInitialViewModel {
  207. // The user has logged-in.
  208. // NSString *version = [[NSUserDefaults standardUserDefaults] valueForKey:RQApplicationVersionKey];
  209. /// 版本不一样就先走 新特性界面
  210. // if (![version isEqualToString:RQ_APP_VERSION]){
  211. // return [[RQNewFeatureViewModel alloc] initWithServices:self.services params:nil];
  212. // }else{
  213. /// 这里判断一下
  214. // if ([SAMKeychain rawLogin] && self.services.client.currentUser) {
  215. /// 有账号+有用户数据
  216. /// 已经登录,跳转到主页
  217. return [[RQMainTabBarViewModel alloc] initWithServices:self.services params:nil];
  218. // }else if(self.services.client.currentUser){ /// 没账号+有用户数据
  219. /// 跳转到账户登录页面
  220. // return [[RQAccountLoginViewModel alloc] initWithServices:self.services params:nil];
  221. // }else{
  222. /// 第一次使用微信
  223. // return [[RQBootLoginViewModel alloc] initWithServices:self.services params:nil];
  224. // }
  225. // }
  226. }
  227. /**
  228. 对文件重命名
  229. @param filePath 旧路径
  230. @return 新路径
  231. */
  232. - (NSString *)p_setupFileRename:(NSString *)filePath {
  233. NSString *lastPathComponent = [NSString new];
  234. //获取文件名: 视频.MP4
  235. lastPathComponent = [filePath lastPathComponent];
  236. //获取后缀:MP4
  237. NSString *pathExtension = [filePath pathExtension];
  238. //用传过来的路径创建新路径 首先去除文件名
  239. NSString *pathNew = [filePath stringByReplacingOccurrencesOfString:lastPathComponent withString:@""];
  240. //然后拼接新文件名:新文件名为当前的:年月日时分秒 yyyyMMddHHmmss
  241. NSString *moveToPath = [NSString stringWithFormat:@"%@%@.%@",pathNew,[self htmi_getCurrentTime],pathExtension];
  242. NSFileManager *fileManager = [NSFileManager defaultManager];
  243. //通过移动该文件对文件重命名
  244. BOOL isSuccess = [fileManager moveItemAtPath:filePath toPath:moveToPath error:nil];
  245. if (isSuccess) {
  246. NSLog(@"rename success");
  247. }else{
  248. NSLog(@"rename fail");
  249. }
  250. return moveToPath;
  251. }
  252. /**
  253. 获取当地时间
  254. @return 获取当地时间
  255. */
  256. - (NSString *)htmi_getCurrentTime {
  257. NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
  258. [formatter setDateFormat:@"yyyyMMddHHmmss"];
  259. NSString *dateTime = [formatter stringFromDate:[NSDate date]];
  260. return dateTime;
  261. }
  262. - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray<id<UIUserActivityRestoring>> * __nullable restorableObjects))restorationHandler {
  263. return [WXApi handleOpenUniversalLink:userActivity delegate:RQ_WECHAT_MANAGER];
  264. }
  265. //- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options {
  266. // return [WXApi handleOpenURL:url delegate:RQ_WECHAT_MANAGER];
  267. //}
  268. #pragma mark - 推送(个推)
  269. /// 通知展示(iOS10及以上版本)
  270. /// @param center center
  271. /// @param notification notification
  272. /// @param completionHandler completionHandler
  273. - (void)GeTuiSdkNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification completionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler API_AVAILABLE(ios(10.0)){
  274. NSString *msg = [NSString stringWithFormat:@"[ TestDemo ] [APNs] %@ \n%@", NSStringFromSelector(_cmd), notification.request.content.userInfo];
  275. NSLog(@"-----个推-----通知展示----%@",msg);
  276. // [ 参考代码,开发者注意根据实际需求自行修改 ] 根据APP需要,判断是否要提示用户Badge、Sound、Alert等
  277. //completionHandler(UNNotificationPresentationOptionNone); 若不显示通知,则无法点击通知
  278. completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert);
  279. }
  280. /// 收到通知信息
  281. /// @param userInfo apns通知内容
  282. /// @param center UNUserNotificationCenter(iOS10及以上版本)
  283. /// @param response UNNotificationResponse(iOS10及以上版本)
  284. /// @param completionHandler 用来在后台状态下进行操作(iOS10以下版本)
  285. - (void)GeTuiSdkDidReceiveNotification:(NSDictionary *)userInfo notificationCenter:(UNUserNotificationCenter *)center response:(UNNotificationResponse *)response fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler API_AVAILABLE(ios(10.0)){
  286. NSString *msg = [NSString stringWithFormat:@"[ TestDemo ] [APNs] %@ \n%@", NSStringFromSelector(_cmd), userInfo];
  287. NSLog(@"-----个推-----收到通知信息----%@",msg);
  288. if(completionHandler) {
  289. // [ 参考代码,开发者注意根据实际需求自行修改 ] 根据APP需要自行修改参数值
  290. completionHandler(UIBackgroundFetchResultNoData);
  291. }
  292. }
  293. /// 收到透传消息
  294. /// @param userInfo 推送消息内容
  295. /// @param fromGetui YES: 个推通道 NO:苹果apns通道
  296. /// @param offLine 是否是离线消息,YES.是离线消息
  297. /// @param appId 应用的appId
  298. /// @param taskId 推送消息的任务id
  299. /// @param msgId 推送消息的messageid
  300. /// @param completionHandler 用来在后台状态下进行操作(通过苹果apns通道的消息 才有此参数值)
  301. - (void)GeTuiSdkDidReceiveSlience:(NSDictionary *)userInfo fromGetui:(BOOL)fromGetui offLine:(BOOL)offLine appId:(NSString *)appId taskId:(NSString *)taskId msgId:(NSString *)msgId fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  302. // [ GTSDK ]:汇报个推自定义事件(反馈透传消息),开发者可以根据项目需要决定是否使用, 非必须
  303. // [GeTuiSdk sendFeedbackMessage:90001 andTaskId:taskId andMsgId:msgId];
  304. NSString *msg = [NSString stringWithFormat:@"[ TestDemo ] [APN] %@ \nReceive Slience: fromGetui:%@ appId:%@ offLine:%@ taskId:%@ msgId:%@ userInfo:%@ ", NSStringFromSelector(_cmd), fromGetui ? @"个推消息" : @"APNs消息", appId, offLine ? @"离线" : @"在线", taskId, msgId, userInfo];
  305. NSLog(@"-----个推-----收到透传消息----%@",msg);
  306. if(completionHandler) {
  307. // [ 参考代码,开发者注意根据实际需求自行修改 ] 根据APP需要自行修改参数值
  308. completionHandler(UIBackgroundFetchResultNoData);
  309. }
  310. }
  311. - (void)GeTuiSdkNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(UNNotification *)notification API_AVAILABLE(ios(10.0)){
  312. // [ 参考代码,开发者注意根据实际需求自行修改 ] 根据APP需要自行修改参数值
  313. }
  314. - (void)GeTuiSdkDidOccurError:(NSError *)error {
  315. NSString *msg = [NSString stringWithFormat:@"[ TestDemo ] [GeTuiSdk GeTuiSdkDidOccurError]:%@\n\n",error.localizedDescription];
  316. // SDK发生错误时,回调异常错误信息
  317. NSLog(@"%@", msg);
  318. }
  319. /** SDK启动成功返回cid */
  320. - (void)GeTuiSdkDidRegisterClient:(NSString *)clientId {
  321. //个推SDK已注册,返回clientId
  322. NSLog(@"\n>>>[GeTuiSdk RegisterClient]:%@\n\n", clientId);
  323. }
  324. #pragma mark - DouyinOpenSDKLogDelegate
  325. - (void)onLog:(nonnull NSString *)logInfo {
  326. }
  327. - (BOOL)application:(UIApplication *)application openURL:(nonnull NSURL *)url options:(nonnull NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
  328. if ([[DouyinOpenSDKApplicationDelegate sharedInstance] application:application openURL:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] annotation:options[UIApplicationOpenURLOptionsAnnotationKey]]
  329. ) {
  330. return YES;
  331. }
  332. return [WXApi handleOpenURL:url delegate:RQ_WECHAT_MANAGER];
  333. }
  334. /// 在这里写支持的旋转方向,为了防止横屏方向,应用启动时候界面变为横屏模式
  335. //- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
  336. // if (self.allowOrentitaionRotation) {
  337. // return UIInterfaceOrientationMaskAllButUpsideDown;
  338. // }
  339. // return UIInterfaceOrientationMaskPortrait;
  340. //}
  341. - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
  342. ZFInterfaceOrientationMask orientationMask = [ZFLandscapeRotationManager supportedInterfaceOrientationsForWindow:window];
  343. if (orientationMask != ZFInterfaceOrientationMaskUnknow) {
  344. return (UIInterfaceOrientationMask)orientationMask;
  345. }
  346. /// 这里是非播放器VC支持的方向
  347. return UIInterfaceOrientationMaskPortrait;
  348. }
  349. @end