添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

场景介绍

应用内通话消息,支持应用实现网络音视频通话的能力。当终端处于锁屏或解锁两种不同状态时,Push Kit将分别进行以下处理:

  • 终端处于锁屏状态时,可在锁屏上点击接听或拒绝按钮。锁屏状态下只支持接听语音。
  • 终端处于解锁状态时,网络音视频通话呼叫消息显性展示于横幅,支持用户接听视频或语音。

接听视频时会拉起应用内的接听界面。接通后,可以正常挂断(主动挂断/被动挂断)应用内通话消息。

应用内通话消息样式可参考如下示例,真实样式请以实际效果为准:

锁屏

来电横幅

说明
  • 应用内通话消息当前仅支持Phone与Tablet机型。
  • 应用内通话消息的问题场景请参见 指导
  • 应用内通话消息的pushOptions. ttl 建议设置为 30~60秒

开通权益

推送应用内通话消息需要申请场景化消息权益,请参见 申请推送应用内通话消息权益

频控规则

正式发布阶段 ,单设备单应用下每日推送消息总条数受 设备消息频控 限制,所有场景化消息发送条数不超过 3000 条。

开发步骤

  1. 参见指导 获取Push Token
  2. 在您的工程内创建一个UIAbility类型的组件,如VoIPUIAbility.ets(在项目工程的 src/main/ets/entryability 目录下),负责处理应用内通话消息的主流程,并完成 onCreate ()、 onWindowStageCreate ()、 onDestroy ()方法的覆写,代码示例如下:

    收起
    自动换行
    深色代码主题
    复制
    1. import { UIAbility } from '@kit.AbilityKit';
    2. import { pushService } from '@kit.PushKit';
    3. import { window } from '@kit.ArkUI';
    4. import { hilog } from '@kit.PerformanceAnalysisKit';
    5. import { VoipCallService } from '../service/VoipCallService';
    6. export default class VoIPUIAbility extends UIAbility {
    7. onCreate(): void {
    8. hilog.info(0x0000, 'testTag', `VoIPUIAbility onCreate`);
    9. try {
    10. pushService.receiveMessage('VoIP', this, async (data) => {
    11. // process message,并建议对Callback进行try-catch
    12. try {
    13. await VoipCallService.processVoIPMainMsg(data.data, this.context);
    14. } catch (error) {
    15. hilog.error(0x0000, 'testTag', 'Failed to process VoIP message: %{public}d %{public}s',
    16. error.code,
    17. error.message);
    18. }
    19. });
    20. } catch (e) {
    21. hilog.info(0x0000, 'testTag', `Failed to register VOIP, error: ${e.code}, ${e.message}.`);
    22. }
    23. }
    24. onWindowStageCreate(windowStage: window.WindowStage): void {
    25. hilog.info(0x0000, 'testTag', `VoIPUIAbility onWindowStageCreate`);
    26. windowStage.loadContent('pages/CalleePage');
    27. }
    28. onDestroy(): void {
    29. hilog.info(0x0000, 'testTag', 'VoIPUIAbility onDestroy');
    30. }
    31. }

    VoipCallService.ets(在项目工程的 src/main/ets/service 目录下),处理应用内通话消息,代码示例如下:

    收起
    自动换行
    深色代码主题
    复制
    1. import { voipCall } from '@kit.CallServiceKit';
    2. import { hilog } from '@kit.PerformanceAnalysisKit';
    3. import { common } from '@kit.AbilityKit';
    4. import { image } from '@kit.ImageKit';
    5. import { resourceManager } from '@kit.LocalizationKit';
    6. export interface VoipScene {
    7. scene: string;
    8. }
    9. export interface Content {
    10. data: string;
    11. header: string;
    12. callId: string;
    13. }
    14. export class VoipCallService {
    15. private static callId: string | undefined;
    16. public static async processVoIPMainMsg(data: string,
    17. context: common.UIAbilityContext): Promise<void> {
    18. hilog.info(0x0000, 'testTag', `Process VoIP message: ${data}`);
    19. let content: Content = JSON.parse(data);
    20. let scene: VoipScene = JSON.parse(content.data);
    21. let callId: string = content.callId;
    22. if (!callId) {
    23. hilog.error(0x0000, 'testTag', `CallId is null`);
    24. }
    25. VoipCallService.callId = callId;
    26. // 注册voipCallUiEvent事件
    27. voipCall.on('voipCallUiEvent', async (event) => {
    28. hilog.info(0x0000, 'testTag', `Process voip call ui event: ${JSON.stringify(event)}.`);
    29. await VoipCallService.processVoipCallEvent(event.voipCallUiEvent);
    30. });
    31. const resourceMgr: resourceManager.ResourceManager = context.resourceManager;
    32. // example.png表示用户头像,取值为“/resources/rawfile”路径下的文件名
    33. const fileData: Uint8Array = await resourceMgr.getRawFileContent('example.png');
    34. const buffer = fileData.buffer;
    35. const imageSource: image.ImageSource = image.createImageSource(buffer);
    36. const pixelMap: image.PixelMap = await imageSource.createPixelMap();
    37. if (pixelMap) {
    38. pixelMap.getImageInfo((err, imageInfo) => {
    39. if (imageInfo) {
    40. hilog.info(0x0000, 'testTag',
    41. `User profile imageInfo: ${imageInfo.size.width} * ${imageInfo.size.height}.`);
    42. }
    43. });
    44. }
    45. // 构造上报来电的参数。注意,voipCallType.scene为您自定义的场景类型字段,从云侧推送消息时,请注意与端侧取值保持一致
    46. let call: voipCall.VoipCallAttribute = {
    47. callId: callId,
    48. voipCallType: scene?.scene === 'video' ? voipCall.VoipCallType.VOIP_CALL_VIDEO :
    49. voipCall.VoipCallType.VOIP_CALL_VOICE,
    50. userName: 'push',
    51. userProfile: pixelMap,
    52. abilityName: 'VoIPUIAbility',
    53. voipCallState: voipCall.VoipCallState.VOIP_CALL_STATE_RINGING
    54. };
    55. // 上报来电
    56. let error = await voipCall.reportIncomingCall(call);
    57. hilog.info(0x0000, 'testTag', `ReportIncomingCall result: ${error}.`);
    58. // ...应用播放振动和铃声
    59. }
    60. public static async processVoipCallEvent(event: voipCall.VoipCallUiEvent) {
    61. switch (event) {
    62. case voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_VOICE_ANSWER:
    63. case voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_VIDEO_ANSWER:
    64. // 立即向Call Service Kit上报answered状态
    65. await voipCall.reportCallStateChange(VoipCallService.callId,
    66. voipCall.VoipCallState.VOIP_CALL_STATE_ANSWERED);
    67. //...在应用内完成接听
    68. // 应用内接听后,向Call Service Kit上报active状态
    69. voipCall.reportCallStateChange(VoipCallService.callId,
    70. voipCall.VoipCallState.VOIP_CALL_STATE_ACTIVE);
    71. break;
    72. case voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_REJECT:
    73. case voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_HANGUP:
    74. // ...应用内完成挂断
    75. // 向Call Service Kit上报通话状态
    76. await voipCall.reportCallStateChange(VoipCallService.callId,
    77. voipCall.VoipCallState.VOIP_CALL_STATE_DISCONNECTED);
    78. break;
    79. default: {
    80. break;
    81. }
    82. }
    83. }
    84. public static close(): void {
    85. hilog.info(0x0000, 'testTag', `Close VoIP`);
    86. VoipCallService.processVoipCallEvent(voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_HANGUP);
    87. voipCall.off('voipCallUiEvent');
    88. }
    89. }
    注意

    需要在项目工程的src/main/resources/rawfile目录下添加example.png,表示来电时的用户头像。

    • UIAbility.onCreate是同步接口,不支持异步回调,需要在onCreate生命周期的入口,完成 pushService.receiveMessage ()注册,并且保证在注册前没有等待异步方法执行的调用。
    • receiveMessage ()回调中接收应用内通话消息,建议应用提前和服务器建连,用户点击接听后可以立即进行通话,并调用 voipCall.on ()接口注册监听通话状态回调。用户点击接听或者拒绝接听之后,系统会通过应用注册的事件监听通话状态回调结果。
    • 应用需要在10秒内调用 voipCall.reportIncomingCall ()接口上报通话来电状态,调用完成之后,系统会弹出应用内通话横幅通知。 voipCall.reportIncomingCall () 接口入参中的callId需要使用receiveMessage()回调中的callId
    • 如果应用来电消息建立失败,需要调用 voipCall.reportIncomingCallError ()通知来电消息建立失败。如果应用在前台,通过自己的网络连接接收到来电消息,调用 voipCall.reportIncomingCall ()接口上报了通话来电状态,后面才收到Push推送的应用内通话消息,在该消息处理中需要调用 voipCall.reportIncomingCallError ()上报 应用线路忙
    • 应用内通话主要有三种回调状态,分别为:接听状态、拒绝状态和挂断状态。
      • 在接听状态回调中,应用在建立连接成功之后,需要调用 voipCall.reportCallStateChange ()接口上报通话激活状态。
      • 在拒绝接听状态回调中,应用断开和服务器的连接之后,需要调用 voipCall.reportCallStateChange ()接口上报通话断开状态。
      • 在应用进行应用内通话的同时,若运营商来电,会弹出运营商来电接听界面,用户点击接听运营商来电之后,会回调应用内通话挂断状态,在回调方法中应用需要自行断开和服务器的连接,并调用 voipCall.reportCallStateChange ()接口上报通话断开状态。
    • 有关应用内通话回调状态的更多信息,详情请参见 Call Service Kit简介
    • 应用上报通话来电状态之后,可以调用 vibrator.startVibration 触发振动,有关振动的更多详情,请参见 Sensor Service Kit简介 。可以使用AVPlayer播放应用铃声,音频流建议设置为铃声,usage设置为STREAM_USAGE_RINGTONE,效果为开始响铃,播放的音乐会暂停播放。同时推荐使用AudioSession管理音频焦点,可以保证接听过程中、通话过程中都保持音频焦点,详情请参见: Audio Kit简介
    • 进行音视频通话时,若您的应用处于Overhead场景(设备发热严重或负载较重,Level=4),请降低码率和帧率,或关闭视频流降级为音频。相关说明请参见Basic Services Kit(基础服务)提供的接口 getLevel ()。

  3. 在项目工程的 src/main/ets/pages 目录添加:视频接听页面CalleePage.ets,代码示例如下:

    收起
    自动换行
    深色代码主题
    复制
    1. import CallComponent from '../component/CallComponent';
    2. import { router } from '@kit.ArkUI';
    3. import { hilog } from '@kit.PerformanceAnalysisKit';
    4. @Entry
    5. @Component
    6. struct CalleePage {
    7. @StorageLink('close') @Watch('close') end: boolean | undefined = undefined;
    8. aboutToAppear() {
    9. hilog.info(0x0000, 'testTag', `CalleePage aboutToAppear`);
    10. this.end = false;
    11. }
    12. private close() {
    13. if (this.end) {
    14. hilog.info(0x0000, 'testTag', `CalleePage close`);
    15. router.back(); // 此处仅为示例(跳转返回),请根据实际情况设定路由
    16. }
    17. }
    18. aboutToDisappear() {
    19. hilog.info(0x0000, 'testTag', `CalleePage aboutToDisappear`);
    20. }
    21. build() {
    22. Column() {
    23. CallComponent({})
    24. }
    25. }
    26. }

    CallComponent.ets(在项目工程的 src/main/ets/component 目录下),代码示例如下:

    收起
    自动换行
    深色代码主题
    复制
    1. import { VoipCallService } from '../service/VoipCallService';
    2. import { voipCall } from '@kit.CallServiceKit';
    3. @Component
    4. export default struct CallComponent {
    5. @StorageLink('close') end: boolean | undefined = undefined;
    6. build() {
    7. Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween }) {
    8. Row() {
    9. }
    10. .width('100%')
    11. .justifyContent(FlexAlign.Center)
    12. Row({ space: 30 }) {
    13. Column() {
    14. Button()
    15. .width(80)
    16. .height(80)
    17. .backgroundColor(Color.Green)
    18. .onClick(() => {
    19. VoipCallService.processVoipCallEvent(voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_VIDEO_ANSWER);
    20. })
    21. Text('Answer').fontColor(Color.White).padding({ top: 5 })
    22. }
    23. Column() {
    24. Button()
    25. .width(80)
    26. .height(80)
    27. .backgroundColor(Color.Red)
    28. .onClick(() => {
    29. this.end = true;
    30. VoipCallService.close();
    31. })
    32. Text('Hang Up').fontColor(Color.White).padding({ top: 5 })
    33. }
    34. }
    35. .width('100%')
    36. .justifyContent(FlexAlign.Center)
    37. }
    38. .padding('30 10')
    39. .backgroundColor(Color.Black)
    40. }
    41. }

    在项目工程的 src/main/resources/base/profile/main_pages.json添加page目录,示例如下:

    收起
    自动换行
    深色代码主题
    复制
    1. {
    2. "src": [
    3. "pages/Index",
    4. "pages/CalleePage"
    5. ]
    6. }
    注意

    示例代码提供的页面效果仅供开发参考,不代表最终效果。

  4. 在项目工程的 src/main/module.json5 文件的 abilities 模块中配置VoIPUIAbility的 actions 信息。

    收起
    自动换行
    深色代码主题
    复制
    1. "abilities": [
    2. {
    3. "name": "VoIPUIAbility",
    4. "srcEntry": "./ets/entryability/VoIPUIAbility.ets",
    5. "launchType": "singleton",
    6. "description": "VoIPUIAbility test",
    7. "startWindowIcon" : "$media:startIcon",
    8. "startWindowBackground": "$color:start_window_background",
    9. "exported": false,
    10. "skills": [
    11. {
    12. "actions": ["action.ohos.push.listener"]
    13. }
    14. ]
    15. }
    16. ]
    • actions:内容为 action.ohos.push.listener ,有且只能有一个ability定义该action, 若同时添加uris参数,则uris内容需为空

  5. 应用服务端调用REST API推送消息,消息详情可参见 场景化消息API接口功能介绍

