天天看點

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

InCallScreen是什麼

本文來自http://blog.csdn.net/yihongyuelan 轉載請務必注明出處

本文以MTK平台Android 4.2為分析對象,MTK在原生的基礎之上添加了許多自己的東西,是以個别地方跟原生代碼以及QCOM的代碼不同,請讀者知悉。

在電話的呼出流程中,我們最後需要按下撥号鍵,才能将電話撥打出去,那麼在按下撥号鍵之後,我們可以看到會彈出一個界面,顯示撥号資訊以及一些其他資訊,這個界面就是我們的InCallScreen界面。當然,在來電的時候,彈出的界面依然是InCallScreen,在我們接通電話之後顯示的那個界面仍然是InCallScreen。也就是說在通話過程中,我們一直可見并操作的那個界面就是InCallScreen。

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結
Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結
Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 1 InCallScreen界面

圖1中所示就是InCallScreen不同情況下所展示的界面,分别是撥号、接通、來電三種情況下InCallScreen的現實情況。

InCallScreen結構分析

經過對比後可以發現,InCallScreen的撥号以及接通時,界面表現基本一緻,而來電界面主要由于多了一個滑動接聽的控件,進而導緻界面不太一樣。這裡我們隊InCallScreen的結構分析,采用接通之後的界面。如圖2:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 2 來電/去電接通後InCallScreen

InCallScreen布局分析

在InCallScreen.java中,我們可以在onCreate方法中找到InCallScreen加載的布局檔案,即incall_screen.xml。在incall_screen.java檔案中,我們可以看到有以下幾個控件:

  • call_card:顯示目前通話的資訊,比如來電号碼,通話時間,移動營運商等等;
  • incall_touch_ui:包括挂斷按鈕,顯示DTMF撥号盤按鈕,揚聲器,靜音,暫停,加入通話等幾個按鈕,就是通話界面下方的控制按鈕;
  • otaCallCardStub:CDMA模式下跟OTA相關的控件;
  • manageConferencePanelStub:多方通話管理界面;
  • vtInCallScreenStub:視屏通話控件;
  • dtmf_twelve_key_dialer_stub:這個控件為DTMF控件,也就是我們點選
    Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結
    按鈕之後,會彈出一個0~9以及*和#的撥号盤。

因為我們這裡主要分析一般模式下的通話界面,是以暫不涉及到視屏通話。

總的來講,在圖2顯示的界面中,我們直覺能夠看到的控件主要是:call_card以及incall_touch_ui這兩塊。當我們點選DTMF彈出按鈕之後,會顯示DTMF控件。當然在我們接入多方通話之後,就會看到多方通話的界面了。

call_card通話資訊展示

call_card控件,實際上顯示的資訊主要為通話聯系人的相關資訊,布局如下:

<?xml version="1.0" encoding="utf-8"?>
<com.android.phone.CallCard xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/call_info_container" 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include android:id="@+id/primary_call_info"
         layout="@layout/primary_call_info" />

    <RelativeLayout android:id="@+id/largeAreaForSharing"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone"/>
</com.android.phone.CallCard>
           

整個布局情況如下圖:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結
Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結
Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 3  CallCard界面 詳細資訊請參看primary_call_info.xml。

incall_touch_ui通話控制界面

這裡所謂的控制主要包括了在通話界面底部的一些操作按鈕,比如:挂斷、顯示/取消顯示DTMF撥号界面、開啟/關閉揚聲器、啟動/停止靜音、開啟/取消來電保持、添加多路通話等一些操作按鈕。當然這裡并沒有将所有的控制按鈕或者控制控件全部顯示出來,比如當來電時,我們需要滑動接聽電話,這裡的滑動控件即MultiWaveView也屬于incall_touch_ui.xml布局。布局如圖4:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結
Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 4 InCallTouchUi

InCallScreen初始化流程

通過檢視Phone.apk的AndroidManifest.xml檔案可以看到:

<activity android:name="InCallScreen"
            android:theme="@style/Theme.InCallScreen"  <!-- InCallScreen的Theme  -->
            android:label="@string/phoneIconLabel"
            android:excludeFromRecents="true"    <!-- 該Activity不會顯示在最近使用清單中 -->
            android:launchMode="singleInstance"   <!-- 該Activity為單例模式 -->
            android:screenOrientation="nosensor"   <!-- 該Activity不會橫豎屏切換,預設豎屏 -->
            android:configChanges="keyboardHidden"  <!-- 該Activity顯示時隐藏keyboard -->
            android:exported="false">                <!-- 該Activity不能被其它調用 -->
        </activity>
           

檢視@style/Theme.InCallScreen可以看到:

<style name="Theme.InCallScreen" parent="@android:style/Theme.Holo.NoActionBar">
        <item name="android:windowBackground">@android:color/black</item>
        <item name="*android:windowAnimationStyle">@style/InCallAnimationStyle</item>
    </style>
           

可以看到InCallScreen的Theme中沒有ActionBar,視窗背景為黑色,有過場動畫。

因為InCallScreen為單例模式,第一次啟動時調用onCreate而後面則會調用其onNewIntent方法。我們知道在onCreate方法中,一般都是對一些對象進行建立并初始化,以及設定布局檔案等等。在InCallScreen的onCreate方法中,完成了PhoneApp對象的擷取,以及Window參數的設定等等,在這些過程中我們需要關注以下三個方法:

  1. initInCallScreen:初始化CallCard以及InCallTouchUi等截面;
  2. registerForPhoneStates:注冊關于Phone狀态改變的監聽事件,這也就是為什麼Phone狀态改變之後InCallScreen能夠收到變化消息的原因,這一點我們在來電流程中也有提及;
  3. internalResolveIntent:該方法用于處理InCallScreen收到的Intent資訊;

