Flutter通话保持插件mitek_callkeep的使用

Flutter通话保持插件mitek_callkeep的使用

展示通过iOS CallKit和Android自定义UI在Flutter中显示来电通知/屏幕。

原生设置

mitek_callkeep 需要以下权限。

Android

无需额外设置。

iOS

Info.plist 文件中添加:

<key>UIBackgroundModes</key>
<array>
    <string>processing</string>
    <string>remote-notification</string>
    <string>voip</string>
</array>

然后需要更新 AppDelegate.swift 来处理PushKit,因为由于iOS 13 PushKit VoIP限制,推送处理必须通过原生iOS代码进行。

import UIKit
import PushKit
import Flutter
import mitek_callkeep

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, PKPushRegistryDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
        
        // 设置VOIP
        let mainQueue = DispatchQueue.main
        let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)
        voipRegistry.delegate = self
        voipRegistry.desiredPushTypes = [PKPushType.voIP]
        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    
    // 处理更新的推送凭据
    func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
        print(credentials.token)
        let deviceToken = credentials.token.map { String(format: "%02x", $0) }.joined()
        print(deviceToken)
        // 将设备令牌保存到服务器
        MitekCallKeepPlugin.sharedInstance?.setDevicePushTokenVoIP(deviceToken)
    }
    
    func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
        print("didInvalidatePushTokenFor")
        MitekCallKeepPlugin.sharedInstance?.setDevicePushTokenVoIP("")
    }
    
    // 处理传入的推送
    func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
        print("didReceiveIncomingPushWith")
        guard type == .voIP else { return }
        
        let id = payload.dictionaryPayload["id"] as? String ?? ""
        let callerName = payload.dictionaryPayload["callerName"] as? String ?? ""
        let userId = payload.dictionaryPayload["callerId"] as? String ?? ""
        let handle = payload.dictionaryPayload["handle"] as? String ?? ""
        let isVideo = payload.dictionaryPayload["isVideo"] as? Bool ?? false
        
        let data = MitekCallKeep.Data(id: id, callerName: callerName, handle: handle, hasVideo: isVideo)
        // 设置更多数据
        data.extra = ["userId": userId, "platform": "ios"]
        data.appName = "Done"
        // data.iconName = ...
        // data.....
        MitekCallKeepPlugin.sharedInstance?.displayIncomingCall(data, fromPushKit: true)
    }   
}

使用方法

设置

你需要有基本的 CallKeep 配置以减少代码重复并使显示来电更加容易:

final callKeepBaseConfig = CallKeepBaseConfig(
    appName: 'Done',
    androidConfig: CallKeepAndroidConfig(
        logo: 'logo',
        notificationIcon: 'notification_icon',
        ringtoneFileName: 'ringtone.mp3',
        accentColor: '#34C7C2',
    ),
    iosConfig: CallKeepIosConfig(
        iconName: 'Icon',
        maximumCallGroups: 1,
    ),
);

显示来电

配置和uuid是必需的参数:

// 配置和uuid是必需的参数
final config = CallKeepIncomingConfig.fromBaseConfig(
    config: callKeepBaseConfig,
    uuid: uuid,
    contentTitle: 'Incoming call from Done',
    hasVideo: hasVideo,
    handle: handle,
    callerName: incomingCallUsername,
    extra: callData,
);
await CallKeep.instance.displayIncomingCall(config);

显示未接来电通知(仅限Android)

配置和uuid是必需的参数:

// 配置和uuid是必需的参数
final config = CallKeepIncomingConfig.fromBaseConfig(
    config: callKeepBaseConfig,
    uuid: uuid,
    contentTitle: 'Incoming call from Done',
    hasVideo: hasVideo,
    handle: handle,
    callerName: incomingCallUsername,
    extra: callData,
);
await CallKeep.instance.showMissCallNotification(config);

开始外呼

配置和uuid是必需的参数:

// 配置和uuid是必需的参数
final config = CallKeepOutgoingConfig.fromBaseConfig(
    config: DoneCallsConfig.instance.callKeepBaseConfig,
    uuid: uuid,
    handle: handle,
    hasVideo: hasVideo ?? false,
);
CallKeep.instance.startCall(config);

处理事件

监听事件:

CallKeep.instance.onEvent.listen((event) async {
    // TODO: 实现其他事件
    if (event == null) return;
    switch (event.type) {
        case CallKeepEventType.callAccept:
            final data = event.data as CallKeepCallData;
            print('call answered: ${data.toMap()}');
            NavigationService.instance
                .pushNamedIfNotCurrent(AppRoute.callingPage, args: data.toMap());
            if (callback != null) callback.call(event);
            break;
        case CallKeepEventType.callDecline:
            final data = event.data as CallKeepCallData;
            print('call declined: ${data.toMap()}');
            await requestHttp("ACTION_CALL_DECLINE_FROM_DART");
            if (callback != null) callback.call(data);
            break;
        default:
            break;
    }
});

自定义(Android)

