fix:调整为callkit实现

This commit is contained in:
liyi 2025-07-02 17:11:39 +08:00
parent 2ab2a48f02
commit 4e08f57c48
7 changed files with 179 additions and 4 deletions

View File

@ -7,11 +7,15 @@
#import <UserNotifications/UserNotifications.h>
#import <PushKit/PushKit.h>
#import <CallKit/CallKit.h>
// #import <UMCommon/UMCommon.h>
@interface AppDelegate()<PKPushRegistryDelegate>
@interface AppDelegate()<PKPushRegistryDelegate, CXProviderDelegate>
@property (nonatomic, strong) FlutterMethodChannel *methodChannel;
@property (nonatomic, strong) CXProvider *callKitProvider;
@property (nonatomic, copy) NSString *pendingCallKitEvent; // CallKit
@property (nonatomic, strong) NSUUID *lastCallUUID;
@end
@ -45,6 +49,45 @@
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 {
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;
}
@ -190,13 +233,52 @@
- (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

View File

@ -109,6 +109,8 @@
<array>
<string>remote-notification</string>
<string>voip</string>
<string>processing</string>
<string>fetch</string>
</array>
<key>UIFileSharingEnabled</key>
<true/>

View File

@ -1,9 +1,37 @@
import Foundation
import UIKit
#if USE_CALLKIT
import CallKit
#endif
import LiveCommunicationKit
@objc class LCKBridge: NSObject {
@objc static func presentCallInterfaceFromRootVC(_ rootVC: UIViewController, callerName: String) {
@objc(LCKBridge)
class LCKBridge: NSObject {
// CallKit
@objc(presentCallInterfaceWithCallKit:)
class func presentCallInterfaceWithCallKit(_ callerName: NSString) {
#if USE_CALLKIT
let providerConfiguration = CXProviderConfiguration(localizedName: callerName as String)
providerConfiguration.supportsVideo = false
providerConfiguration.maximumCallsPerCallGroup = 1
let provider = CXProvider(configuration: providerConfiguration)
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .generic, value: callerName as String)
update.hasVideo = false
provider.setDelegate(nil, queue: nil)
provider.reportNewIncomingCall(with: UUID(), update: update) { error in
if let error = error {
print("CallKit来电弹窗失败: \(error.localizedDescription)")
} else {
print("CallKit来电弹窗成功")
}
}
#endif
}
// LiveCommunicationKit
@objc(presentCallInterfaceFromRootVC:callerName:)
class func presentCallInterfaceFromRootVC(_ rootVC: UIViewController, callerName: NSString) {
#if !USE_CALLKIT
if #available(iOS 17.4, *) {
// ConversationManager
let config = ConversationManager.Configuration(
@ -16,7 +44,7 @@ import LiveCommunicationKit
supportedHandleTypes: [.generic, .phoneNumber, .emailAddress]
)
let manager = ConversationManager(configuration: config)
let local = Handle(type: .generic, value: callerName, displayName: callerName)
let local = Handle(type: .generic, value: callerName as String, displayName: callerName as String)
let update = Conversation.Update(localMember: local, members: [local], activeRemoteMembers: [local])
Task {
do {
@ -27,5 +55,6 @@ import LiveCommunicationKit
}
}
}
#endif
}
}

View File

@ -109,6 +109,8 @@
<array>
<string>remote-notification</string>
<string>voip</string>
<string>processing</string>
<string>fetch</string>
</array>
<key>UIFileSharingEnabled</key>
<true/>

View File

@ -109,6 +109,8 @@
<array>
<string>remote-notification</string>
<string>voip</string>
<string>processing</string>
<string>fetch</string>
</array>
<key>UIFileSharingEnabled</key>
<true/>

View File

@ -19,6 +19,7 @@ import 'package:star_lock/talk/starChart/handle/impl/debug_Info_model.dart';
import 'package:star_lock/talk/starChart/status/appLifecycle_observer.dart';
import 'package:star_lock/tools/baseGetXController.dart';
import 'package:star_lock/tools/bugly/bugly_tool.dart';
import 'package:star_lock/tools/callkit_handler.dart';
import 'package:star_lock/tools/device_info_service.dart';
import 'package:star_lock/tools/eventBusEventManage.dart';
import 'package:star_lock/tools/jverify_one_click_login.dart';
@ -65,6 +66,7 @@ FutureOr<void> main() async {
// runApp(MultiProvider(providers: [
// ChangeNotifierProvider(create: (_) => DebugInfoModel()),
// ], child: MyApp(isLogin: isLogin)));
CallKitHandler.setupListener();
runApp(MyApp(isLogin: isLogin));
}, onException: (FlutterErrorDetails details) async {
debugPrint('FlutterErrorDetails ${details.exceptionAsString()}');

View File

@ -0,0 +1,56 @@
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:star_lock/appRouters.dart';
import 'package:star_lock/app_settings/app_settings.dart';
class CallKitHandler {
static const MethodChannel _channel = MethodChannel('com.starlock/callkit');
static bool _isInitialized = false;
/// context
static void setupListener() async {
if (_isInitialized) {
print('CallKitHandler.setupListener 已初始化,跳过');
return;
}
_isInitialized = true;
print('CallKitHandler.setupListener 初始化准备拉取pending事件');
// pending事件
final pendingEvent =
await _channel.invokeMethod<String>('get_pending_event');
print('CallKitHandler 拉取到pendingEvent: $pendingEvent');
if (pendingEvent == 'callkit_answered') {
print('启动时拉取到callkit_answered跳转到对讲页面');
//
} else if (pendingEvent == 'callkit_declined') {
print('启动时拉取到callkit_declined返回主页面');
} else {
print('启动时无pending事件');
}
//
_channel.setMethodCallHandler((call) async {
print('CallKitHandler 收到原生事件: ${call.method}');
if (call.method == 'callkit_answered') {
print('跳转到对讲页面');
String currentRoute = Get.currentRoute;
print('当前路由: $currentRoute');
if (currentRoute != Routers.h264View) {
Get.toNamed(
Routers.h264View,
);
}
} else if (call.method == 'callkit_declined') {
print('返回主页面');
} else {
print('收到未知事件: ${call.method}');
}
});
print('CallKitHandler.setupListener 监听已注册');
}
/// CallKit通话
static Future<void> endCall() async {
print('CallKitHandler.endCall 通知原生端结束通话');
await _channel.invokeMethod('end_call');
}
}