同樣,在onNewIntent方法中主要調用了internalResolveIntent方法,是以下面我們着重分析以下幾個方法在InCallScreen初始化的過程中作用。

initInCallScreen

該方法在InCallScreen的onCreate方法中調用,僅執行一次,代碼如下:

private void initInCallScreen() {
        ... ...省略
        // Initialize CallTime 通話時間初始化
        mCallTime = new CallTime(this);
        // Initialize the CallCard. 通話資訊初始化
        mCallCard = (CallCard) findViewById(R.id.callCard);
        ... ...省略
        //第二路通話界面初始化
        mSecCallInfo = (ViewStub) findViewById(R.id.secondary_call_info);
        mCallCard.setInCallScreenInstance(this);
        //通話錄音按鈕初始化
        mVoiceRecorderIcon = (ImageView) findViewById(R.id.voiceRecorderIcon);
        mVoiceRecorderIcon.setBackgroundResource(R.drawable.voice_record);
        mVoiceRecorderIcon.setVisibility(View.INVISIBLE);
        // Initialize the onscreen UI elements. 通話控制布局初始化
        initInCallTouchUi();
        ... ...省略
        // The DTMF Dialpad. DTMF撥号盤初始化
        ViewStub stub = (ViewStub) findViewById(R.id.dtmf_twelve_key_dialer_stub);
        mDialer = new DTMFTwelveKeyDialer(this, stub);
        mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
        // Initialize VTInCallScreen 視屏電話初始化
        mVTInCallScreen = new VTInCallScreenProxy(this, mDialer);
    }
           

可以看到這裡全都是對InCallScreen上布局的一些初始化過程,我們繼續看到其中對于InCallTouchUi初始化的代碼:

private void initInCallTouchUi() {
        ... ...省略
        mInCallTouchUi = (InCallTouchUi) findViewById(R.id.inCallTouchUi);
        mInCallTouchUi.setInCallScreenInstance(this);
        // 挂斷并通話短信回複
        mRespondViaSmsManager = new RespondViaSmsManager();
        mRespondViaSmsManager.setInCallScreenInstance(this);
    }
           

通過代碼可以知道InCallScreen上的布局顯示幾乎都是在initInCallScreen方法中做的,如果我們修改了InCallScreen的布局那麼我們應該在這裡對修改後的布局進行初始化。

registerForPhoneStates

該方法在initInCallScreen方法之後,我們在來電流程中也有分析過該方法,代碼如下:

