HarmonyOS鸿蒙Next中VOIP应用内通话的横幅通知点击接受后直接拉起UiAbility

HarmonyOS鸿蒙Next中VOIP应用内通话的横幅通知点击接受后直接拉起UiAbility 是否能否在用户点击VOIP来电通知的接受按钮后,不仅仅是给APP发送voipCallUiEvent,而且还把UiAbility直接拉起来,我们想实现一个全屏通话的界面。

voipCallType选成video是可以直接拉起UiAblility的,但是VOICE不行。用户接听后还是一个小的banner,除非再次点击banner的其他地方才会拉起整个UiAbility。

13 回复

开发者您好,单人语言通话点击接听按钮不会跳转,多人语音通话点击接听按钮会进行跳转,此为系统规格。建议开发者配置多人语音相关的参数isconferencecall为true看下是否能满足您的需求。

更多关于HarmonyOS鸿蒙Next中VOIP应用内通话的横幅通知点击接受后直接拉起UiAbility的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


经查阅相关文档,您可以尝试在voipCallUiEvent事件中当判断为语音接听动作时拉起UIAbility,相关参考文档:

来电场景开发文档:

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/incoming-calls

订阅voipCallUiEvent事件:

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/call-voipcall#voipcallonvoipcalluievent

启动应用内的uiability:

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/uiability-intra-device-interaction#启动应用内的uiability

不行的,启动应用内startAbility方法只能在前台调用,后台是无法调用的,没有start_ability_from_background的权限。

如果这样也不行就只能提交工单问问平台技术支持大佬了。

你这个情况属于Push Kit和Call Kit配合时候,来了VoIP通知,只是拉起应用主进程在后台,通知应用注册的voipCallUiEvent事件。这些在后台执行的逻辑,对三方应用没办法在这些流程里拉起其他ability。

另外从后台拉起其他应用或Ability是危险的行为,受系统严格管控是必须的。所以从设计上想办法吧。

先说原因VIDEO类型通常会被系统自动拉起全屏界面,因为视频通话需要立即展示视频画面,但是voipCallType为VOICE的通话,系统只会保持横幅状态,需要用户再次点击横幅其他区域才能进入全屏界面,这个是系统默认的行为,也是比较符合实际的;

你可以考虑在事件回调中判断用户操作,并决定是否拉起全屏界面