应用内通话消息

  1. 如果您需要呼叫,应用服务器可以调用REST API推送应用内通话消息,请求示例如下:

    收起
    自动换行
    深色代码主题
    复制
    1. // Request URL
    2. POST https://push-api.cloud.huawei.com/v3/[projectId]/messages:send
    3. // Request Header
    4. Content-Type: application/json
    5. Authorization: Bearer eyJr*****OiIx---****.eyJh*****iJodHR--***.QRod*****4Gp---****
    6. push-type: 10
    7. {
    8. "pushOptions": {
    9. "ttl": 30
    10. },
    11. "payload": {
    12. "extraData": "{\"scene\": \"voice\"}"
    13. },
    14. "target": {
    15. "token": ["MAMzLg**********aZW"]
    16. }
    17. }
    • [projectId]:项目ID,登录 AppGallery Connect 网站,选择“开发与服务”,在项目列表中选择对应的项目,左侧导航栏选择“项目设置”,在该页面获取。
    • Authorization:JWT格式字符串,可参见Authorization获取。
    • push-type:10表示应用内通话消息场景。
    • token:Push Token,可参见3.4 获取Push Token获取。
    • extraData:携带的额外数据,字符串类型。详情参见extraData。extraData数据获取请参考 示例代码
    • ttl:消息缓存时间,建议设置为30~60秒,详见pushOptions.ttl。