private void registerForPhoneStates() {
        if (!mRegisteredForPhoneStates) {
            if (FeatureOption.MTK_GEMINI_SUPPORT) {
                ... ...省略
                mCMGemini.registerForIncomingRingGemini(mHandler, PHONE_INCOMING_RING, null, PhoneConstants.GEMINI_SIM_1);
                mCMGemini.registerForIncomingRingGemini(mHandler, PHONE_INCOMING_RING2, null, PhoneConstants.GEMINI_SIM_2);
                ... ...省略
            } else {
                ... ...省略
                mCM.registerForIncomingRing(mHandler, PHONE_INCOMING_RING, null);
                ... ...省略
    }
           

這裡所用的mHandler就是InCallScreen的mHandler,這裡通過register****方法注冊監聽實際上為觀察者模式的運用。

internalResolveIntent

該方法是InCallScreen用于處理Intent的方法,代碼如下:

private void internalResolveIntent(Intent intent) {
        ... ...省略
        if (action.equals(intent.ACTION_MAIN)) {
            //是否顯示Dialpad
            if (intent.hasExtra(SHOW_DIALPAD_EXTRA)) {
                boolean showDialpad = intent.getBooleanExtra(SHOW_DIALPAD_EXTRA, false);
                if (VDBG) log("- internalResolveIntent: SHOW_DIALPAD_EXTRA: " + showDialpad);

                mApp.inCallUiState.showDialpad = showDialpad;
                final boolean hasActiveCall = mCM.hasActiveFgCall();
                final boolean hasHoldingCall = mCM.hasActiveBgCall();
                if (showDialpad && !hasActiveCall && hasHoldingCall) {
                    PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall());
                }
            }

            ... ...省略
            //強制開啟揚聲器
            if (FeatureOption.MTK_TB_APP_CALL_FORCE_SPEAKER_ON) {
              if (intent.hasExtra(EXTRA_FORCE_SPEAKER_ON)) {
                  boolean forceSpeakerOn = intent.getBooleanExtra(EXTRA_FORCE_SPEAKER_ON, false);
                  if (forceSpeakerOn)
                  {
                    Log.e("MTK_TB_APP_CALL_FORCE_SPEAKER_ON", "forceSpeakerOn is true");
                    if (!PhoneGlobals.getInstance().isHeadsetPlugged() 
                            && !(mApp.isBluetoothHeadsetAudioOn())) {
                      //Only force the speaker ON while not video call and speaker is not ON
                      if (!intent.getBooleanExtra(Constants.EXTRA_IS_VIDEO_CALL, false)
                          && !PhoneUtils.isSpeakerOn(mApp)) {
                        Log.e("MTK_TB_APP_CALL_FORCE_SPEAKER_ON", "PhoneUtils.turnOnSpeaker");
                        PhoneUtils.turnOnSpeaker(mApp, true, true, true);
                      }
                    }
                  }
              }
            }
            
            //視屏通話處理
            if (FeatureOption.MTK_VT3G324M_SUPPORT) {
                if (getInVoiceAnswerVideoCall()) {
                    setInVoiceAnswerVideoCall(false);
                }
                if (mCM.getState() == PhoneConstants.State.RINGING) {
                    if (DBG) {
                        log("call manager state is ringing");
                    }
                    // When VT call incoming, use voice call incoming call GUI
                    mVTInCallScreen.setVTVisible(false);
                    mVTInCallScreen.setVTScreenMode(Constants.VTScreenMode.VT_SCREEN_CLOSE);
                } else if (intent.getBooleanExtra(Constants.EXTRA_IS_VIDEO_CALL, false)) {
                    if (DBG) {
                        log("vt extra is true");
                    }
                    // When dialing VT call, inflate VTInCallScreen
                    mVTInCallScreen.initVTInCallScreen();
                    // When dialed a VT call, but dialed failed, needs not init state for dialing
                    if (CallStatusCode.SUCCESS == mApp.inCallUiState.getPendingCallStatusCode()) {
                        mVTInCallScreen.initDialingSuccessVTState();
                    }
                    mVTInCallScreen.initDialingVTState();
                    mVTInCallScreen.initCommonVTState();
                    if (PhoneConstants.State.IDLE != PhoneGlobals.getInstance().mCM.getState() &&
                                !VTCallUtils.isVideoCall(mCM.getActiveFgCall())) {
                        // When voice is connected and place a VT call, need close VT GUI
                        mVTInCallScreen.setVTScreenMode(Constants.VTScreenMode.VT_SCREEN_CLOSE);
                    } else {
                        mVTInCallScreen.setVTScreenMode(Constants.VTScreenMode.VT_SCREEN_OPEN);
                    }
                } else {
                    // set VT open or close according the active foreground call
                    if (mCM.getState() != PhoneConstants.State.IDLE && VTCallUtils.isVideoCall(mCM.getActiveFgCall())) {
                        if (DBG) {
                            log("receive ACTION_MAIN, but active foreground call is video call");
                        }
                        mVTInCallScreen.initVTInCallScreen();
                        mVTInCallScreen.initCommonVTState();
                        mVTInCallScreen.setVTScreenMode(Constants.VTScreenMode.VT_SCREEN_OPEN);
                    } else if (!intent.getBooleanExtra(Constants.EXTRA_IS_NOTIFICATION, false)) {
                        mVTInCallScreen.setVTScreenMode(Constants.VTScreenMode.VT_SCREEN_CLOSE);
                    }
                } 
                mVTInCallScreen.updateVTScreen(mVTInCallScreen.getVTScreenMode());
            }
            return;
        }
        //接聽電話時觸發
        if (action.equals(Intent.ACTION_ANSWER)) {
            internalAnswerCall();
            mApp.setRestoreMuteOnInCallResume(false);
            return;
        }
        //OTA相關處理
        if (action.equals(OtaUtils.ACTION_DISPLAY_ACTIVATION_SCREEN)) {
            if (!TelephonyCapabilities.supportsOtasp(mPhone)) {
                throw new IllegalStateException(
                    "Received ACTION_DISPLAY_ACTIVATION_SCREEN intent on non-OTASP-capable device: "
                    + intent);
            }

            setInCallScreenMode(InCallScreenMode.OTA_NORMAL);
            if ((mApp.cdmaOtaProvisionData != null)
                && (!mApp.cdmaOtaProvisionData.isOtaCallIntentProcessed)) {
                mApp.cdmaOtaProvisionData.isOtaCallIntentProcessed = true;
                mApp.cdmaOtaScreenState.otaScreenState =
                        CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION;
            }
            return;
        }

        // 異常和未定義intent處理
        if (action.equals(OtaUtils.ACTION_PERFORM_CDMA_PROVISIONING)) {
            throw new IllegalStateException(
                "Unexpected ACTION_PERFORM_CDMA_PROVISIONING received by InCallScreen: "
                + intent);
        } else if (action.equals(Intent.ACTION_CALL)
                   || action.equals(Intent.ACTION_CALL_EMERGENCY)) {
            throw new IllegalStateException("Unexpected CALL action received by InCallScreen: "
                                            + intent);
        } else if (action.equals(ACTION_UNDEFINED)) {
            Log.wtf(LOG_TAG, "internalResolveIntent: got launched with ACTION_UNDEFINED");
            return;
        } else {
            Log.wtf(LOG_TAG, "internalResolveIntent: unexpected intent action: " + action);
            return;
        }
    }
           

InCallScreen初始化小結

經過以上幾個關鍵步驟之後,InCallScreen的初始化就完成了,緊接着自然是進入onResume方法執行,這裡就不展開去介紹onResume方法了,其主要就是為顯示InCallScreen之前做一些準備工作和檢查工作。

InCallScreenUI控制流程

通過前面的分析,InCallScreen的UI控制主要包括以下6個方面:

  1. incomingCallWidget:接通/挂斷/短信回複時需要使用;
  2. dialpadButton:也就是顯示或隐藏撥号盤(DTMF);
  3. audioButton:開啟/關閉揚聲器;
  4. muteButton:開啟/關閉麥克風靜音,開啟之後對方無法聽到你的聲音;
  5. holdButton:開啟/關閉呼叫保持;
  6. addButton:增加多路通話;也就是在通話的過程中可以暫停目前通話,撥打另一路通話并接通;

當我們點選addButton之後,會彈出撥号盤提示添加一路通話,添加通話之後整個InCallScreen界面将會變成如圖5所示:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結
Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 5 添加一路通話界面以及添加後界面

在改變之後的界面中,我們可以看到多了兩個圖示:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

,前者表示在兩路通話之間進行切換,後者表示開啟視屏電話。

incomingCallWidget滑動控件

該滑動控件在來電的時候,會顯示在InCallScreen界面上,預設情況下使用者可以選擇接聽、挂斷、挂斷并短信回複三種模式,向右滑動為接聽,向左滑動為挂斷,向上滑動為挂斷并選擇快捷短信回複,如圖6:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 6 incomingCallWidget在來電時的顯示

該控件隻有在來電的時候才會顯示出來,使用者可以滑動中間的白色部分到圖5中的三個方向:左、上、右,分别觸發不同的效果。實際上該控件和解鎖界面上的滑動解鎖使用的是同一控件,使用滑動的方式觸發不同的效果,接下來我們就來看看其控制流程吧。

控制流程

1)布局檔案

