#import "AppDelegate.h" #import "GeneratedPluginRegistrant.h" #import "CommonDefine.h" #import "XSFlutterManager.h" #import "Runner-Swift.h" #import #import #import // #import @interface AppDelegate() @property (nonatomic, strong) FlutterMethodChannel *methodChannel; @property (nonatomic, strong) CXProvider *callKitProvider; @property (nonatomic, copy) NSString *pendingCallKitEvent; // 缓存未处理的CallKit事件 @property (nonatomic, strong) NSUUID *lastCallUUID; @property (nonatomic, copy) NSString *voipTokenString; // + 新增:缓存VoIP Token @end @implementation AppDelegate { } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // [UMConfigure initWithAppkey:@"671244ae80464b33f6df9646" channel:@"Product"]; //Required //notice: 3.0.0 及以后版本注册可以这样写,也可以继续用之前的注册方式 JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init]; entity.types = JPAuthorizationOptionAlert|JPAuthorizationOptionBadge|JPAuthorizationOptionSound|JPAuthorizationOptionProvidesAppNotificationSettings; if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) { // 可以添加自定义 categories // NSSet *categories for iOS10 or later // NSSet *categories for iOS8 and iOS9 } [JPUSHService registerForRemoteNotificationConfig:entity delegate:self]; // 注册VoIP推送 PKPushRegistry *voipRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()]; voipRegistry.delegate = self; voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP]; XSFlutterManager *VC = [[XSFlutterManager alloc] init]; self.window.rootViewController = VC; [self.window makeKeyAndVisible]; // 初始化FlutterMethodChannel FlutterViewController *controller = (FlutterViewController *)self.window.rootViewController; self.methodChannel = [FlutterMethodChannel methodChannelWithName:@"com.starlock/callkit" binaryMessenger:controller.binaryMessenger]; // 注册拉取pending事件方法 __weak typeof(self) weakSelf = self; [self.methodChannel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) { if ([call.method isEqualToString:@"get_pending_event"]) { if (weakSelf.pendingCallKitEvent) { result(weakSelf.pendingCallKitEvent); weakSelf.pendingCallKitEvent = nil; } else { result(nil); } } else if ([call.method isEqualToString:@"end_call"]) { NSLog(@"[CallKit] 收到Flutter端结束通话请求"); // 结束CallKit通话 // 这里只能结束最近一次来电,需保存UUID if (weakSelf.lastCallUUID) { CXCallEndedReason reason = CXCallEndedReasonRemoteEnded; [weakSelf.callKitProvider reportCallWithUUID:weakSelf.lastCallUUID endedAtDate:[NSDate date] reason:reason]; weakSelf.lastCallUUID = nil; result(@"ok"); } else { NSLog(@"[CallKit] 无有效UUID,无法结束通话"); result(@"no_call"); } } else if ([call.method isEqualToString:@"get_voip_token"]) { // + 新增 if (weakSelf.voipTokenString) { result(weakSelf.voipTokenString); } else { result(nil); } } else { result(FlutterMethodNotImplemented); } }]; // 初始化CallKit Provider(仅国外包) #if USE_CALLKIT CXProviderConfiguration *providerConfiguration = [[CXProviderConfiguration alloc] initWithLocalizedName:@"来电"]; providerConfiguration.supportsVideo = NO; providerConfiguration.maximumCallsPerCallGroup = 1; self.callKitProvider = [[CXProvider alloc] initWithConfiguration:providerConfiguration]; [self.callKitProvider setDelegate:self queue:nil]; #endif return YES; } - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { // 判断是否是你的应用程序的 URL Scheme if ([url.scheme isEqualToString:@"skysmartlock"]) { // 处理被唤起的逻辑,可以根据 URL 中的其他信息来执行相应的操作 return YES; } return NO; } /** * 注册苹果推送,获取deviceToken用于推送 * * @param application */ - (void)registerAPNS:(UIApplication *)application { if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) { // iOS 8 Notifications [application registerUserNotificationSettings: [UIUserNotificationSettings settingsForTypes: (UIUserNotificationTypeSound | UIUserNotificationTypeAlert) categories:nil]]; [application registerForRemoteNotifications]; } else { // iOS < 8 Notifications [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)]; } } /* * 苹果推送注册成功回调,将苹果返回的deviceToken上传到CloudPush服务器 */ //- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { // NSString *tokenString = [self hexStringFromData:deviceToken]; // NSLog(@"starlock didRegisterForRemoteNotificationsWithDeviceToken token: %@", tokenString); // /// Required - 注册 DeviceToken // [JPUSHService registerDeviceToken:deviceToken]; // //} - (NSString *)hexStringFromData:(NSData *)data { const unsigned char *dataBuffer = (const unsigned char *)[data bytes]; NSMutableString *hexString = [NSMutableString stringWithCapacity:data.length * 2]; for (NSInteger i = 0; i < data.length; i++) { [hexString appendFormat:@"%02x", dataBuffer[i]]; } return [hexString copy]; } /* * 苹果推送注册失败回调 */ - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { //Optional NSLog(@"did Fail To Register For Remote Notifications With Error: %@", error); } /** * 注册推送消息到来监听 */ - (void)registerMessageReceive { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMessageReceived:) name:@"CCPDidReceiveMessageNotification" object:nil]; } /** * 处理到来推送消息 * * @param notification */ - (void)onMessageReceived:(NSNotification *)notification { CCPSysMessage *message = [notification object]; NSString *title = [[NSString alloc] initWithData:message.title encoding:NSUTF8StringEncoding]; NSString *body = [[NSString alloc] initWithData:message.body encoding:NSUTF8StringEncoding]; NSLog(@"Receive message title: %@, content: %@.", title, body); } # pragma mark- JPUSHRegisterDelegate // iOS 12 Support - (void)jpushNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(UNNotification *)notification{ if (notification && [notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) { //从通知界面直接进入应用 }else{ //从通知设置界面进入应用 } } // iOS 10 Support - (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler { // Required NSDictionary * userInfo = notification.request.content.userInfo; if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) { [JPUSHService handleRemoteNotification:userInfo]; } completionHandler(UNNotificationPresentationOptionAlert); // 需要执行这个方法,选择是否提醒用户,有 Badge、Sound、Alert 三种类型可以选择设置 } // iOS 10 Support - (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler { // Required NSDictionary * userInfo = response.notification.request.content.userInfo; if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) { [JPUSHService handleRemoteNotification:userInfo]; } completionHandler(); // 系统要求执行这个方法 } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { // Required, iOS 7 Support [JPUSHService handleRemoteNotification:userInfo]; completionHandler(UIBackgroundFetchResultNewData); } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { // Required, For systems with less than or equal to iOS 6 [JPUSHService handleRemoteNotification:userInfo]; } #pragma mark - PKPushRegistryDelegate // 获取VoIP Token - (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type { if ([type isEqualToString:PKPushTypeVoIP]) { const unsigned char *tokenBytes = credentials.token.bytes; NSMutableString *tokenString = [NSMutableString string]; for (NSInteger i = 0; i < credentials.token.length; i++) { [tokenString appendFormat:@"%02x", tokenBytes[i]]; } NSLog(@"[VoIP] didUpdatePushCredentials, token: %@", tokenString); self.voipTokenString = tokenString; // + 缓存token if (self.methodChannel) { [self.methodChannel invokeMethod:@"voip_token" arguments:tokenString]; } // 若Flutter未ready,Flutter端可通过get_voip_token拉取 } } // 收到VoIP推送 - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion { if ([type isEqualToString:PKPushTypeVoIP]) { NSLog(@"[VoIP] didReceiveIncomingPushWithPayload: %@", payload.dictionaryPayload); #if USE_CALLKIT // 国外环境,直接用CallKit弹窗 NSString *callerName = @"来电"; // 可根据payload内容自定义 // 复用已初始化的provider CXCallUpdate *update = [[CXCallUpdate alloc] init]; update.remoteHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:callerName]; update.hasVideo = NO; NSUUID *callUUID = [NSUUID UUID]; self.lastCallUUID = callUUID; [self.callKitProvider reportNewIncomingCallWithUUID:callUUID update:update completion:^(NSError * _Nullable error) { if (error) { NSLog(@"CallKit error: %@", error); } }]; #else // 国内环境,使用LiveCommunicationKit弹窗 UIViewController *rootVC = [UIApplication sharedApplication].delegate.window.rootViewController; NSString *callerName = @"来电"; // 可根据payload内容自定义 [LCKBridge presentCallInterfaceFromRootVC:rootVC callerName:callerName]; #endif } if (completion) { completion(); } } #pragma mark - CXProviderDelegate // 用户点击"接听" - (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action { NSLog(@"[CallKit] 用户接听来电"); if (self.methodChannel) { [self.methodChannel invokeMethod:@"callkit_answered" arguments:nil]; } else { self.pendingCallKitEvent = @"callkit_answered"; } [action fulfill]; } // 用户点击"拒绝"或挂断 - (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action { NSLog(@"[CallKit] 用户拒绝/挂断来电"); if (self.methodChannel) { [self.methodChannel invokeMethod:@"callkit_declined" arguments:nil]; } else { self.pendingCallKitEvent = @"callkit_declined"; } [action fulfill]; } @end