TRTC 云助手 系统来电监听与处理

在使用语聊直播 App 的过程当中,经常会遇到移动设备系统来电的情形。此时会出现语聊直播 App 的音频采集被打断、音频播放干扰来电通话声音等问题。下面我们将分别介绍在 Android、iOS、Flutter、HarmonyOS 平台上,针对系统来电状态的监听方法,以及对应状态下 TRTC 的处理策略。

相关产品
TRTC 云助手 产品页,含版本规格、价格与购买入口。

二、步骤一:声明权限

在 AndroidManifest.xml 文件中添加以下权限声明,以便应用能够访问电话状态。

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

三、步骤二:检查并申请权限

检查权限授予情况,并在运行时动态申请 READ_PHONE_STATE 权限。

if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_PHONE_STATE}, PERMISSION_REQUEST_CODE);}

四、步骤三:注册广播接收器

注册广播接收器(BroadcastReceiver),用于监听特定的系统事件(如电话状态变化)。

@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 注册广播接收器 IntentFilter filter = new IntentFilter(); filter.addAction("android.intent.action.PHONE_STATE"); registerReceiver(callStateReceiver, filter);}@Overrideprotected void onDestroy() { super.onDestroy(); // 取消注册广播接收器 unregisterReceiver(callStateReceiver);}

五、步骤四:接收电话状态变化

通过广播接收器(BroadcastReceiver)接收系统电话状态变化,并进行通话状态映射。

private final BroadcastReceiver callStateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals("android.intent.action.PHONE_STATE")) { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); if (TelephonyManager.EXTRA_STATE_IDLE.equals(state)) { // 空闲状态 handleCallState("idle"); } if (TelephonyManager.EXTRA_STATE_RINGING.equals(state)) { // 来电状态 handleCallState("incoming"); } if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(state)) { // 通话状态 handleCallState("offhook"); } } }
};

六、步骤五:处理电话状态变化

处理通话状态变化,在特定状态下进行 TRTC 音频处理,避免对来电通话声音产生干扰。