Phone/res/layout/incall_touch_ui.xml

<com.android.internal.widget.multiwaveview.GlowPadView
        android:id="@+id/incomingCallWidget"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center|bottom"
        android:layout_marginTop="20dip"
        android:layout_marginBottom="-110dip"
        android:background="@android:color/black"
        android:visibility="gone"
        android:gravity="top"

        prvandroid:targetDrawables="@array/incoming_call_widget_3way_targets"
        prvandroid:targetDescriptions="@array/incoming_call_widget_3way_target_descriptions"
        prvandroid:directionDescriptions="@array/incoming_call_widget_3way_direction_descriptions"
        prvandroid:handleDrawable="@drawable/ic_in_call_touch_handle"
        prvandroid:innerRadius="@*android:dimen/glowpadview_inner_radius"
        prvandroid:outerRadius="@*android:dimen/glowpadview_target_placement_radius"
        prvandroid:outerRingDrawable="@*android:drawable/ic_lockscreen_outerring"
        prvandroid:snapMargin="@*android:dimen/glowpadview_snap_margin"
        prvandroid:vibrationDuration="20"
        prvandroid:feedbackCount="1"
        prvandroid:glowRadius="@*android:dimen/glowpadview_glow_radius"
        prvandroid:pointDrawable="@*android:drawable/ic_lockscreen_glowdot"
        />
           

(2)初始化檔案

Phone/src/com/android/phone/inCallTouchUi.java

(3)工作流程

首先初始化,代碼如下:

private GlowPadView mIncomingCallWidget;
mIncomingCallWidget = (GlowPadView) findViewById(R.id.incomingCallWidget);
mIncomingCallWidget.setOnTriggerListener(this);
           

通過初始化代碼我們可以看到其注冊了一個TriggerListener并且就在本類中就有其實作,那麼我繼續找到其TriggerListener的實作,代碼如下:

/**
     * Handles "Answer" and "Reject" actions for an incoming call.
     * We get this callback from the incoming call widget
     * when the user triggers an action.
     */
    @Override
    public void onTrigger(View view, int whichHandle) {
        ... ...省略
        mShowInCallControlsDuringHidingAnimation = false;
        switch (whichHandle) {
            //來電選擇接聽電話
            case ANSWER_CALL_ID:
                if (DBG) log("ANSWER_CALL_ID: answer!");
                cancelIncomingPingTime();
                mInCallScreen.handleOnscreenButtonClick(R.id.incomingCallAnswer);
                mShowInCallControlsDuringHidingAnimation = true;
                mLastIncomingCallActionTime = SystemClock.uptimeMillis();
                break;
            //來電選擇挂斷并通過短信回複
            case SEND_SMS_ID:
                if (DBG) log("SEND_SMS_ID!");
                mInCallScreen.handleOnscreenButtonClick(R.id.incomingCallRespondViaSms);
                break;
            //來電選擇拒接
            case DECLINE_CALL_ID:
                if (DBG) log("DECLINE_CALL_ID: reject!");
                mInCallScreen.handleOnscreenButtonClick(R.id.incomingCallReject);
                mLastIncomingCallActionTime = SystemClock.uptimeMillis();
                break;
            default:
                Log.wtf(LOG_TAG, "onDialTrigger: unexpected whichHandle value: " + whichHandle);
                break;
        }
        //隐藏滑動控件
        hideIncomingCallWidget();
        // Regardless of what action the user did, be sure to clear out
        // the hint text we were displaying while the user was dragging.
        mInCallScreen.updateIncomingCallWidgetHint(0, 0);
    }
           

最終根據使用者的不同選擇,跳轉到InCallScreen.java中的handleOnscreenButtonClick方法去執行具體代碼,如下:

