Android Dialer源碼分析之通話IncallUI顯示

關於電話來電流程可以參看以下三篇博客:
Android 8.0來電流程分析(一)
Android 8.0來電流程分析(二)
Android 8.0來電流程分析(三)
講述了從RIL(Radio Interface Layer)層到InCallPresenter的流程。

到了InCallPresenter,就是Incall UI的部分


InCallPresenter

InCallPresenter implements CallList.Listener

在CallList.Listener裏都是從通話服務裏更新狀態的監聽回調。
其中

void onIncomingCall(DialerCall call);
void onCallListChange(CallList callList);

這兩個是來電和通話有改變時的接口方法。UI的更新顯示就是從這裏開始的。是的,剛進行通話的這時候Activity還沒構建呢。

InCallPresenter的構造使用了單例模式,初始化出現的地方是在setup方法裏,而初始化調用的地方是在InCallServiceImpl裏。

public void setUp(
      @NonNull Context context,
      CallList callList,
      ExternalCallList externalCallList,
      StatusBarNotifier statusBarNotifier,
      ExternalCallNotifier externalCallNotifier,
      ContactInfoCache contactInfoCache,
      ProximitySensor proximitySensor,
      FilteredNumberAsyncQueryHandler filteredNumberQueryHandler) {

...
addListener(mStatusBarNotifier);
...
addIncomingCallListener(mStatusBarNotifier);
...
addListener(mProximitySensor);
...
mCallList.addListener(this);
...
}

初始化的時候就是設置了一些監聽器。

當有來電的時候監聽回調onIncomingCall,當不是來電,是撥出去的時候監聽回調onCallListChange。這兩個方法裏都是可以調起IncallActivity的。
至於狀態改變的監聽,都是從底層監聽傳來的
下面分析 onIncomingCallonCallListChange

  /** Called when there is a new incoming call. */
  @Override
  public void onIncomingCall(DialerCall call) {
    android.util.Log.i(TAG, "onIncomingCall ");
    InCallState newState = startOrFinishUi(InCallState.INCOMING);
    InCallState oldState = mInCallState;
    mInCallState = newState;
    for (IncomingCallListener listener : mIncomingCallListeners) {
      listener.onIncomingCall(oldState, mInCallState, call);
    }
    if (mInCallActivity != null) {
      // Re-evaluate which fragment is being shown.
      mInCallActivity.onPrimaryCallStateChanged();
    }
  }

@Override
  public void onCallListChange(CallList callList) {
    android.util.Log.i(TAG, "onCallListChange ");
    
    if (mInCallActivity != null && mInCallActivity.isInCallScreenAnimating()) {
      mAwaitingCallListUpdate = true;
      return;
    }
    if (callList == null) {
      return;
    }

    mAwaitingCallListUpdate = false;

    InCallState newState = getPotentialStateFromCallList(callList);
    InCallState oldState = mInCallState;
    LogUtil.d(
        "InCallPresenter.onCallListChange",
        "onCallListChange oldState= " + oldState + " newState=" + newState);

    newState = startOrFinishUi(newState);
    LogUtil.d(
        "InCallPresenter.onCallListChange", "onCallListChange newState changed to " + newState);

    // Set the new state before announcing it to the world
    LogUtil.d(
        "InCallPresenter.onCallListChange",
        "Phone switching state: " + oldState + " -> " + newState);
    mInCallState = newState;

	...省略
  }

這兩個方法裏都執行了startOrFinishUi這個方法;
startOrFinishUi這個方法顧名思義,就是檢測是否需要顯示和關閉UI,

private InCallState startOrFinishUi(InCallState newState) {
    ...省略
    boolean showCallUi = InCallState.OUTGOING == newState && mainUiNotVisible;

    showCallUi |=
        (InCallState.PENDING_OUTGOING == mInCallState
            && InCallState.INCALL == newState
            && !isShowingInCallUi());

    showCallUi |= mainUiNotVisible && shouldLaunchMainUiForVideoCall(mInCallState, newState);

    showCallUi |=
        InCallState.PENDING_OUTGOING == newState
            && mainUiNotVisible
            && isCallWithNoValidAccounts(mCallList.getPendingOutgoingCall());

    showCallUi |=
        InCallState.WAITING_FOR_ACCOUNT == mInCallState
            && InCallState.INCALL == newState
            && mCallList.getActiveOrBackgroundCall() != null;

    if (showCallUi || showAccountPicker) {
      LogUtil.i("InCallPresenter.startOrFinishUi", "Start in call UI");
      showInCall(false /* showDialpad */, !showAccountPicker /* newOutgoingCall */);
    } else if (startIncomingCallSequence) {
      if (!startUi(newState)) {
        return mInCallState;
      }
      /// @}
    } else if (newState == InCallState.NO_CALLS) {
      // The new state is the no calls state.  Tear everything down.
      attemptFinishActivity();
      attemptCleanup();
    }
    return newState;
  }

這個方法內容有點多,主要就是通過各種屬性的判斷,最後判斷showCallUi這個值是不是True,如果是True那麼就去開啓UI

  public void showInCall(boolean showDialpad, boolean newOutgoingCall) {
    LogUtil.d("InCallPresenter.showInCall", "Showing InCallActivity");
    mContext.startActivity(
        InCallActivity.getIntent(
            mContext, showDialpad, newOutgoingCall, false /* forFullScreen */));
  }

showInCall就是去開啓InCallActivity.這樣InCallActivity就開始顯示了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章