# 来电场景

#### 场景介绍

应用接收到来自网络的音/视频通话，称为来电场景。

来电场景的效果图展示如下：

| 语音来电 | 视频来电 | 视频来电（不支持语音接听） |
| --- | --- | --- |
|  |  |  |
| 通话中 | 锁屏语音来电 | 锁屏视频来电 |
|  |  |  |

#### 约束与限制

来电场景支持Phone、Tablet设备，并从6.0.0(20)版本开始支持Wearable设备，6.1.1(24)版本开始支持PC/2in1设备。

#### 业务流程

#### 来电场景：接听流程图

#### 来电场景：拒接流程图

#### 接口说明

来电场景的接口，由voipCall提供。更多接口信息详见接口文档。

| 接口名 | 描述 |
| --- | --- |
| on(type: 'voipCallUiEvent', callback: Callback<VoipCallUiEventInfo>): void | 订阅voipCallUiEvent事件。 |
| off(type: 'voipCallUiEvent', callback?: Callback<VoipCallUiEventInfo>): void | 取消订阅voipCallUiEvent事件。 |
| reportIncomingCall(voipCallAttribute: VoipCallAttribute): Promise<ErrorReason> | 上报来电。 |
| reportCallAudioEventChange(callId: string, callAudioEvent: CallAudioEvent): Promise<void> | 上报音频事件。 |
| reportCallStateChange(callId: string, callState: VoipCallState): Promise<void> | 上报通话状态改变。 |
| reportCallStateChange(callId: string, callState: VoipCallState, callType: VoipCallType): Promise<void> | 上报通话状态改变，并指定通话类型。 |

#### 开发步骤

1.  导入相关依赖。
    
    ```
    import { voipCall } from '@kit.CallServiceKit';
    import { image } from '@kit.ImageKit';
    import { hilog } from '@kit.PerformanceAnalysisKit';
    ```
    
2.  为了感知到用户在横幅通知上做的接听、挂断、静音与解除静音等操作，应用需要注册voipCallUiEvent事件。建议在上报来电之前注册。
    
    示例代码如下：
    
    ```
    // 注册voipCallUiEvent事件
    voipCall.on('voipCallUiEvent', callback => {
      hilog.info(0x0000, 'CallDemo', 'Succeeded in registering voipCallUiEvent');
    });
    ```
    
3.  应用内部建立通话连接之后，需要向Call Service Kit上报来电，并携带通话信息，详见VoipCallAttribute。
    
    如果当时应用在后台，系统会展示来电横幅。
    
    示例代码如下：
    
    ```
    // 构造上报来电的参数
    let voipCallAttribute: voipCall.VoipCallAttribute = {
      callId: '1234567890',
      voipCallType: voipCall.VoipCallType.VOIP_CALL_VOICE,
      userName: 'Callman',
      userProfile: image.createPixelMapSync(new ArrayBuffer(100), { size: { width: 90, height: 90 } }),
      abilityName: 'VoipCallAbility',
      voipCallState: voipCall.VoipCallState.VOIP_CALL_STATE_RINGING,
      showBannerForIncomingCall: true
    };
    
    // 上报来电
    voipCall.reportIncomingCall(voipCallAttribute).then(errorReason => {
      if (errorReason == voipCall.ErrorReason.ERROR_NONE) {
        hilog.info(0x0000, 'CallDemo', 'Succeeded in reporting the incoming call');
      } else {
        hilog.error(0x0000, 'CallDemo', 'Failed to report the incoming call: %{public}d', errorReason);
      }
    });
    ```
    
    对于视频通话，可以通过参数isVoiceAnswerSupported指定是否允许语音接听，示例代码如下：
    
    ```
    // 构造上报来电的参数
    let voipCallAttribute: voipCall.VoipCallAttribute = {
      callId: '1234567890',
      voipCallType: voipCall.VoipCallType.VOIP_CALL_VIDEO,
      userName: 'Jack',
      userProfile: image.createPixelMapSync(new ArrayBuffer(100), { size: { width: 90, height: 90 } }),
      abilityName: 'VoipCallAbility',
      voipCallState: voipCall.VoipCallState.VOIP_CALL_STATE_RINGING,
      showBannerForIncomingCall: true,
      isVoiceAnswerSupported: false  // 视频通话不支持语音接听
    };
    ```
    