private void handleCallState(String state) { Log.d("CallState", state); TRTCCloud mTRTCCloud = TRTCCloud.sharedInstance(this); if (!state.equals("idle")) { // 暂停播放所有远端用户的音频流 mTRTCCloud.muteAllRemoteAudio(true); } else { // 恢复播放所有远端用户的音频流 mTRTCCloud.muteAllRemoteAudio(false); }
}

七、iOS

iOS 平台有两类 API 可以监听获取系统电话状态,下面分别介绍两种方法的具体实现步骤。

八、方法一:CoreTelephony

这是一种较老的 API,虽然 Xcode 会提示该 API 已过时,但目前在 iOS 17 及以下系统版本仍能正常使用。

九、步骤一:引入头文件,声明 property

#import <CoreTelephony/CTCall.h>#import <CoreTelephony/CTCallCenter.h>@interface ViewController ()@property (nonatomic, strong) CTCallCenter *callCenter;@end

十、步骤二:注册回调,处理电话状态变化

self.callCenter = [[CTCallCenter alloc] init];self.callCenter.callEventHandler = ^(CTCall *call) { if ([call.callState isEqualToString:CTCallStateDialing]) { NSLog(@"正在拨号"); // 暂停播放所有远端用户的音频流 [self.trtcCloud muteAllRemoteAudio:YES]; } else if([call.callState isEqualToString:CTCallStateIncoming]) { NSLog(@"来电"); [self.trtcCloud muteAllRemoteAudio:YES]; } else if([call.callState isEqualToString:CTCallStateConnected]) { NSLog(@"正在通话"); [self.trtcCloud muteAllRemoteAudio:YES]; } else if([call.callState isEqualToString:CTCallStateDisconnected]) { NSLog(@"断开通话"); // 恢复播放所有远端用户的音频流 [self.trtcCloud muteAllRemoteAudio:NO]; } else { NSLog(@"未知"); }};

十一、方法二:CallKit

这是一种新的 API,但目前在中国大陆地区使用 CallKit,可能会无法通过 App Store 的审核,请酌情谨慎选择。

十二、步骤一:引入头文件,声明 property

#import <CallKit/CXCallObserver.h>#import <CallKit/CXCall.h>@interface ViewController () <CXCallObserverDelegate>@property (nonatomic, strong) CXCallObserver *callObserver;@end

十三、步骤二:设置代理

self.callObserver = [[CXCallObserver alloc] init];[self.callObserver setDelegate:self queue:dispatch_get_main_queue()];

十四、步骤三:实现回调,监听通话状态

#pragma mark – CXCallObserverDelegate- (void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call { NSLog(@"The unique identifier for the call: %@", call.UUID); NSLog(@"outgoing(拨打):%d onHold(挂起):%d hasConnected(已接通):%d hasEnded(已挂断):%d", call.outgoing, call.onHold, call.hasConnected, call.hasEnded); // 来电 if (!call.outgoing && !call.hasConnected && !call.hasEnded) { NSLog(@"来电"); // 暂停播放所有远端用户的音频流 [self.trtcCloud muteAllRemoteAudio:YES]; } // 拨打 if (call.outgoing && !call.hasConnected && !call.hasEnded) { NSLog(@"拨打"); [self.trtcCloud muteAllRemoteAudio:YES]; } // 接通 if (call.hasConnected && !call.hasEnded) { NSLog(@"接通"); [self.trtcCloud muteAllRemoteAudio:YES]; } // 挂断 if (call.hasEnded) { NSLog(@"挂断"); // 恢复播放所有远端用户的音频流 [self.trtcCloud muteAllRemoteAudio:NO]; }}

十五、Flutter

Flutter 应用可以通过底层原生平台监听系统电话状态,然后利用 EventChannel 传递来自原生平台的事件。

十六、步骤一:事件监听

在原生平台上,通过 StreamHandler 监听事件源(如系统电话状态变化)。

AndroidiOSprivate static final String CALL_STATUS_CHANNEL = "com.example.call_status_listener/callStatus";private EventChannel.EventSink eventSink;new EventChannel(getFlutterEngine().getDartExecutor().getBinaryMessenger(), CALL_STATUS_CHANNEL).setStreamHandler( new EventChannel.StreamHandler() { @Override public void onListen(Object arguments, EventChannel.EventSink events) { eventSink = events; IntentFilter filter = new IntentFilter(); filter.addAction("android.intent.action.PHONE_STATE"); registerReceiver(callStateReceiver, filter); } @Override public void onCancel(Object arguments) { eventSink = null; unregisterReceiver(callStateReceiver); } }
);var eventSink: FlutterEventSink?// 以CallKit为例,也可参考iOS原生部分的示例,使用CoreTelephonylet callObserver = CXCallObserver()override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { GeneratedPluginRegistrant.register(with: self) let controller : FlutterViewController = window?.rootViewController as! FlutterViewController let eventChannel = FlutterEventChannel(name: "com.example.call_status_listener/callStatus", binaryMessenger: controller.binaryMessenger) eventChannel.setStreamHandler(self) callObserver.setDelegate(self, queue: nil) return super.application(application, didFinishLaunchingWithOptions: launchOptions)}func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { self.eventSink = events return nil}func onCancel(withArguments arguments: Any?) -> FlutterError? { self.eventSink = nil return nil}

十七、步骤二:事件发送

在原生平台上,当事件发生时,通过 EventSink 将事件发送到 Flutter。

AndroidiOSprivate final BroadcastReceiver callStateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals("android.intent.action.PHONE_STATE")) { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); if (eventSink != null) { if (TelephonyManager.EXTRA_STATE_IDLE.equals(state)) { eventSink.success("idle"); } if (TelephonyManager.EXTRA_STATE_RINGING.equals(state)) { eventSink.success("incoming"); } if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(state)) { eventSink.success("offhook"); } } } }
};@objc func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) { // 挂断 if call.hasEnded == true { eventSink?("idle") } // 拨打 else if call.isOutgoing == true && call.hasConnected == false { eventSink?("offhook") } // 来电 else if call.isOutgoing == false && call.hasConnected == false && call.hasEnded == false { eventSink?("incoming") } // 接通 else if call.hasConnected == true && call.hasEnded == false { eventSink?("offhook") }}

十八、步骤三:事件接收

在 Flutter 端,通过 EventChannel 通道接收特定事件,并处理这些事件。

static const EventChannel _callStateChannel = EventChannel("com.example.call_status_listener/callStatus");String _callState = "Unknown";TRTCCloud trtcCloud = (await TRTCCloud.sharedInstance())!;_callStateChannel.receiveBroadcastStream().listen(_onCallStateChanged, onError: _onError);void _onCallStateChanged(Object? state) { setState(() { _callState = state.toString(); print("_callState: " + _callState); if (_callState != "idle") { // 暂停播放所有远端用户的音频流 trtcCloud.muteAllRemoteAudio(true); } else { // 恢复播放所有远端用户的音频流 trtcCloud.muteAllRemoteAudio(false); } });
}void _onError(Object error) { setState(() { _callState = "Failed to get call state: $error"; print("_callState: " + _callState); });
}

十九、HarmonyOS

在 HarmonyOS 平台上,可通过电话状态变化监听(callStateChange),主动获取系统电话的“空闲、来电、通话”三种状态。

注意:说明:

注意:根据当前华为产品文档,第三方应用可获取通话状态 state 字段,无需读取号码信息;仅当需要读取来电号码 number 字段时,才需要声明 ohos.permission.READ_CALL_LOG(系统权限)。实际权限要求可能因 HarmonyOS 版本和 API Level 而有所差异,请以目标版本的产品文档为准。

20、步骤一:注册并监听电话状态变化

通过 @kit.TelephonyKit 的 observer.on('callStateChange') 订阅通话状态变化事件,并将系统状态映射为空闲、来电、通话三态。

import { observer, call } from '@kit.TelephonyKit';// 注册通话状态监听function registerCallObserver(): void { observer.on('callStateChange', (data: observer.CallStateInfo) => { switch (data.state) { case call.CallState.CALL_STATE_IDLE: // 空闲状态(state = 0) onCallStateChanged('idle'); break; case call.CallState.CALL_STATE_RINGING: // 来电状态(state = 1) onCallStateChanged('incoming'); break; case call.CallState.CALL_STATE_OFFHOOK: // 通话状态(state = 2) onCallStateChanged('offhook'); break; default: // 无效状态(state = -1) break; } });}// 取消注册通话状态监听(如在 aboutToDisappear 时调用)function unregisterCallObserver(): void { observer.off('callStateChange');}

21、步骤二:处理电话状态变化

处理通话状态变化,在非空闲状态下进行 TRTC 音频处理,避免对来电通话声音产生干扰。

import { getTRTCShareInstance } from 'liteavsdk';const trtc = getTRTCShareInstance(getContext());function onCallStateChanged(state: 'idle' | 'incoming' | 'offhook'): void { if (state === 'idle') { // 空闲状态:恢复播放所有远端用户的音频流 trtc.muteAllRemoteAudio(false); } else { // 来电 / 通话状态:暂停播放所有远端用户的音频流 trtc.muteAllRemoteAudio(true); }}

赞 (0)
上一篇 4天前
下一篇 4天前