2023-07-10 17:50:31 +08:00
|
|
|
|
#import "AppDelegate.h"
|
2023-11-01 14:18:52 +08:00
|
|
|
|
|
2023-07-10 17:50:31 +08:00
|
|
|
|
#import "GeneratedPluginRegistrant.h"
|
2023-10-14 17:08:25 +08:00
|
|
|
|
#import "CommonDefine.h"
|
2023-11-01 14:18:52 +08:00
|
|
|
|
#import "XSFlutterManager.h"
|
2025-07-01 14:22:57 +08:00
|
|
|
|
#import "Runner-Swift.h"
|
2023-11-01 14:18:52 +08:00
|
|
|
|
|
|
|
|
|
|
#import <UserNotifications/UserNotifications.h>
|
2025-06-30 17:17:07 +08:00
|
|
|
|
#import <PushKit/PushKit.h>
|
2025-07-02 17:11:39 +08:00
|
|
|
|
#import <CallKit/CallKit.h>
|
2025-01-09 19:35:02 +08:00
|
|
|
|
// #import <UMCommon/UMCommon.h>
|
2023-11-01 14:18:52 +08:00
|
|
|
|
|
2024-03-19 18:26:07 +08:00
|
|
|
|
|
2025-07-02 17:11:39 +08:00
|
|
|
|
@interface AppDelegate()<PKPushRegistryDelegate, CXProviderDelegate>
|
2025-02-13 10:56:24 +08:00
|
|
|
|
@property (nonatomic, strong) FlutterMethodChannel *methodChannel;
|
2025-07-02 17:11:39 +08:00
|
|
|
|
@property (nonatomic, strong) CXProvider *callKitProvider;
|
|
|
|
|
|
@property (nonatomic, copy) NSString *pendingCallKitEvent; // 缓存未处理的CallKit事件
|
|
|
|
|
|
@property (nonatomic, strong) NSUUID *lastCallUUID;
|
2025-07-03 11:30:56 +08:00
|
|
|
|
@property (nonatomic, copy) NSString *voipTokenString; // + 新增:缓存VoIP Token
|
2023-11-01 14:18:52 +08:00
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@implementation AppDelegate {
|
|
|
|
|
|
}
|
2023-07-10 17:50:31 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- (BOOL)application:(UIApplication *)application
|
|
|
|
|
|
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
2023-11-01 14:18:52 +08:00
|
|
|
|
|
2025-01-09 19:35:02 +08:00
|
|
|
|
// [UMConfigure initWithAppkey:@"671244ae80464b33f6df9646" channel:@"Product"];
|
2025-02-13 10:56:24 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//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<UNNotificationCategory *> *categories for iOS10 or later
|
|
|
|
|
|
// NSSet<UIUserNotificationCategory *> *categories for iOS8 and iOS9
|
|
|
|
|
|
}
|
|
|
|
|
|
[JPUSHService registerForRemoteNotificationConfig:entity delegate:self];
|
|
|
|
|
|
|
2025-06-30 17:17:07 +08:00
|
|
|
|
// 注册VoIP推送
|
|
|
|
|
|
PKPushRegistry *voipRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
|
|
|
|
|
|
voipRegistry.delegate = self;
|
|
|
|
|
|
voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
|
2024-10-19 18:26:41 +08:00
|
|
|
|
|
2023-11-01 14:18:52 +08:00
|
|
|
|
XSFlutterManager *VC = [[XSFlutterManager alloc] init];
|
2023-10-16 17:18:10 +08:00
|
|
|
|
self.window.rootViewController = VC;
|
|
|
|
|
|
[self.window makeKeyAndVisible];
|
2025-02-13 10:56:24 +08:00
|
|
|
|
|
2025-07-02 17:11:39 +08:00
|
|
|
|
// 初始化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");
|
|
|
|
|
|
}
|
2025-07-03 11:30:56 +08:00
|
|
|
|
} else if ([call.method isEqualToString:@"get_voip_token"]) { // + 新增
|
|
|
|
|
|
if (weakSelf.voipTokenString) {
|
|
|
|
|
|
result(weakSelf.voipTokenString);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
result(nil);
|
|
|
|
|
|
}
|
2025-07-02 17:11:39 +08:00
|
|
|
|
} 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
|
|
|
|
|
|
|
2023-10-16 17:18:10 +08:00
|
|
|
|
return YES;
|
2023-07-10 17:50:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-17 14:29:13 +08:00
|
|
|
|
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
|
|
|
|
|
// 判断是否是你的应用程序的 URL Scheme
|
|
|
|
|
|
if ([url.scheme isEqualToString:@"skysmartlock"]) {
|
|
|
|
|
|
// 处理被唤起的逻辑,可以根据 URL 中的其他信息来执行相应的操作
|
|
|
|
|
|
return YES;
|
|
|
|
|
|
}
|
|
|
|
|
|
return NO;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-11-01 14:18:52 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 注册苹果推送,获取deviceToken用于推送
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param application
|
|
|
|
|
|
*/
|
|
|
|
|
|
- (void)registerAPNS:(UIApplication *)application {
|
|
|
|
|
|
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
|
|
|
|
|
|
// iOS 8 Notifications
|
|
|
|
|
|
[application registerUserNotificationSettings:
|
|
|
|
|
|
[UIUserNotificationSettings settingsForTypes:
|
2024-06-06 15:56:23 +08:00
|
|
|
|
(UIUserNotificationTypeSound | UIUserNotificationTypeAlert)
|
2023-11-01 14:18:52 +08:00
|
|
|
|
categories:nil]];
|
|
|
|
|
|
[application registerForRemoteNotifications];
|
|
|
|
|
|
}
|
|
|
|
|
|
else {
|
|
|
|
|
|
// iOS < 8 Notifications
|
|
|
|
|
|
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
|
2024-06-06 15:56:23 +08:00
|
|
|
|
(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)];
|
2023-11-01 14:18:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
|
|
* 苹果推送注册成功回调,将苹果返回的deviceToken上传到CloudPush服务器
|
|
|
|
|
|
*/
|
2025-02-22 17:12:22 +08:00
|
|
|
|
//- (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]];
|
|
|
|
|
|
}
|
2024-03-19 18:26:07 +08:00
|
|
|
|
|
2025-02-22 17:12:22 +08:00
|
|
|
|
return [hexString copy];
|
2023-11-01 14:18:52 +08:00
|
|
|
|
}
|
2025-02-22 17:12:22 +08:00
|
|
|
|
|
2023-11-01 14:18:52 +08:00
|
|
|
|
/*
|
|
|
|
|
|
* 苹果推送注册失败回调
|
|
|
|
|
|
*/
|
|
|
|
|
|
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
|
2025-02-13 10:56:24 +08:00
|
|
|
|
//Optional
|
|
|
|
|
|
NSLog(@"did Fail To Register For Remote Notifications With Error: %@", error);
|
2023-11-01 14:18:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 注册推送消息到来监听
|
|
|
|
|
|
*/
|
|
|
|
|
|
- (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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-13 10:56:24 +08:00
|
|
|
|
# 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];
|
|
|
|
|
|
}
|
2023-11-01 14:18:52 +08:00
|
|
|
|
|
2025-06-30 17:17:07 +08:00
|
|
|
|
#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);
|
2025-07-03 11:30:56 +08:00
|
|
|
|
self.voipTokenString = tokenString; // + 缓存token
|
|
|
|
|
|
if (self.methodChannel) {
|
|
|
|
|
|
[self.methodChannel invokeMethod:@"voip_token" arguments:tokenString];
|
|
|
|
|
|
}
|
|
|
|
|
|
// 若Flutter未ready,Flutter端可通过get_voip_token拉取
|
2025-06-30 17:17:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 收到VoIP推送
|
|
|
|
|
|
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
|
|
|
|
|
|
if ([type isEqualToString:PKPushTypeVoIP]) {
|
|
|
|
|
|
NSLog(@"[VoIP] didReceiveIncomingPushWithPayload: %@", payload.dictionaryPayload);
|
2025-07-02 17:11:39 +08:00
|
|
|
|
#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弹窗
|
2025-07-01 14:22:57 +08:00
|
|
|
|
UIViewController *rootVC = [UIApplication sharedApplication].delegate.window.rootViewController;
|
|
|
|
|
|
NSString *callerName = @"来电"; // 可根据payload内容自定义
|
|
|
|
|
|
[LCKBridge presentCallInterfaceFromRootVC:rootVC callerName:callerName];
|
2025-07-02 17:11:39 +08:00
|
|
|
|
#endif
|
2025-06-30 17:17:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (completion) {
|
|
|
|
|
|
completion();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-11-01 14:18:52 +08:00
|
|
|
|
|
2025-07-02 17:11:39 +08:00
|
|
|
|
#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];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-07-10 17:50:31 +08:00
|
|
|
|
@end
|