说明
  • 应用内通话消息只能用于音视频通话场景唤醒应用,完成呼叫,不要通过此种类型消息来挂断来电或者和应用通信,应用应该使用自己建立的网络连接和应用通信。相比应用服务器推送Push消息,使用现有的网络连接和应用通信通常会更快,在网络不佳的情况下,推送的Push消息可能无法到达应用。
  • 应用无论是否在前台,自己的网络连接存在时,建议您通过Push推送应用内通话消息,再通过自己的网络连接发送通话消息,保证该呼叫能够到达应用。

未接来电通知

  1. 如果您需要给被叫发送未接来电通知,应用服务器可以调用REST API推送 通知消息 。以通知扩展消息为例,请求示例如下:

    收起
    自动换行
    深色代码主题
    复制
    1. // Request URL
    2. POST https://push-api.cloud.huawei.com/v3/[projectId]/messages:send
    3. // Request Header
    4. Content-Type: application/json
    5. Authorization: Bearer eyJr*****OiIx---****.eyJh*****iJodHR--***.QRod*****4Gp---****
    6. push-type: 0
    7. {
    8. "pushOptions": {
    9. "ttl":86400
    10. },
    11. "payload": {
    12. "extraData": "通知扩展场景携带的额外数据",
    13. "notification": {
    14. "category": "MISS_CALL",
    15. "title": "通知标题",
    16. "body": "通知内容",
    17. "clickAction": {
    18. "actionType": 0
    19. },
    20. "appMessageId": 12345,
    21. }
    22. },
    23. "target": {
    24. "token": ["MAMzLg**********aZW"]
    25. }
    26. }
    • push-type:0表示通知消息场景。
    • category:消息自分类类别,设置为MISS_CALL,请参见 参数说明 ,发送消息前请确保您已 申请通知消息自分类权益
    • appMessageId:应用消息的唯一标识。被叫挂断,被叫方VoIP应用在前台时应用可以通过调用 Notification Kit 发送未接来电通知。被叫方VoIP应用在后台时,可以通过Push推送未接来电通知。应用可能存在前后台状态判断不准确,同一电话会产生两条未接来电,建议您通过Notification Kit和Push Kit推送的未接来电通知使用相同的appMessageId,系统会进行通知去重。
    • 其他参数说明可参见 通知消息请求体参数说明