#import "AppDelegate.h" #import #import #import #import #import //#import #if RCT_NEW_ARCH_ENABLED #import #import #import #import #import #import #import #import static NSString *const kRNConcurrentRoot = @"concurrentRoot"; @interface AppDelegate () { RCTTurboModuleManager *_turboModuleManager; RCTSurfacePresenterBridgeAdapter *_bridgeAdapter; std::shared_ptr _reactNativeConfig; facebook::react::ContextContainer::Shared _contextContainer; } @end #endif @interface AppDelegate() @property (nonatomic, strong) AVAudioPlayer *audioPlayer; @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenNotificationSettingsURLString]]; // NSString *url = UIApplicationOpenNotificationSettingsURLString; // APNS // BOOL isLocation = [CLLocationManager locationServicesEnabled]; UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; self.rnLoaded = NO; NSDate *date = [NSDate date]; //zone为当前时区信息 在我的程序中打印的是@"Asia/Shanghai" NSTimeZone *zone = [NSTimeZone systemTimeZone]; //所在地区时间与协调世界时差距 NSInteger interval = [zone secondsFromGMTForDate: date]; //加上时差,得到本地时间 NSDate *localeDate = [date dateByAddingTimeInterval: interval]; // JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init]; // if (@available(iOS 12.0, *)) { // entity.types = JPAuthorizationOptionNone; //JPAuthorizationOptionAlert|JPAuthorizationOptionBadge|JPAuthorizationOptionSou//nd|JPAuthorizationOptionProvidesAppNotificationSettings; // } // [JPUSHService registerForRemoteNotificationConfig:entity delegate:self]; RCTAppSetupPrepareApp(application); RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions]; // UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; center.delegate = self; #if RCT_NEW_ARCH_ENABLED _contextContainer = std::make_shared(); _reactNativeConfig = std::make_shared(); _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer]; bridge.surfacePresenter = _bridgeAdapter.surfacePresenter; #endif NSDictionary *initProps = [self prepareInitialProps]; UIView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"taroDemo" initialProperties:initProps]; if (@available(iOS 13.0, *)) { rootView.backgroundColor = [UIColor systemBackgroundColor]; } else { rootView.backgroundColor = [UIColor blackColor]; } self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window.backgroundColor = [UIColor blackColor]; UIViewController *rootViewController = [self.reactDelegate createRootViewController]; rootViewController.view = rootView; rootViewController.view.backgroundColor = [UIColor blackColor]; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; [super application:application didFinishLaunchingWithOptions:launchOptions]; [self clearAllDeliveredNotifications]; // [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(demoPush) userInfo:nil repeats:NO]; // [self mixAudio]; // [self demoPush]; // for (int i = 0;i<5;i++){ // [self demoPush:i]; // } return YES; } -(void)demoPush:(int)index{ UNNotificationAction *action = [UNNotificationAction actionWithIdentifier:[NSString stringWithFormat:@"%@%d",@"actionIdentifier",index] title:@"Action Title" options:UNNotificationActionOptionAuthenticationRequired]; // 接着定义一个分类,并将动作添加到分类中 UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:[NSString stringWithFormat:@"%@%d",@"categoryIdentifier",index] actions:@[action] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction]; // 注册分类 [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObjects:category, nil]]; // 创建本地推送内容 UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; content.title = [NSString stringWithFormat:@"%@%d",@"Notification Title",index]; content.body = @"This is the notification body"; content.categoryIdentifier = [NSString stringWithFormat:@"%@%d",@"categoryIdentifier",index]; // 使用你刚刚定义的分类标识符 content.badge = @([UIApplication sharedApplication].applicationIconBadgeNumber + 1); // 创建推送请求 UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:15+index*5 repeats:NO]; UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:[NSString stringWithFormat:@"%@%d",@"notificationIdentifier",index] content:content trigger:trigger]; // 将推送请求添加到通知中心 [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil]; } - (void)mixAudio{ NSError *error = nil; AVMutableComposition *composition = [AVMutableComposition composition]; // 创建音轨 AVMutableCompositionTrack *compositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; // 声音文件路径 NSString *soundFilePath1 = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"m4a"]; NSString *soundFilePath2 = [[NSBundle mainBundle] pathForResource:@"2" ofType:@"m4a"]; // 加载音频文件 AVAsset *asset1 = [AVAsset assetWithURL:[NSURL fileURLWithPath:soundFilePath1]]; AVAsset *asset2 = [AVAsset assetWithURL:[NSURL fileURLWithPath:soundFilePath2]]; // 获取音频文件的时长 CMTimeRange timeRange1 = CMTimeRangeMake(kCMTimeZero, asset1.duration); CMTimeRange timeRange2 = CMTimeRangeMake(kCMTimeZero, asset2.duration); // 将音频文件的音轨添加到合成的音轨中 [compositionTrack insertTimeRange:timeRange1 ofTrack:[[asset1 tracksWithMediaType:AVMediaTypeAudio] firstObject] atTime:kCMTimeZero error:&error]; if (error) { // 错误处理 NSLog(@"Error composing sound: %@", [error localizedDescription]); } // 在第一段声音之后继续添加第二段声音 [compositionTrack insertTimeRange:timeRange2 ofTrack:[[asset2 tracksWithMediaType:AVMediaTypeAudio] firstObject] atTime:asset1.duration error:&error]; if (error) { // 错误处理 NSLog(@"Error composing sound: %@", [error localizedDescription]); } // 输出文件路径 NSString *outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"outputSound.m4a"]; NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL fileExists = [fileManager fileExistsAtPath:outputFilePath]; if (fileExists) { // 文件存在,删除文件 NSError *error; BOOL success = [fileManager removeItemAtPath:outputFilePath error:&error]; if (success) { NSLog(@"文件删除成功"); } else { // 处理错误 NSLog(@"文件删除失败: %@", [error localizedDescription]); } } else { NSLog(@"文件不存在"); } NSURL *outputURL = [NSURL fileURLWithPath:outputFilePath]; // 导出合成的音频 AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetAppleM4A]; exportSession.outputURL = outputURL; exportSession.outputFileType = AVFileTypeAppleM4A; [exportSession exportAsynchronouslyWithCompletionHandler:^{ if (exportSession.status == AVAssetExportSessionStatusCompleted) { // 导出成功,可以播放或者使用输出的文件 NSLog(@"Export success: %@", outputFilePath); NSURL *fileURL = [NSURL fileURLWithPath:outputFilePath]; NSError *error; self->_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error]; if (error) { NSLog(@"Error creating audio player: %@", [error localizedDescription]); } else { [self->_audioPlayer play]; } } else { // 导出失败,处理错误 NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]); } }]; } - (void)applicationDidBecomeActive:(UIApplication *)application { [UIApplication sharedApplication].applicationIconBadgeNumber = 0; } - (void)registerForPushNotifications { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) { if (granted) { dispatch_async(dispatch_get_main_queue(), ^{ [[UIApplication sharedApplication] registerForRemoteNotifications]; }); } else { // 用户拒绝通知或发生错误 } }]; } /// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. /// /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). /// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`. - (BOOL)concurrentRootEnabled { // Switch this bool to turn on and off the concurrent root return true; } - (NSDictionary *)prepareInitialProps { NSMutableDictionary *initProps = [NSMutableDictionary new]; #ifdef RCT_NEW_ARCH_ENABLED initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]); #endif return initProps; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { // return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #if DEBUGaa return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; #else return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } #if RCT_NEW_ARCH_ENABLED #pragma mark - RCTCxxBridgeDelegate - (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge { _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge delegate:self jsInvoker:bridge.jsCallInvoker]; return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager); } #pragma mark RCTTurboModuleManagerDelegate - (Class)getModuleClassFromName:(const char *)name { return RCTCoreModulesClassProvider(name); } - (std::shared_ptr)getTurboModule:(const std::string &)name jsInvoker:(std::shared_ptr)jsInvoker { return nullptr; } - (std::shared_ptr)getTurboModule:(const std::string &)name initParams: (const facebook::react::ObjCTurboModule::InitParams &)params { return nullptr; } - (id)getModuleInstanceFromClass:(Class)moduleClass { return RCTAppSetupDefaultModuleFromClass(moduleClass); } #endif //************************************************JPush start************************************************ //注册 APNS 成功并上报 DeviceToken - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { // [JPUSHService registerDeviceToken:deviceToken]; } - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{ NSLog(@"get notification error"); } //iOS 7 APNS - (void)application:(UIApplication *)application didReceiveRemoteNotification: (NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { // iOS 10 以下 Required NSLog(@"iOS 7 APNS"); // [JPUSHService handleRemoteNotification:userInfo]; // [[NSNotificationCenter defaultCenter] postNotificationName:J_APNS_NOTIFICATION_ARRIVED_EVENT object:userInfo]; completionHandler(UIBackgroundFetchResultNewData); } //iOS 10 前台收到消息 - (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler { NSDictionary * userInfo = notification.request.content.userInfo; if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) { // Apns NSLog(@"iOS 10 APNS 前台收到消息"); // [JPUSHService handleRemoteNotification:userInfo]; // [[NSNotificationCenter defaultCenter] postNotificationName:J_APNS_NOTIFICATION_ARRIVED_EVENT object:userInfo]; } else { // 本地通知 todo NSLog(@"iOS 10 本地通知 前台收到消息"); // [[NSNotificationCenter defaultCenter] postNotificationName:J_LOCAL_NOTIFICATION_ARRIVED_EVENT object:userInfo]; } //需要执行这个方法,选择是否提醒用户,有 Badge、Sound、Alert 三种类型可以选择设置 completionHandler(UNNotificationPresentationOptionAlert); } - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { } //再实现UNUserNotificationCenterDelegate代理的方法 //- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler //{ // //应用在前台时候接收到本地推送通知、远程推送通知调用此方法 //} - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler { //应用程序在后台,用户通过点击本地推送、远程推送进入app时调用此方法 self.timestamp = [[NSDate date] timeIntervalSince1970]*1000; self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(postNotificationData:) userInfo:response repeats:YES]; } //iOS 10 消息事件回调 - (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler: (void (^)(void))completionHandler { NSDictionary * userInfo = response.notification.request.content.userInfo; if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) { // Apns NSLog(@"iOS 10 APNS 消息事件回调"); // [JPUSHService handleRemoteNotification:userInfo]; // // 保障应用被杀死状态下,用户点击推送消息,打开app后可以收到点击通知事件 // [[RCTJPushEventQueue sharedInstance]._notificationQueue insertObject:userInfo atIndex:0]; // [[NSNotificationCenter defaultCenter] postNotificationName:J_APNS_NOTIFICATION_OPENED_EVENT object:userInfo]; } else { // 本地通知 NSLog(@"iOS 10 本地通知 消息事件回调"); // 保障应用被杀死状态下,用户点击推送消息,打开app后可以收到点击通知事件 // [[RCTJPushEventQueue sharedInstance]._localNotificationQueue insertObject:userInfo atIndex:0]; // [[NSNotificationCenter defaultCenter] postNotificationName:J_LOCAL_NOTIFICATION_OPENED_EVENT object:userInfo]; } self.timestamp = [[NSDate date] timeIntervalSince1970]*1000; self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(postNotificationData:) userInfo:response repeats:YES]; // NSString *categoryIdentifier = response.notification.request.content.categoryIdentifier; // [self.nativeBridge.bridge.eventDispatcher sendAppEventWithName:@"notificationReceive" body:@{@"category_id":categoryIdentifier,@"action_id":response.actionIdentifier}]; // 处理用户交互 // if ([response.actionIdentifier isEqualToString:@"ALLOW_ACTION"]) { // // 用户点击了"允许"按钮,发起网络请求 // NSLog(@"User allow the request"); // //// [self.nativeBridge.bridge.eventDispatcher sendAppEventWithName:@"notificationReceive" body:@{@"name":@"1",@"value":@"2"}]; // //// [self makeNetworkRequest]; // } else if ([response.actionIdentifier isEqualToString:@"DENY_ACTION"]) { // // 用户点击了"拒绝"按钮 // NSLog(@"User denied the request"); //// [self makeNetworkRequest]; // } else if ([response.actionIdentifier isEqualToString:@"START_TIMER_NOW"]){ // if ([categoryIdentifier isEqualToString:@"REMINDER_FS_START_FAST"]){ // // } // else if ([categoryIdentifier isEqualToString:@"REMINDER_FS_START_SLEEP"]){ // // } // } else if ([response.actionIdentifier isEqualToString:@"PICK_EARLIER_START"]){ // if ([categoryIdentifier isEqualToString:@"REMINDER_FS_START_FAST"]){ // // } // else if ([categoryIdentifier isEqualToString:@"REMINDER_FS_START_SLEEP"]){ // // } // } // else if ([response.actionIdentifier isEqualToString:@"END_TIMER_NOW"]){ // // } // else if ([response.actionIdentifier isEqualToString:@"PICK_EARLIER_END"]){ // // } // else if ([response.actionIdentifier isEqualToString:@"SKIP"]){ // // } // 系统要求执行这个方法 completionHandler(); } - (void)postNotificationData:(NSTimer *)timerInfo{ if (self.rnLoaded){ UNNotificationResponse *response = timerInfo.userInfo; [self.timer invalidate]; self.timer = nil; NSString *categoryIdentifier = response.notification.request.content.categoryIdentifier; NSString *identifier = response.notification.request.identifier; if ([identifier hasPrefix:@"REMINDER_FS_"]){ NSString *strId = [identifier substringFromIndex:12]; [self.nativeBridge.bridge.eventDispatcher sendAppEventWithName:@"notificationReceive" body:@{@"category_id":categoryIdentifier,@"action_id":response.actionIdentifier,@"id":strId,@"timestamp":@(self.timestamp)}]; [self clearAllDeliveredNotifications]; } } else { } } - (void)jumpNotificationSettingPage{ if (self.rnLoaded){ [self.timer invalidate]; self.timer = nil; [self.nativeBridge.bridge.eventDispatcher sendAppEventWithName:@"openNotificationSetting" body:@{}]; } else { } } - (void)makeNetworkRequest{ //https://api.fast.dev.liveplus.fun/api/static-resource-urls NSURL *url = [NSURL URLWithString:@"https://api.fast.dev.liveplus.fun/api/static-resource-urls"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLSessionDataTask *dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error) { NSLog(@"Error: %@", error.localizedDescription); } else { // 处理返回的数据 NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; NSLog(@"Response: %@", json); } }]; [dataTask resume]; } //自定义消息 - (void)networkDidReceiveMessage:(NSNotification *)notification { NSDictionary * userInfo = [notification userInfo]; // [[NSNotificationCenter defaultCenter] postNotificationName:J_CUSTOM_NOTIFICATION_EVENT object:userInfo]; } - (void)demo{ // 1. 设置 notification 内容 UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; content.title = @"Daily Reminder"; content.body = @"It's 9:00 AM, time to start your day!"; content.sound = [UNNotificationSound defaultSound]; // 2. 设置触发条件 - 每天 9:00 AM NSDateComponents *dateComponents = [[NSDateComponents alloc] init]; dateComponents.hour = 9; dateComponents.minute = 0; UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:dateComponents repeats:YES]; // 3. 创建 notification request UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"DailyReminder" content:content trigger:trigger]; // 4. 添加 notification request 到通知中心 UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { if (error != nil) { NSLog(@"Error adding notification request: %@", error); } }]; } - (void)scheduleCalendarNotificationWithTitle:(NSString *)title body:(NSString *)body date:(NSDate *)date repeats:(BOOL)repeats identifier:(NSString *)identifier { NSLog(@"%s", __FUNCTION__); UNMutableNotificationContent *content = [UNMutableNotificationContent new]; content.title = title; content.body = body; NSCalendar *calendar = NSCalendar.currentCalendar; NSDateComponents *components = [calendar components:(NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond) fromDate:date]; UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:repeats]; UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger]; [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { if (error) { NSLog(@"%@", error); } }]; } - (void)clearAllDeliveredNotifications { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center removeAllDeliveredNotifications]; } - (void)userNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(nullable UNNotification *)notification{ self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(jumpNotificationSettingPage) userInfo:nil repeats:YES]; } @end