public void handleOnscreenButtonClick(int id) {
        ... ...省略
        switch (id) {
             //來電正常接聽
            case R.id.incomingCallAnswer:
                internalAnswerCall();
                break;
            //來電拒接
            case R.id.incomingCallReject:
                ... ...省略
                hangupRingingCall();
                break;
            //來電拒接并短信回複
            case R.id.incomingCallRespondViaSms:
                internalRespondViaSms();
                break;
            ... ...省略
        //更新InCallTouchUi
        updateInCallTouchUi();
    }
           

(4)時序圖

時序圖以來電接聽為例,如圖7:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 7 來電接聽時序圖

dialpadButton顯示/隐藏撥号盤

這裡提到的dialpadButton實際上為InCallTouchUi布局上的:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

該控件的作用是點選之後顯示或隐藏撥号盤,當電話接通之後如果使用者點選該控件則會如圖8所示:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 8 DTMF撥号盤

當使用者再次點選該控件時,撥号盤隐藏。

控制流程

(1)布局檔案

Phone/res/layout/incall_touch_ui.xml

<ToggleButton android:id="@+id/dialpadButton"
             style="@style/InCallCompoundButton"
               android:background="@drawable/btn_dialpad"
               android:contentDescription="@string/onscreenShowDialpadText"/>
           

Phone/res/layout/incall_screen.xml

<ViewStub android:id="@+id/dtmf_twelve_key_dialer_stub"
              android:layout="@layout/dtmf_twelve_key_dialer_view"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:layout_marginBottom="@dimen/dialpad_vertical_margin_dtmf"/>
           

(2)初始化檔案

Phone/src/com/android/phone/inCallTouchUi.java

Phone/src/com/android/phone/inCallScreen.java

(3)工作流程

控件初始化代碼如下:

dialpadButton控件初始化:

private CompoundButton mDialpadButton;
mDialpadButton = (CompoundButton) mInCallControls.findViewById(R.id.dialpadButton);
mDialpadButton.setOnClickListener(this);
mDialpadButton.setOnLongClickListener(this);
           

這裡的LongClick實際上是,在我們長按該控件時會彈出一個toast顯示該控件的作用。

撥号盤控件初始化:

private DTMFTwelveKeyDialer mDialer;
ViewStub stub = (ViewStub) findViewById(R.id.dtmf_twelve_key_dialer_stub);
mDialer = new DTMFTwelveKeyDialer(this, stub);
           

我們先找到mDialpadButton的onClick實作方法,如下:

@Override
    public void onClick(View view) {
        int id = view.getId();
        ... ...省略
        switch (id) {
            ... ...省略
            case R.id.dialpadButton:
            ... ...省略
                mInCallScreen.handleOnscreenButtonClick(id);
                break;
            ... ...省略
        }
    }
           

這裡還是調用了InCallScreen中的handleOnscreenButtonClick方法,代碼如下:

public void handleOnscreenButtonClick(int id) {
        ... ...省略
        switch (id) {
            ... ...省略
            case R.id.dialpadButton:
                onOpenCloseDialpad();
                break;
            ... ...省略
           

這裡最終調用onOpenCloseDialpad方法去實作打開或者關閉撥号盤,這裡我們繼續檢視onOpenCloseDialpad方法,代碼如下:

public void onOpenCloseDialpad() {
        ... ...省略
        //判斷撥号盤是否已經打開,如果是則隐藏反之則顯示
        if (mDialer.isOpened()) {
            closeDialpadInternal(true);
        } else {
            openDialpadInternal(true);
        }
        mApp.updateProximitySensorMode(mCM.getState());
    }
    //顯示撥号盤
    private void openDialpadInternal(boolean animate) {
        mDialer.openDialer(animate);
        mApp.inCallUiState.showDialpad = true;
    }
    //以藏撥号盤
    private void closeDialpadInternal(boolean animate) {
        mDialer.closeDialer(animate);
        mApp.inCallUiState.showDialpad = false;
    }
           

這裡的撥号盤實際上是InCallScreen中一塊獨立的布局,也就是DTMFTwelveKeyDialer,當我們點選dialpadButton後,最終調用到openDialpadInternal或者closeDialpadInternal,通過mDialer去實作撥号盤的顯示或隐藏,如mDialer.openDialer代碼如下:

public void openDialer(boolean animate) {
        ... ...省略

        if (!isOpened()) {
            // 這裡animate=true
            if (animate) {
                AnimationUtils.Fade.show(mDialerView);
            ... ...省略
    }
           

檢視AnimationUtils.Fade.show方法如下:

public static void show(final View view) {
                ... ...省略
                view.setVisibility(View.VISIBLE);
                ... ...省略
        }
           

最終使得mDialerView顯示到界面上。

(4)時序圖

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 9 接通後顯示撥号盤時序圖

audioButton開啟/關閉揚聲器

揚聲器實際上就是手機的外放,在InCallTouchUi布局上顯示為:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

該控件的作用是點選之後開啟或關閉揚聲器,也就是使得對方的通話聲音能夠通過外放增大。如果我們此時連接配接了藍牙耳機,那麼顯示界面如圖10所示:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 10 接入藍牙耳機後InCallTouchUi上audioButton改變

我可以選擇三種不同的音頻輸出方式:Speaker即揚聲器,Handset earpiece手機聽筒,Bluetooth藍牙耳機。

控制流程

(1)布局檔案

Phone/res/layout/incall_touch_ui.xml

<ToggleButton android:id="@+id/audioButton"
             style="@style/InCallCompoundButton"
             android:background="@drawable/btn_compound_audio"
             android:contentDescription="@string/onscreenAudioText"/>
           

(2)初始化檔案

Phone/src/com/android/phone/inCallTouchUi.java

(3)工作流程

控件初始化代碼如下:

audioButton控件初始化:

private CompoundButton mAudioButton;
mAudioButton = (CompoundButton) mInCallControls.findViewById(R.id.audioButton);
mAudioButton.setOnClickListener(this);
mAudioButton.setOnLongClickListener(this);
           

這裡的LongClick實際上是,在我們長按該控件時會彈出一個toast顯示該控件的作用。找到audioButton的onClick實作方法,如下:

@Override
    public void onClick(View view) {
        int id = view.getId();
        ... ...省略
        switch (id) {
            ... ...省略
            case R.id.audioButton:
            ... ...省略
                handleAudioButtonClick();
                break;
            ... ...省略
        }
    }
           

這裡的handleAudioButtonClick方法對是否接入了藍牙耳機進行了判斷,如果是則會像圖9所示那樣,彈出三個選項按鈕。這裡我們假設沒有連接配接藍牙耳機,代碼如下:

private void handleAudioButtonClick() {
        ... ...省略
        //如果連接配接了藍牙耳機則執行if裡面的代碼
        if (inCallControlState.bluetoothEnabled) {
            ... ...省略
        } else {
            ... ...省略
            mInCallScreen.toggleSpeaker();
        }
    }
           

這裡我們繼續檢視InCallScreen中的toggleSpeaker方法:

public void toggleSpeaker() {
        ... ...省略
        PhoneUtils.turnOnSpeaker(this, newSpeakerState, true);
        ... ...省略
    }
           

這裡最終交由PhoneUtils中的turnOnSpeaker去最終負責打開揚聲器。

(4)時序圖

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 11 開啟/關閉揚聲器

muteButton開啟/關閉麥克風靜音

麥克風靜音的作用是在通話過程中,屏蔽自己這段的聲音輸入,在InCallTouchUi布局上顯示為:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

控制流程

(1)布局檔案

Phone/res/layout/incall_touch_ui.xml

<ToggleButton android:id="@+id/muteButton"
             style="@style/InCallCompoundButton"
             android:background="@drawable/btn_compound_mute"
             android:contentDescription="@string/onscreenMuteText"/>
           

(2)初始化檔案

Phone/src/com/android/phone/inCallTouchUi.java

(3)工作流程

控件初始化代碼如下:

muteButton控件初始化:

private CompoundButton mMuteButton;
mMuteButton = (CompoundButton) mInCallControls.findViewById(R.id.muteButton);
mMuteButton.setOnClickListener(this);
mMuteButton.setOnLongClickListener(this);
           

這裡的LongClick實際上是,在我們長按該控件時會彈出一個toast顯示該控件的作用。找到muteButton的onClick實作方法,如下:

@Override
    public void onClick(View view) {
        int id = view.getId();
        ... ...省略
        switch (id) {
            ... ...省略
            case R.id.muteButton:
            ... ...省略
                mInCallScreen.handleOnscreenButtonClick(id);
                break;
            ... ...省略
        }
    }
           

依然調用了InCallScreen中的handleOnscreenButtonClick方法,代碼如下:

public void handleOnscreenButtonClick(int id) {
        ... ...省略
        switch (id) {
            ... ...省略
            case R.id.muteButton:
                onMuteClick();
                break;
            ... ...省略
           

然後調用onMuteClick方法來實作開啟/關閉麥克風靜音功能,代碼如下:

public void onMuteClick() {
        ... ...省略
        PhoneUtils.setMute(newMuteState);
        ... ...省略
    }
           

最終的實作仍然在PhoneUtils中的setMute方法,該方法将繼續傳遞直到audioManager去執行該靜音操作。

(4)時序圖

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 12 麥克風靜音開啟/關閉

holdButton開啟/關閉呼叫保持

holdButton實際上就是呼叫保持的開關,使用者可以暫停目前通話,在InCallTouchUi布局上顯示為:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

。當使用者在通話過程中點選該圖示之後,對方将會聽到類似提示音“請稍等,現在是呼叫保持……”,該功能用于在兩路通話之間進行切換,但隻有一路通話時無法開啟呼叫保持功能。

控制流程

(1)布局檔案

Phone/res/layout/incall_touch_ui.xml

<ToggleButton android:id="@+id/holdButton"
             style="@style/InCallCompoundButton"
             android:background="@drawable/btn_compound_hold"
             android:contentDescription="@string/onscreenHoldText"/>
           

(2)初始化檔案

Phone/src/com/android/phone/inCallTouchUi.java

(3)工作流程

控件初始化代碼如下:

muteButton控件初始化:

private CompoundButton mHoldButton;
mHoldButton = (CompoundButton) mInCallControls.findViewById(R.id.holdButton);
mHoldButton.setOnClickListener(this);
mHoldButton.setOnLongClickListener(this);
           

這裡的LongClick實際上是,在我們長按該控件時會彈出一個toast顯示該控件的作用。找到holdButton的onClick實作方法,如下:

@Override
    public void onClick(View view) {
        int id = view.getId();
        ... ...省略
        switch (id) {
            ... ...省略
            case R.id.muteButton:
            ... ...省略
                mInCallScreen.handleOnscreenButtonClick(id);
                break;
            ... ...省略
        }
    }
           

依然調用了InCallScreen中的handleOnscreenButtonClick方法,代碼如下:

public void handleOnscreenButtonClick(int id) {
        ... ...省略
        switch (id) {
            ... ...省略
            case R.id.holdButton:
                onHoldClick();
                break;
            ... ...省略
           

然後調用onHoldClick方法來實作開啟/關閉呼叫保持功能,代碼如下:

private void onHoldClick() {
        ... ...省略
        if (hasActiveCall && !hasHoldingCall) {
            // 開啟呼叫保持
            PhoneUtils.switchHoldingAndActive(
                mCM.getFirstActiveBgCall());  // Really means "hold" in this state
            newHoldState = true;
            holdButtonEnabled = true;
        } else if (!hasActiveCall && hasHoldingCall && !haveMultipleHoldingCall) {
            // 取消呼叫保持
            PhoneUtils.switchHoldingAndActive(
                mCM.getFirstActiveBgCall());  // Really means "unhold" in this state
            newHoldState = false;
            holdButtonEnabled = true;
        } 
        ... ...省略
        // 強制關閉撥号盤
        closeDialpadInternal(true);  // do the "closing" animation
    }
           

繼續追蹤可以找到PhoneUtils中的switchHoldingAndActive方法,代碼如下:

static void switchHoldingAndActive(Call heldCall) {
        ... ...省略
        try {
            CallManager cm = PhoneGlobals.getInstance().mCM;
            ... ...省略
            cm.switchHoldingAndActive(heldCall);
            ... ...省略
    }
           

最終的實作會通過CallManager一層層的向下傳遞,并最終實作呼叫保持功能。

(4)時序圖

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 13 呼叫保持開啟/關閉

addButton添加一路通話

addButton的意思就是在目前通話的基礎上添加一路通話,目前通話将會切換到呼叫保持狀态。該圖示在InCallTouchUi布局上顯示為:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

當使用者在目前通話過程中點選該圖示之後,界面出現撥号盤,如果添加一路通話成功則會如下圖14所示:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結
Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 14 添加一路通話界面以及添加後界面

控制流程

(1)布局檔案

Phone/res/layout/incall_touch_ui.xml

<ImageButton android:id="@+id/addButton"
            style="@style/InCallButton"
            android:src="@drawable/ic_add_contact_holo_dark"
            android:contentDescription="@string/onscreenAddCallText"/>
           

(2)初始化檔案

Phone/src/com/android/phone/inCallTouchUi.java

(3)工作流程

控件初始化代碼如下:

addButton控件初始化:

private CompoundButton mAddButton;
mAddButton = (CompoundButton) mInCallControls.findViewById(R.id.addButton);
mAddButton.setOnClickListener(this);
mAddButton.setOnLongClickListener(this);
           

這裡的LongClick實際上是,在我們長按該控件時會彈出一個toast顯示該控件的作用。找到holdButton的onClick實作方法,如下:

@Override
    public void onClick(View view) {
        int id = view.getId();
        ... ...省略
        switch (id) {
            ... ...省略
            case R.id.addButton:
            ... ...省略
                mInCallScreen.handleOnscreenButtonClick(id);
                break;
            ... ...省略
        }
    }
           

依然調用了InCallScreen中的handleOnscreenButtonClick方法,代碼如下:

public void handleOnscreenButtonClick(int id) {
        ... ...省略
        switch (id) {
            ... ...省略
            case R.id.addButton:
                onAddCallClick();
                break;
            ... ...省略
           

然後調用onAddClick方法來實作增加一路通話的功能,代碼如下:

private void onAddCallClick() {
        PhoneUtils.startNewCall(mCM);
    }
           

繼續追蹤可以找到PhoneUtils中的startNewCall方法,代碼如下:

/* package */ static boolean startNewCall(final CallManager cm) {
        final PhoneGlobals app = PhoneGlobals.getInstance();
        ... ...省略
        // 将目前的通話靜音
        if (cm.hasActiveFgCall()) {
            setMuteInternal(cm.getActiveFgCall().getPhone(), true);
            app.setRestoreMuteOnInCallResume(true);
        }
        Intent intent = new Intent(Intent.ACTION_DIAL);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.putExtra(ADD_CALL_MODE_KEY, true);
        try {
            //跳轉到撥号界面
            app.startActivity(intent);
        } catch (ActivityNotFoundException e) {
            ... ...省略
        }
        return true;
    }
           

需要注意的是,當我們點選addButton後,目前通話會開啟靜音功能,在我們撥打新一路通話時,先前的通話将會自動切換到呼叫保持狀态。這裡的Intent.ACTION_DIAL查找對應的具體字元串為“android.intent.action.DIAL”,查找對應的Activity找到Contacts中的NoPhoneActivity,該Activity用于加載撥号盤。

(4)時序圖

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 15 新增一路通話時序圖

endButton挂斷目前通話

endButton就是挂斷電話的那個按鈕,點選之後将挂斷目前通話。該圖示在InCallTouchUi布局上顯示為:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

控制流程

(1)布局檔案

Phone/res/layout/incall_touch_ui.xml

<ImageButton android:id="@+id/endButton"
            style="@style/InCallEndButton"
            android:layout_width="match_parent"
            android:layout_weight="1"
            android:src="@drawable/ic_end_call"
            android:background="@drawable/end_call_background"
            android:contentDescription="@string/onscreenEndCallText" />
           

(2)初始化檔案

Phone/src/com/android/phone/inCallTouchUi.java

(3)工作流程

控件初始化代碼如下:

endButton控件初始化:

private CompoundButton mEndButton;
mEndButton = (CompoundButton) mInCallControls.findViewById(R.id.addButton);
mEndButton.setOnClickListener(this);
           

這裡隻注冊了onClick的監聽事件,沒有LongOnClick的監聽事件。找到endButton的onClick實作方法,如下:

@Override
    public void onClick(View view) {
        int id = view.getId();
        ... ...省略
        switch (id) {
            ... ...省略
            case R.id.endButton:
            ... ...省略
                mInCallScreen.handleOnscreenButtonClick(id);
                break;
            ... ...省略
        }
    }
           

依然調用了InCallScreen中的handleOnscreenButtonClick方法,代碼如下:

public void handleOnscreenButtonClick(int id) {
        ... ...省略
        switch (id) {
            ... ...省略
            case R.id.endButton:
                internalHangup();
                break;
            ... ...省略
           

然後調用internalHangup方法來實作挂斷目前通話,代碼如下:

private void internalHangup() {
        ... ...省略
        PhoneUtils.hangup(mCM);
        ... ...省略
        }
    }
           

繼續追蹤可以找到PhoneUtils中的hangup方法,代碼如下:

public static boolean hangup(CallManager cm) {
        ... ...省略
            ringing = cm.getFirstActiveRingingCall();
            fg = cm.getActiveFgCall();
            bg = cm.getFirstActiveBgCall();
        ... ...省略
        //因為選擇的是挂斷目前通話,是以fg.isIdle()為false
        } else if (!fg.isIdle() || fg.state == Call.State.DISCONNECTING) {
            if (DBG) log("hangup(): hanging up foreground call");
            hungup = hangup(fg);
        ... ...省略
    }
           

這裡我們需要注意,fg=cm.getActiveFgCall傳回類型是Call類型的,這裡的hangup(fg)實際處理代碼為:

static boolean hangup(Call call) {
        ... ...省略
        //挂斷目前通話
        call.hangup();
        ... ...省略
    }
           

到了這裡需要注意下,這裡的call對象是什麼呢?因為我們這裡使用的是GSM卡(WCDMA卡也一樣),是以這裡我們實際上得到的call對象是GsmCall.java的對象,進而直接找到GsmCall中的hangup方法,代碼如下:

public void
    hangup() throws CallStateException {
        owner.hangup(this);
    }
           

這裡最終會調用到GsmCallTracker中的hangup(GsmCall)方法中去,這裡就不詳解了。

(4)時序圖

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 16 挂斷目前通話時序圖

InCallScreen CallCard通話資訊顯示

CallCard說起來感覺很陌生,實際上在我們通話過程中,除了InCallTouchUi之外,InCallScreen所展示的界面就是CallCard了,如圖17:

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結
Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結
Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖 17 CallCard通話資訊

如圖17所顯示,CallCard包含了通話時間、通話背景、通話對象号碼(10086)、來電/去電狀态、号碼歸屬地、SIM卡營運商類型。

CallCard布局檔案

通話資訊界面實際上也在incall_screen.xml的布局中,但實際引用的布局為call_card.xml,整個call_card的布局較為複雜,因為通話可以是一路通話但也可以是兩路通話,可以是來電也可以是去電,是以CallCard的布局較為繁雜,通過Hierarchy Viewer可以檢視到。

CallCard界面更新

因為CallCard界面主要用于告知使用者目前的通話狀态,主要反映的是一些狀态資訊,是以我們主要檢視其更新的代碼,在InCallScreen的onResume中,我們可以看到有關于同步Phone狀态的代碼如下:

SyncWithPhoneStateStatus status = syncWithPhoneState();
           

而這裡的syncWithPhoneState方法就是用于同步Phone的狀态,代碼如下:

private SyncWithPhoneStateStatus syncWithPhoneState() {
        ... ...省略
        if (mCM.hasActiveFgCall() || mCM.hasActiveBgCall() || mCM.hasActiveRingingCall()
                || hasPendingMmiCodes || hasPendingMmiCodes2 || showProgressIndication || showScreenEvenAfterDisconnect) {
            if (VDBG) log("syncWithPhoneState: it's ok to be here; update the screen...");
            updateScreen();
            return SyncWithPhoneStateStatus.SUCCESS;
        }
        ... ...省略
    }
           

這裡會執行到updateScreen方法中,繼續檢視代碼:

private void updateScreen() {
        ... ...省略
        mCallCard.updateState(mCM);
        ... ...省略
    }
           

因為這裡我們主要關注CallCard的更新,其它内容就省略掉了。這裡調用了CallCard的對象mCallCard,并調用其中的updataState方法。如下:

/* package */ void updateState(CallManager cm) {
        ... ...省略
            //更新來電資訊
            updateRingingCall(cm);
        ... ...省略
            //更新目前通話資訊
            updateForegroundCall(cm);
        ... ...省略
            //更新已斷開連接配接的通話資訊
            updateAlreadyDisconnected(cm);
        ... ...省略
            //更新沒有通話的界面資訊
            updateNoCall(cm);
        ... ...省略
}
           

在updateState中會根據不同的條件選擇更新不同的界面,進而在InCallScreen中展示不同的結果。有以下四個方法用于更新不同狀态下的CallCard資訊:

  • updateRingingCall:顯示/更新來電界面資訊;
  • updateForegroundCall:顯示/更新目前通話界面資訊;
  • updateAlreadyDisconnected:更新/顯示已斷開連接配接的通話界面資訊,該界面隻是一瞬間狀态;
  • updateNoCall:更新/顯示沒有通話時的通話界面資訊;該界面一般不會出現,隻為以防萬一;

CallCard更新時序圖

Android 4.2 InCallScreen UI淺析InCallScreen是什麼InCallScreen結構分析InCallScreen初始化流程InCallScreenUI控制流程 InCallScreen CallCard通話資訊顯示 小結

圖18 CallCard更新時序圖

小結

在MTK的Android 4.2平台上,InCallScreen相對于原生的界面改動不算大,但其也增加了一些屬于自己的東西,如:視屏通話界面,雙卡控制界面,來電歸屬地等等,這些功能的添加使得Android手機在使用上更加便捷,也增加了使用者體驗。

在弄清楚了這界面UI的控制流程之後,對于修改InCallScreen界面有很大幫助,便于後續對InCallScreen進行個性化定制。

本文旨在分析InCallScreen上的UI控制流程,基于MTK Android 4.2的源碼分析,其中不乏缺漏之處還懇請各位看官見諒。