4.  接收到来电之后，用户可以选择接听或拒接。
    
    接听有两种开发方式：上报两次状态、只上报一次状态。
    
    *   上报两次状态（推荐）。
        
        以语音通话为例，应用在接收到VOIP\_CALL\_EVENT\_VOICE\_ANSWER事件回调之后，立即向Call Service Kit上报VOIP\_CALL\_STATE\_ANSWERED状态，并同时执行应用内接听。
        
        在完成应用内接听之后，再向Call Service Kit上报VOIP\_CALL\_STATE\_ACTIVE状态，系统会更新通话横幅。
        
        示例代码如下：
        
        ```
        voipCall.on('voipCallUiEvent', callback => {
          if (callback?.voipCallUiEvent == voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_VOICE_ANSWER) {
            // 立即向Call Service Kit上报answered状态
            voipCall.reportCallStateChange(callback.callId, voipCall.VoipCallState.VOIP_CALL_STATE_ANSWERED);
        
            // ...在应用内完成接听
        
            // 应用内接听后，向Call Service Kit上报active状态
            voipCall.reportCallStateChange(callback.callId, voipCall.VoipCallState.VOIP_CALL_STATE_ACTIVE);
          }
        });
        ```
        
        接听过程的效果图展示如下：
        
        | 正在接通 | 接通后 |
        | --- | --- |
        |  |  |
        
        通话接听时，上报两次状态的好处是：
        
        因为网络等原因，从用户点击接听，到通话真正被接通的时间间隔可能比较长（比如，1s左右）。这段时间，如果横幅通知的样式不变，一直停留在来电状态，用户可能认为点击接听无响应，体验不好。
        
        上报两次状态，可以在接听的过程中，在界面上给用户以反馈。
        
    *   只上报一次状态。
        
    
    以语音通话为例，应用在接收到VOIP\_CALL\_EVENT\_VOICE\_ANSWER事件回调之后，执行应用内接听。
    
    一直到完成应用内接听后，再向Call Service Kit上报VOIP\_CALL\_STATE\_ACTIVE状态。
    
    示例代码如下：
    
    ```
    voipCall.on('voipCallUiEvent', callback => {
      if (callback?.voipCallUiEvent == voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_VOICE_ANSWER) {
        // ...在应用内完成接听
    
        // 应用内接听后，向Call Service Kit上报通话状态
        voipCall.reportCallStateChange(callback.callId, voipCall.VoipCallState.VOIP_CALL_STATE_ACTIVE);
      }
    });
    ```
    
    如果用户在横幅通知上点击拒接，则应用在接收到VOIP\_CALL\_EVENT\_REJECT事件回调之后，在应用内完成拒接，然后向Call Service Kit 上报VOIP\_CALL\_STATE\_DISCONNECTED状态，系统会取消横幅通知。
    
    拒接之后，应用可跳过5、6步，直接看第7步。
    
    拒接的示例代码如下：
    
    ```
    voipCall.on('voipCallUiEvent', callback => {
      if (callback?.voipCallUiEvent == voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_REJECT) {
        // ...应用内完成拒接
    
        // 向Call Service Kit上报通话状态
        voipCall.reportCallStateChange(callback.callId, voipCall.VoipCallState.VOIP_CALL_STATE_DISCONNECTED);
      }
    });
    ```
    
5.  在通话过程中，用户可以根据需要，可以静音或解除静音。
    
    以静音为例，用户在横幅上点击静音，Call Service Kit会给应用回调VOIP\_CALL\_EVENT\_MUTED事件。
    
    应用在完成静音后，应向Call Service Kit上报AUDIO\_EVENT\_MUTED音频状态。
    
    示例代码如下：
    
    ```
    voipCall.on('voipCallUiEvent', callback => {
      if (callback?.voipCallUiEvent == voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_MUTED) {
        // 向Call Service Kit上报静音
        voipCall.reportCallAudioEventChange(callback.callId, voipCall.CallAudioEvent.AUDIO_EVENT_MUTED);
      } else if (callback?.voipCallUiEvent == voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_UNMUTED) {
        // 向Call Service Kit上报解除静音
        voipCall.reportCallAudioEventChange(callback.callId, voipCall.CallAudioEvent.AUDIO_EVENT_UNMUTED);
      }
    });
    ```
    
    静音、解除静音的横幅通知效果图展示如下：
    
    | 静音 | 解除静音 |
    | --- | --- |
    |  |  |
    
6.  用户在横幅点击挂断，Call Service Kit会给应用回调VOIP\_CALL\_EVENT\_HANGUP事件。
    
    应用收到该事件后，应在应用内完成挂断，然后，向Call Service Kit上报VOIP\_CALL\_STATE\_DISCONNECTED状态，系统会取消横幅通知。
    
    示例代码如下：
    
    ```
    voipCall.on('voipCallUiEvent', callback => {
      if (callback?.voipCallUiEvent == voipCall.VoipCallUiEvent.VOIP_CALL_EVENT_HANGUP) {
        // ...应用内完成挂断
    
        // 向Call Service Kit上报通话状态
        voipCall.reportCallStateChange(callback.callId, voipCall.VoipCallState.VOIP_CALL_STATE_DISCONNECTED);
      }
    });
    ```
    
7.  通话结束后，应用不再需要感知到用户在通话横幅上的操作，可以解除voipCallUiEvent事件。
    
    示例代码如下：
    
    ```
    // 解除voipCallUiEvent事件
    voipCall.off('voipCallUiEvent', callback => {
      hilog.info(0x0000, 'CallDemo', `Succeeded in unRegistering voipCallUiEvent, callId: ${callback.callId}`);
    });
    ```