import { voipCall } from '@kit.CallServiceKit';
import { common, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

// 在UIAbility的合适生命周期(如onCreate)中订阅事件
voipCall.on('voipCallUiEvent', (voipCall.VoipCallUiEventInfo) => {
  hilog.info(0x0000, 'testTag', `收到voipCallUiEvent: ${data.voipCallUiEvent}, callId: ${data.callId}`);
  
  // 判断是否为语音接听事件
  if (data.voipCallUiEvent === voipCall.VoipCallUiEvent.VOIP_CALL_UI_EVENT_ACCEPT_VOICE) {
   
    let context: common.UIAbilityContext = this.context;
    
    // 构造Want,指定要拉起的全屏通话UIAbility
    let want: Want = {
      bundleName: 'com.your.bundle', // 用你的应用包名
      abilityName: 'FullScreenCallAbility', // 名称
      parameters: {
        callId: data.callId, // 传递通话ID
        // 其他自定义参数
      }
    };
    
    // 拉起目标UIAbility
    context.startAbility(want).then(() => {
      hilog.info(0x0000, 'testTag', '成功拉起全屏通话界面');
    }).catch((err: BusinessError) => {
      hilog.error(0x0000, 'testTag', `拉起全屏界面失败: ${err.code}, ${err.message}`);
    });
  }
});

用代码来拉起全屏。

应用后台是没有权限startAbility的

Voice 通话点击接听不自动拉起 Ability 是系统限制,无法通过配置绕过。必须在 VOIP_CALL_EVENT_VOICE_ANSWER 回调里手动 startAbility 或 restoreWindow,同时建议采用"上报两次状态(ANSWERED → ACTIVE)"给用户接通中的视觉反馈。

在 Voice 接听回调里手动拉起
官方文档也明确说明:voipCallUiEvent 事件回调不会自动将应用拉回前台,开发者需要自己恢复窗口或启动 Ability
推荐代码(上报两次状态 + 手动拉起):

import { voipCall } from '@kit.CallServiceKit';
import { window } from '@kit.ArkUI';
import { common } from '@kit.AbilityKit';

// 1. 提前订阅事件(必须在 reportIncomingCall 之前)
voipCall.on('voipCallUiEvent', async (callback) => {
  if (callback?.voipCallUiEvent === voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_VOICE_ANSWER) {
    
    // 第一步:立即上报 ANSWERED,横幅变成"正在接通"
    await voipCall.reportCallStateChange(
      callback.callId, 
      voipCall.VoipCallState.VOIP_CALL_STATE_ANSWERED
    );

    // 第二步:拉起通话界面(关键!系统不会帮你拉)
    const context = getContext(this) as common.UIAbilityContext;
    await context.startAbility({
      bundleName: context.applicationInfo.name,
      abilityName: 'VoipCallAbility',  // 你的全屏通话 Ability
      parameters: { callId: callback.callId }
    });

    // 或者如果 Ability 已在后台,恢复窗口:
    // const windowStage = AppStorage.get<window.WindowStage>('windowStage');
    // if (windowStage) {
    //   const mainWindow = await windowStage.getMainWindow();
    //   await windowStage.restoreWindow(mainWindow);
    // }

    // 第三步:应用内真正接通后,上报 ACTIVE
    await this.establishVoiceConnection(callback.callId);
    await voipCall.reportCallStateChange(
      callback.callId,
      voipCall.VoipCallState.VOIP_CALL_STATE_ACTIVE
    );
  }
});

特别注意:首次来电的拉起机制

还有一个容易踩的坑:如果这是首次创建横幅(应用从未上报过 ACTIVE 状态),点击接听可能直接拉起 Ability 且不走 voipCallUiEvent 回调。此时你需要在 VoipAbility 的 onWindowStageCreate 里处理接听逻辑。

兜底方案:无论系统是否自动拉起,都在 VoipAbility 的入口和 voipCallUiEvent 回调里同时处理接听逻辑,确保两条路径都能接通。

首先应用如果不在前台,是没有权限调用startAbility的。

其次,恢复窗口有restoreWindow这个方法?只有Window.restore方法。而且在后台即便是调用也是失败。

这个应该是鸿蒙当前版本对三方 VOIP 应用的设计死锁,我建议你提交工单问问平台技术支持大佬了

在 HarmonyOS Next 中,VOIP 应用可实现 CallExtensionAbility 并注册为 type: 'voip'。通知点击“接受”时,系统会通过 onCallStarted 回调自动拉起关联的 UiAbility,无需开发者额外处理。需在 module.json5 中配置 CallExtensionAbility 及其 want 指向目标 Ability。

在HarmonyOS Next中,VoIP来电通知的voipCallTypeVOICE时,系统默认只发送voipCallUiEvent而不自动拉起全屏UIAbility。要实现点击“接受”后直接显示全屏通话界面,需要在接收事件的Ability中主动启动目标通话UI。

示例:在承载VoIP服务的UIAbility里重写onVoipCallUiEvent(callId: string, event: voipCallUiEvent),当event为接听时,调用如下代码启动全屏通话Ability:

onVoipCallUiEvent(callId: string, event: voipCallUiEvent) {
  if (event.callEvent === voipCallUiEvent.ACCEPT) {
    let want = {
      bundleName: 'com.example.app',
      abilityName: 'FullScreenCallAbility',
      parameters: {
        callId: callId,
        isIncoming: true
      },
      action: 'ohos.want.action.voice_call'
    };
    this.context.startAbility(want);
  }
}

注意:通话期间需持有长时任务,避免从后台启动受限。此方式可确保用户点击通知接受后,直接呈现全屏通话界面。

回到顶部