你可以通过在 {{yourApp}}/android/app/src/main/res/values{{yourApp}}/android/app/src/main/res/values-{{languageCode}} 添加值来定制背景颜色和添加本地化文本。

主要值包括: 在 colors.xml 中:

<!-- 用于显示在自定义来电UI顶部的十六进制颜色值 -->
<color name="incoming_call_bg_color">#80ffffff</color>

strings.xml 中:

<!-- 接受按钮来电文本,便于本地化 -->
<string name="accept_text">接受</string>
<!-- 拒绝按钮来电文本,便于本地化 -->
<string name="decline_text">拒绝</string>
<!-- 未接来电文本,便于本地化 -->
<string name="text_missed_call">未接来电</string>
<!-- 回拨按钮文本,便于本地化 -->
<string name="text_call_back">回拨</string>
<!-- 自定义来电UI头部,便于本地化 -->
<!-- 这也可以从Flutter中设置 -->
<string name="call_header">来自CallKeep的来电</string>

更多关于Flutter通话保持插件mitek_callkeep的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter通话保持插件mitek_callkeep的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用mitek_callkeep插件的示例代码。mitek_callkeep插件主要用于处理来电界面(Call Screen)和通话保持功能。请注意,由于mitek_callkeep可能并非一个广为人知的库(在撰写时,callkeep是更常见的库名),以下示例将基于一个假设的mitek_callkeep插件的API设计,该设计类似于广泛使用的callkeep插件。

首先,确保在pubspec.yaml文件中添加依赖项(假设库名为mitek_callkeep,实际使用时请替换为正确的库名):

dependencies:
  flutter:
    sdk: flutter
  mitek_callkeep: ^x.y.z  # 替换为实际的版本号

然后运行flutter pub get来安装依赖。

接下来,在ios/Runner/Info.plistandroid/app/src/main/AndroidManifest.xml中添加必要的权限和配置,以允许应用处理来电。这些配置取决于具体的插件文档,以下是一个示例配置(针对callkeep插件,mitek_callkeep可能有所不同,请参考其官方文档):

iOS (Info.plist):

<key>UIBackgroundModes</key>
<array>
    <string>voip</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>
<key>NSVoIPUsageDescription</key>
<string>Need VoIP permissions for calling features</string>

Android (AndroidManifest.xml):

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

<service
    android:name="io.flutter.plugins.callkeep.CallKeepService"
    android:permission="android.permission.BIND_JOB_SERVICE"
    android:foregroundServiceType="voip">
    <intent-filter>
        <action android:name="android.telecom.CallScreeningService" />
    </intent-filter>
</service>

在Dart代码中,初始化并使用mitek_callkeep插件(假设其API与callkeep类似):

import 'package:flutter/material.dart';
import 'package:mitek_callkeep/mitek_callkeep.dart';

void main() {
  runApp(MyApp());
  _configureCallKeep();
}

void _configureCallKeep() {
  // 初始化 CallKeep
  MitekCallKeep().setup(
    android: AndroidInitSettings(
      packageName: 'com.example.myapp', // 替换为你的包名
      alicePackageName: null, // 如果需要
      notificationChannelName: 'My Call Keep Channel',
      notificationChannelDescription: 'Channel for Call Keep notifications',
      defaultToSpeaker: false,
    ),
    ios: IOSInitSettings(
      appName: 'MyApp',
    ),
  );

  // 监听来电事件
  MitekCallKeep().addEventListener((CallKeepEvent event) {
    switch (event.type) {
      case CallKeepEventType.didReceiveStartCallAction:
        _handleIncomingCall(event.uuid);
        break;
      case CallKeepEventType.didPerformSetMutedAction:
        _handleMuteCall(event.uuid, event.muted);
        break;
      // 处理其他事件...
      default:
        break;
    }
  });
}

void _handleIncomingCall(String uuid) {
  MitekCallKeep().startCall(uuid, handleType: 'generic');
  MitekCallKeep().displayIncomingCall(uuid, localizedCallerName: 'John Doe');
}

void _handleMuteCall(String uuid, bool muted) {
  // 在这里处理静音逻辑
  print('Call $uuid is ${muted ? 'muted' : 'unmuted'}');
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter CallKeep Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () {
              // 发起一个模拟来电
              final uuid = Uuid().v4(); // 使用uuid库生成唯一标识符
              _handleIncomingCall(uuid);
            },
            child: Text('Start Mock Call'),
          ),
        ),
      ),
    );
  }
}

请注意,上述代码中的Uuid()来自uuid包,你需要在pubspec.yaml中添加依赖:

dependencies:
  uuid: ^x.y.z  # 替换为实际的版本号

并且运行flutter pub get来安装它。

由于mitek_callkeep可能是一个假想的库名或者一个不常见的库,具体的API和配置可能会有所不同。务必参考该库的官方文档和示例代码进行集成。如果mitek_callkeep实际上并不存在,你可能需要查找并使用一个更广泛支持的库,如callkeep

回到顶部