InCallScreen是什麼

InCallScreen是什麼


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

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

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

圖 1 InCallScreen界面

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

InCallScreen結構分析

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


圖 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控件,也就是我們點擊按鈕之後,會彈出一個0~9以及*和#的撥號盤。

因爲我們這裏主要分析一般模式下的通話界面,因此暫不涉及到視屏通話。

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

call_card通話信息展示

call_card控件,實際上顯示的信息主要爲通話聯繫人的相關信息,佈局如下:
[html] view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <com.android.phone.CallCard xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:id="@+id/call_info_container"   
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:orientation="vertical">  
  7.   
  8.     <include android:id="@+id/primary_call_info"  
  9.          layout="@layout/primary_call_info" />  
  10.   
  11.     <RelativeLayout android:id="@+id/largeAreaForSharing"  
  12.         android:layout_width="match_parent"  
  13.         android:layout_height="match_parent"  
  14.         android:visibility="gone"/>  
  15. </com.android.phone.CallCard>  

整個佈局情況如下圖:

圖 3  CallCard界面
詳細信息請參看primary_call_info.xml。

incall_touch_ui通話控制界面

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

圖 4 InCallTouchUi

InCallScreen初始化流程

通過查看Phone.apk的AndroidManifest.xml文件可以看到:

[html] view plaincopy
  1. <activity android:name="InCallScreen"  
  2.     android:theme="@style/Theme.InCallScreen"  <!-- InCallScreen的Theme  -->  
  3.     android:label="@string/phoneIconLabel"  
  4.     android:excludeFromRecents="true"    <!-- 該Activity不會顯示在最近使用列表中 -->  
  5.     android:launchMode="singleInstance"   <!-- 該Activity爲單例模式 -->  
  6.     android:screenOrientation="nosensor"   <!-- 該Activity不會橫豎屏切換,默認豎屏 -->  
  7.     android:configChanges="keyboardHidden"  <!-- 該Activity顯示時隱藏keyboard -->  
  8.     android:exported="false">                <!-- 該Activity不能被其它調用 -->  
  9. </activity>  

查看@style/Theme.InCallScreen可以看到:

[html] view plaincopy
  1. <style name="Theme.InCallScreen" parent="@android:style/Theme.Holo.NoActionBar">  
  2.     <item name="android:windowBackground">@android:color/black</item>  
  3.     <item name="*android:windowAnimationStyle">@style/InCallAnimationStyle</item>  
  4. </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方法中調用,僅執行一次,代碼如下:
[java] view plaincopy
  1. private void initInCallScreen() {  
  2.     ... ...省略  
  3.     // Initialize CallTime 通話時間初始化  
  4.     mCallTime = new CallTime(this);  
  5.     // Initialize the CallCard. 通話信息初始化  
  6.     mCallCard = (CallCard) findViewById(R.id.callCard);  
  7.     ... ...省略  
  8.     //第二路通話界面初始化  
  9.     mSecCallInfo = (ViewStub) findViewById(R.id.secondary_call_info);  
  10.     mCallCard.setInCallScreenInstance(this);  
  11.     //通話錄音按鈕初始化  
  12.     mVoiceRecorderIcon = (ImageView) findViewById(R.id.voiceRecorderIcon);  
  13.     mVoiceRecorderIcon.setBackgroundResource(R.drawable.voice_record);  
  14.     mVoiceRecorderIcon.setVisibility(View.INVISIBLE);  
  15.     // Initialize the onscreen UI elements. 通話控制佈局初始化  
  16.     initInCallTouchUi();  
  17.     ... ...省略  
  18.     // The DTMF Dialpad. DTMF撥號盤初始化  
  19.     ViewStub stub = (ViewStub) findViewById(R.id.dtmf_twelve_key_dialer_stub);  
  20.     mDialer = new DTMFTwelveKeyDialer(this, stub);  
  21.     mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);  
  22.     // Initialize VTInCallScreen 視屏電話初始化  
  23.     mVTInCallScreen = new VTInCallScreenProxy(this, mDialer);  
  24. }  

可以看到這裏全都是對InCallScreen上佈局的一些初始化過程,我們繼續看到其中對於InCallTouchUi初始化的代碼:
[java] view plaincopy
  1. private void initInCallTouchUi() {  
  2.     ... ...省略  
  3.     mInCallTouchUi = (InCallTouchUi) findViewById(R.id.inCallTouchUi);  
  4.     mInCallTouchUi.setInCallScreenInstance(this);  
  5.     // 掛斷並通話短信回覆  
  6.     mRespondViaSmsManager = new RespondViaSmsManager();  
  7.     mRespondViaSmsManager.setInCallScreenInstance(this);  
  8. }  

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

registerForPhoneStates

該方法在initInCallScreen方法之後,我們在來電流程中也有分析過該方法,代碼如下:
[java] view plaincopy
  1. private void registerForPhoneStates() {  
  2.     if (!mRegisteredForPhoneStates) {  
  3.         if (FeatureOption.MTK_GEMINI_SUPPORT) {  
  4.             ... ...省略  
  5.             mCMGemini.registerForIncomingRingGemini(mHandler, PHONE_INCOMING_RING, null, PhoneConstants.GEMINI_SIM_1);  
  6.             mCMGemini.registerForIncomingRingGemini(mHandler, PHONE_INCOMING_RING2, null, PhoneConstants.GEMINI_SIM_2);  
  7.             ... ...省略  
  8.         } else {  
  9.             ... ...省略  
  10.             mCM.registerForIncomingRing(mHandler, PHONE_INCOMING_RING, null);  
  11.             ... ...省略  
  12. }  

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

internalResolveIntent

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

[java] view plaincopy
  1. private void internalResolveIntent(Intent intent) {  
  2.     ... ...省略  
  3.     if (action.equals(intent.ACTION_MAIN)) {  
  4.         //是否顯示Dialpad  
  5.         if (intent.hasExtra(SHOW_DIALPAD_EXTRA)) {  
  6.             boolean showDialpad = intent.getBooleanExtra(SHOW_DIALPAD_EXTRA, false);  
  7.             if (VDBG) log("- internalResolveIntent: SHOW_DIALPAD_EXTRA: " + showDialpad);  
  8.   
  9.             mApp.inCallUiState.showDialpad = showDialpad;  
  10.             final boolean hasActiveCall = mCM.hasActiveFgCall();  
  11.             final boolean hasHoldingCall = mCM.hasActiveBgCall();  
  12.             if (showDialpad && !hasActiveCall && hasHoldingCall) {  
  13.                 PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall());  
  14.             }  
  15.         }  
  16.   
  17.         ... ...省略  
  18.         //強制開啓揚聲器  
  19.         if (FeatureOption.MTK_TB_APP_CALL_FORCE_SPEAKER_ON) {  
  20.           if (intent.hasExtra(EXTRA_FORCE_SPEAKER_ON)) {  
  21.               boolean forceSpeakerOn = intent.getBooleanExtra(EXTRA_FORCE_SPEAKER_ON, false);  
  22.               if (forceSpeakerOn)  
  23.               {  
  24.                 Log.e("MTK_TB_APP_CALL_FORCE_SPEAKER_ON""forceSpeakerOn is true");  
  25.                 if (!PhoneGlobals.getInstance().isHeadsetPlugged()   
  26.                         && !(mApp.isBluetoothHeadsetAudioOn())) {  
  27.                   //Only force the speaker ON while not video call and speaker is not ON  
  28.                   if (!intent.getBooleanExtra(Constants.EXTRA_IS_VIDEO_CALL, false)  
  29.                       && !PhoneUtils.isSpeakerOn(mApp)) {  
  30.                     Log.e("MTK_TB_APP_CALL_FORCE_SPEAKER_ON""PhoneUtils.turnOnSpeaker");  
  31.                     PhoneUtils.turnOnSpeaker(mApp, truetruetrue);  
  32.                   }  
  33.                 }  
  34.               }  
  35.           }  
  36.         }  
  37.           
  38.         //視屏通話處理  
  39.         if (FeatureOption.MTK_VT3G324M_SUPPORT) {  
  40.             if (getInVoiceAnswerVideoCall()) {  
  41.                 setInVoiceAnswerVideoCall(false);  
  42.             }  
  43.             if (mCM.getState() == PhoneConstants.State.RINGING) {  
  44.                 if (DBG) {  
  45.                     log("call manager state is ringing");  
  46.                 }  
  47.                 // When VT call incoming, use voice call incoming call GUI  
  48.                 mVTInCallScreen.setVTVisible(false);  
  49.                 mVTInCallScreen.setVTScreenMode(Constants.VTScreenMode.VT_SCREEN_CLOSE);  
  50.             } else if (intent.getBooleanExtra(Constants.EXTRA_IS_VIDEO_CALL, false)) {  
  51.                 if (DBG) {  
  52.                     log("vt extra is true");  
  53.                 }  
  54.                 // When dialing VT call, inflate VTInCallScreen  
  55.                 mVTInCallScreen.initVTInCallScreen();  
  56.                 // When dialed a VT call, but dialed failed, needs not init state for dialing  
  57.                 if (CallStatusCode.SUCCESS == mApp.inCallUiState.getPendingCallStatusCode()) {  
  58.                     mVTInCallScreen.initDialingSuccessVTState();  
  59.                 }  
  60.                 mVTInCallScreen.initDialingVTState();  
  61.                 mVTInCallScreen.initCommonVTState();  
  62.                 if (PhoneConstants.State.IDLE != PhoneGlobals.getInstance().mCM.getState() &&  
  63.                             !VTCallUtils.isVideoCall(mCM.getActiveFgCall())) {  
  64.                     // When voice is connected and place a VT call, need close VT GUI  
  65.                     mVTInCallScreen.setVTScreenMode(Constants.VTScreenMode.VT_SCREEN_CLOSE);  
  66.                 } else {  
  67.                     mVTInCallScreen.setVTScreenMode(Constants.VTScreenMode.VT_SCREEN_OPEN);  
  68.                 }  
  69.             } else {  
  70.                 // set VT open or close according the active foreground call  
  71.                 if (mCM.getState() != PhoneConstants.State.IDLE && VTCallUtils.isVideoCall(mCM.getActiveFgCall())) {  
  72.                     if (DBG) {  
  73.                         log("receive ACTION_MAIN, but active foreground call is video call");  
  74.                     }  
  75.                     mVTInCallScreen.initVTInCallScreen();  
  76.                     mVTInCallScreen.initCommonVTState();  
  77.                     mVTInCallScreen.setVTScreenMode(Constants.VTScreenMode.VT_SCREEN_OPEN);  
  78.                 } else if (!intent.getBooleanExtra(Constants.EXTRA_IS_NOTIFICATION, false)) {  
  79.                     mVTInCallScreen.setVTScreenMode(Constants.VTScreenMode.VT_SCREEN_CLOSE);  
  80.                 }  
  81.             }   
  82.             mVTInCallScreen.updateVTScreen(mVTInCallScreen.getVTScreenMode());  
  83.         }  
  84.         return;  
  85.     }  
  86.     //接聽電話時觸發  
  87.     if (action.equals(Intent.ACTION_ANSWER)) {  
  88.         internalAnswerCall();  
  89.         mApp.setRestoreMuteOnInCallResume(false);  
  90.         return;  
  91.     }  
  92.     //OTA相關處理  
  93.     if (action.equals(OtaUtils.ACTION_DISPLAY_ACTIVATION_SCREEN)) {  
  94.         if (!TelephonyCapabilities.supportsOtasp(mPhone)) {  
  95.             throw new IllegalStateException(  
  96.                 "Received ACTION_DISPLAY_ACTIVATION_SCREEN intent on non-OTASP-capable device: "  
  97.                 + intent);  
  98.         }  
  99.   
  100.         setInCallScreenMode(InCallScreenMode.OTA_NORMAL);  
  101.         if ((mApp.cdmaOtaProvisionData != null)  
  102.             && (!mApp.cdmaOtaProvisionData.isOtaCallIntentProcessed)) {  
  103.             mApp.cdmaOtaProvisionData.isOtaCallIntentProcessed = true;  
  104.             mApp.cdmaOtaScreenState.otaScreenState =  
  105.                     CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION;  
  106.         }  
  107.         return;  
  108.     }  
  109.   
  110.     // 異常和未定義intent處理  
  111.     if (action.equals(OtaUtils.ACTION_PERFORM_CDMA_PROVISIONING)) {  
  112.         throw new IllegalStateException(  
  113.             "Unexpected ACTION_PERFORM_CDMA_PROVISIONING received by InCallScreen: "  
  114.             + intent);  
  115.     } else if (action.equals(Intent.ACTION_CALL)  
  116.                || action.equals(Intent.ACTION_CALL_EMERGENCY)) {  
  117.         throw new IllegalStateException("Unexpected CALL action received by InCallScreen: "  
  118.                                         + intent);  
  119.     } else if (action.equals(ACTION_UNDEFINED)) {  
  120.         Log.wtf(LOG_TAG, "internalResolveIntent: got launched with ACTION_UNDEFINED");  
  121.         return;  
  122.     } else {  
  123.         Log.wtf(LOG_TAG, "internalResolveIntent: unexpected intent action: " + action);  
  124.         return;  
  125.     }  
  126. }  


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所示:

圖 5 添加一路通話界面以及添加後界面
在改變之後的界面中,我們可以看到多了兩個圖標:,前者表示在兩路通話之間進行切換,後者表示開啓視屏電話。

incomingCallWidget滑動控件

該滑動控件在來電的時候,會顯示在InCallScreen界面上,默認情況下用戶可以選擇接聽、掛斷、掛斷並短信回覆三種模式,向右滑動爲接聽,向左滑動爲掛斷,向上滑動爲掛斷並選擇快捷短信回覆,如圖6:


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

控制流程

1)佈局文件

Phone/res/layout/incall_touch_ui.xml

[html] view plaincopy
  1. <com.android.internal.widget.multiwaveview.GlowPadView  
  2.      android:id="@+id/incomingCallWidget"  
  3.      android:layout_width="match_parent"  
  4.      android:layout_height="wrap_content"  
  5.      android:layout_gravity="center|bottom"  
  6.      android:layout_marginTop="20dip"  
  7.      android:layout_marginBottom="-110dip"  
  8.      android:background="@android:color/black"  
  9.      android:visibility="gone"  
  10.      android:gravity="top"  
  11.   
  12.      prvandroid:targetDrawables="@array/incoming_call_widget_3way_targets"  
  13.      prvandroid:targetDescriptions="@array/incoming_call_widget_3way_target_descriptions"  
  14.      prvandroid:directionDescriptions="@array/incoming_call_widget_3way_direction_descriptions"  
  15.      prvandroid:handleDrawable="@drawable/ic_in_call_touch_handle"  
  16.      prvandroid:innerRadius="@*android:dimen/glowpadview_inner_radius"  
  17.      prvandroid:outerRadius="@*android:dimen/glowpadview_target_placement_radius"  
  18.      prvandroid:outerRingDrawable="@*android:drawable/ic_lockscreen_outerring"  
  19.      prvandroid:snapMargin="@*android:dimen/glowpadview_snap_margin"  
  20.      prvandroid:vibrationDuration="20"  
  21.      prvandroid:feedbackCount="1"  
  22.      prvandroid:glowRadius="@*android:dimen/glowpadview_glow_radius"  
  23.      prvandroid:pointDrawable="@*android:drawable/ic_lockscreen_glowdot"  
  24.      />  

(2)初始化文件

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

(3)工作流程

首先初始化,代碼如下:

[java] view plaincopy
  1. private GlowPadView mIncomingCallWidget;  
  2. mIncomingCallWidget = (GlowPadView) findViewById(R.id.incomingCallWidget);  
  3. mIncomingCallWidget.setOnTriggerListener(this);  

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

[java] view plaincopy
  1. /** 
  2.  * Handles "Answer" and "Reject" actions for an incoming call. 
  3.  * We get this callback from the incoming call widget 
  4.  * when the user triggers an action. 
  5.  */  
  6. @Override  
  7. public void onTrigger(View view, int whichHandle) {  
  8.     ... ...省略  
  9.     mShowInCallControlsDuringHidingAnimation = false;  
  10.     switch (whichHandle) {  
  11.         //來電選擇接聽電話  
  12.         case ANSWER_CALL_ID:  
  13.             if (DBG) log("ANSWER_CALL_ID: answer!");  
  14.             cancelIncomingPingTime();  
  15.             mInCallScreen.handleOnscreenButtonClick(R.id.incomingCallAnswer);  
  16.             mShowInCallControlsDuringHidingAnimation = true;  
  17.             mLastIncomingCallActionTime = SystemClock.uptimeMillis();  
  18.             break;  
  19.         //來電選擇掛斷並通過短信回覆  
  20.         case SEND_SMS_ID:  
  21.             if (DBG) log("SEND_SMS_ID!");  
  22.             mInCallScreen.handleOnscreenButtonClick(R.id.incomingCallRespondViaSms);  
  23.             break;  
  24.         //來電選擇拒接  
  25.         case DECLINE_CALL_ID:  
  26.             if (DBG) log("DECLINE_CALL_ID: reject!");  
  27.             mInCallScreen.handleOnscreenButtonClick(R.id.incomingCallReject);  
  28.             mLastIncomingCallActionTime = SystemClock.uptimeMillis();  
  29.             break;  
  30.         default:  
  31.             Log.wtf(LOG_TAG, "onDialTrigger: unexpected whichHandle value: " + whichHandle);  
  32.             break;  
  33.     }  
  34.     //隱藏滑動控件  
  35.     hideIncomingCallWidget();  
  36.     // Regardless of what action the user did, be sure to clear out  
  37.     // the hint text we were displaying while the user was dragging.  
  38.     mInCallScreen.updateIncomingCallWidgetHint(00);  
  39. }  

最終根據用戶的不同選擇,跳轉到InCallScreen.java中的handleOnscreenButtonClick方法去執行具體代碼,如下:
[java] view plaincopy
  1. public void handleOnscreenButtonClick(int id) {  
  2.    ... ...省略  
  3.    switch (id) {  
  4.         //來電正常接聽  
  5.        case R.id.incomingCallAnswer:  
  6.            internalAnswerCall();  
  7.            break;  
  8.        //來電拒接  
  9.        case R.id.incomingCallReject:  
  10.            ... ...省略  
  11.            hangupRingingCall();  
  12.            break;  
  13.        //來電拒接並短信回覆  
  14.        case R.id.incomingCallRespondViaSms:  
  15.            internalRespondViaSms();  
  16.            break;  
  17.        ... ...省略  
  18.    //更新InCallTouchUi  
  19.    updateInCallTouchUi();  

(4)時序圖

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

圖 7 來電接聽時序圖

dialpadButton顯示/隱藏撥號盤

這裏提到的dialpadButton實際上爲InCallTouchUi佈局上的:

該控件的作用是點擊之後顯示或隱藏撥號盤,當電話接通之後如果用戶點擊該控件則會如圖8所示:


圖 8 DTMF撥號盤

當用戶再次點擊該控件時,撥號盤隱藏。

控制流程

(1)佈局文件

Phone/res/layout/incall_touch_ui.xml
[html] view plaincopy
  1. <ToggleButton android:id="@+id/dialpadButton"  
  2.              style="@style/InCallCompoundButton"  
  3.                android:background="@drawable/btn_dialpad"  
  4.                android:contentDescription="@string/onscreenShowDialpadText"/>  

Phone/res/layout/incall_screen.xml

[html] view plaincopy
  1. <ViewStub android:id="@+id/dtmf_twelve_key_dialer_stub"  
  2.           android:layout="@layout/dtmf_twelve_key_dialer_view"  
  3.           android:layout_width="match_parent"  
  4.           android:layout_height="match_parent"  
  5.           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控件初始化:

[java] view plaincopy
  1. private CompoundButton mDialpadButton;  
  2. mDialpadButton = (CompoundButton) mInCallControls.findViewById(R.id.dialpadButton);  
  3. mDialpadButton.setOnClickListener(this);  
  4. mDialpadButton.setOnLongClickListener(this);  

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

撥號盤控件初始化:

[java] view plaincopy
  1. private DTMFTwelveKeyDialer mDialer;  
  2. ViewStub stub = (ViewStub) findViewById(R.id.dtmf_twelve_key_dialer_stub);  
  3. mDialer = new DTMFTwelveKeyDialer(this, stub);  

我們先找到mDialpadButton的onClick實現方法,如下:
[java] view plaincopy
  1. @Override  
  2. public void onClick(View view) {  
  3.     int id = view.getId();  
  4.     ... ...省略  
  5.     switch (id) {  
  6.         ... ...省略  
  7.         case R.id.dialpadButton:  
  8.         ... ...省略  
  9.             mInCallScreen.handleOnscreenButtonClick(id);  
  10.             break;  
  11.         ... ...省略  
  12.     }  
  13. }  

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

[java] view plaincopy
  1. public void handleOnscreenButtonClick(int id) {  
  2.    ... ...省略  
  3.    switch (id) {  
  4.        ... ...省略  
  5.        case R.id.dialpadButton:  
  6.            onOpenCloseDialpad();  
  7.            break;  
  8.        ... ...省略  

這裏最終調用onOpenCloseDialpad方法去實現打開或者關閉撥號盤,這裏我們繼續查看onOpenCloseDialpad方法,代碼如下:

[java] view plaincopy
  1. public void onOpenCloseDialpad() {  
  2.     ... ...省略  
  3.     //判斷撥號盤是否已經打開,如果是則隱藏反之則顯示  
  4.     if (mDialer.isOpened()) {  
  5.         closeDialpadInternal(true);  
  6.     } else {  
  7.         openDialpadInternal(true);  
  8.     }  
  9.     mApp.updateProximitySensorMode(mCM.getState());  
  10. }  
  11. //顯示撥號盤  
  12. private void openDialpadInternal(boolean animate) {  
  13.     mDialer.openDialer(animate);  
  14.     mApp.inCallUiState.showDialpad = true;  
  15. }  
  16. //以藏撥號盤  
  17. private void closeDialpadInternal(boolean animate) {  
  18.     mDialer.closeDialer(animate);  
  19.     mApp.inCallUiState.showDialpad = false;  
  20. }  

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

[java] view plaincopy
  1. public void openDialer(boolean animate) {  
  2.     ... ...省略  
  3.   
  4.     if (!isOpened()) {  
  5.         // 這裏animate=true  
  6.         if (animate) {  
  7.             AnimationUtils.Fade.show(mDialerView);  
  8.         ... ...省略  
  9. }  

查看AnimationUtils.Fade.show方法如下:

[java] view plaincopy
  1. public static void show(final View view) {  
  2.         ... ...省略  
  3.         view.setVisibility(View.VISIBLE);  
  4.         ... ...省略  
  5. }  

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

(4)時序圖


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

audioButton開啓/關閉揚聲器

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

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


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

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

控制流程

(1)佈局文件

Phone/res/layout/incall_touch_ui.xml

[html] view plaincopy
  1. <ToggleButton android:id="@+id/audioButton"  
  2.              style="@style/InCallCompoundButton"  
  3.              android:background="@drawable/btn_compound_audio"  
  4.              android:contentDescription="@string/onscreenAudioText"/>  

(2)初始化文件

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

(3)工作流程

控件初始化代碼如下:

audioButton控件初始化:

[java] view plaincopy
  1. private CompoundButton mAudioButton;  
  2. mAudioButton = (CompoundButton) mInCallControls.findViewById(R.id.audioButton);  
  3. mAudioButton.setOnClickListener(this);  
  4. mAudioButton.setOnLongClickListener(this);  

這裏的LongClick實際上是,在我們長按該控件時會彈出一個toast顯示該控件的作用。找到audioButton的onClick實現方法,如下:
[java] view plaincopy
  1. @Override  
  2. public void onClick(View view) {  
  3.     int id = view.getId();  
  4.     ... ...省略  
  5.     switch (id) {  
  6.         ... ...省略  
  7.         case R.id.audioButton:  
  8.         ... ...省略  
  9.             handleAudioButtonClick();  
  10.             break;  
  11.         ... ...省略  
  12.     }  
  13. }  

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

[java] view plaincopy
  1. private void handleAudioButtonClick() {  
  2.     ... ...省略  
  3.     //如果連接了藍牙耳機則執行if裏面的代碼  
  4.     if (inCallControlState.bluetoothEnabled) {  
  5.         ... ...省略  
  6.     } else {  
  7.         ... ...省略  
  8.         mInCallScreen.toggleSpeaker();  
  9.     }  
  10. }  

這裏我們繼續查看InCallScreen中的toggleSpeaker方法:

[java] view plaincopy
  1. public void toggleSpeaker() {  
  2.     ... ...省略  
  3.     PhoneUtils.turnOnSpeaker(this, newSpeakerState, true);  
  4.     ... ...省略  
  5. }  

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

(4)時序圖


圖 11 開啓/關閉揚聲器

muteButton開啓/關閉麥克風靜音

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

控制流程

(1)佈局文件

Phone/res/layout/incall_touch_ui.xml

[html] view plaincopy
  1. <ToggleButton android:id="@+id/muteButton"  
  2.              style="@style/InCallCompoundButton"  
  3.              android:background="@drawable/btn_compound_mute"  
  4.              android:contentDescription="@string/onscreenMuteText"/>  

(2)初始化文件

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

(3)工作流程

控件初始化代碼如下:

muteButton控件初始化:

[java] view plaincopy
  1. private CompoundButton mMuteButton;  
  2. mMuteButton = (CompoundButton) mInCallControls.findViewById(R.id.muteButton);  
  3. mMuteButton.setOnClickListener(this);  
  4. mMuteButton.setOnLongClickListener(this);  

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

[java] view plaincopy
  1. @Override  
  2. public void onClick(View view) {  
  3.     int id = view.getId();  
  4.     ... ...省略  
  5.     switch (id) {  
  6.         ... ...省略  
  7.         case R.id.muteButton:  
  8.         ... ...省略  
  9.             mInCallScreen.handleOnscreenButtonClick(id);  
  10.             break;  
  11.         ... ...省略  
  12.     }  
  13. }  

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

[java] view plaincopy
  1. public void handleOnscreenButtonClick(int id) {  
  2.    ... ...省略  
  3.    switch (id) {  
  4.        ... ...省略  
  5.        case R.id.muteButton:  
  6.            onMuteClick();  
  7.            break;  
  8.        ... ...省略  

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

[java] view plaincopy
  1. public void onMuteClick() {  
  2.     ... ...省略  
  3.     PhoneUtils.setMute(newMuteState);  
  4.     ... ...省略  
  5. }  

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

(4)時序圖


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

holdButton開啓/關閉呼叫保持

holdButton實際上就是呼叫保持的開關,用戶可以暫停當前通話,在InCallTouchUi佈局上顯示爲:。當用戶在通話過程中點擊該圖標之後,對方將會聽到類似提示音“請稍等,現在是呼叫保持……”,該功能用於在兩路通話之間進行切換,但只有一路通話時無法開啓呼叫保持功能。

控制流程

(1)佈局文件

Phone/res/layout/incall_touch_ui.xml

[html] view plaincopy
  1. <ToggleButton android:id="@+id/holdButton"  
  2.              style="@style/InCallCompoundButton"  
  3.              android:background="@drawable/btn_compound_hold"  
  4.              android:contentDescription="@string/onscreenHoldText"/>  

(2)初始化文件

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

(3)工作流程

控件初始化代碼如下:

muteButton控件初始化:

[java] view plaincopy
  1. private CompoundButton mHoldButton;  
  2. mHoldButton = (CompoundButton) mInCallControls.findViewById(R.id.holdButton);  
  3. mHoldButton.setOnClickListener(this);  
  4. mHoldButton.setOnLongClickListener(this);  

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

[java] view plaincopy
  1. @Override  
  2. public void onClick(View view) {  
  3.     int id = view.getId();  
  4.     ... ...省略  
  5.     switch (id) {  
  6.         ... ...省略  
  7.         case R.id.muteButton:  
  8.         ... ...省略  
  9.             mInCallScreen.handleOnscreenButtonClick(id);  
  10.             break;  
  11.         ... ...省略  
  12.     }  
  13. }  

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

[java] view plaincopy
  1. public void handleOnscreenButtonClick(int id) {  
  2.    ... ...省略  
  3.    switch (id) {  
  4.        ... ...省略  
  5.        case R.id.holdButton:  
  6.            onHoldClick();  
  7.            break;  
  8.        ... ...省略  

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

[java] view plaincopy
  1. private void onHoldClick() {  
  2.     ... ...省略  
  3.     if (hasActiveCall && !hasHoldingCall) {  
  4.         // 開啓呼叫保持  
  5.         PhoneUtils.switchHoldingAndActive(  
  6.             mCM.getFirstActiveBgCall());  // Really means "hold" in this state  
  7.         newHoldState = true;  
  8.         holdButtonEnabled = true;  
  9.     } else if (!hasActiveCall && hasHoldingCall && !haveMultipleHoldingCall) {  
  10.         // 取消呼叫保持  
  11.         PhoneUtils.switchHoldingAndActive(  
  12.             mCM.getFirstActiveBgCall());  // Really means "unhold" in this state  
  13.         newHoldState = false;  
  14.         holdButtonEnabled = true;  
  15.     }   
  16.     ... ...省略  
  17.     // 強制關閉撥號盤  
  18.     closeDialpadInternal(true);  // do the "closing" animation  
  19. }  

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

[java] view plaincopy
  1. static void switchHoldingAndActive(Call heldCall) {  
  2.     ... ...省略  
  3.     try {  
  4.         CallManager cm = PhoneGlobals.getInstance().mCM;  
  5.         ... ...省略  
  6.         cm.switchHoldingAndActive(heldCall);  
  7.         ... ...省略  
  8. }  

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

(4)時序圖


圖 13 呼叫保持開啓/關閉

addButton添加一路通話

addButton的意思就是在當前通話的基礎上添加一路通話,當前通話將會切換到呼叫保持狀態。該圖標在InCallTouchUi佈局上顯示爲:

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


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

控制流程

(1)佈局文件

Phone/res/layout/incall_touch_ui.xml

[html] view plaincopy
  1. <ImageButton android:id="@+id/addButton"  
  2.             style="@style/InCallButton"  
  3.             android:src="@drawable/ic_add_contact_holo_dark"  
  4.             android:contentDescription="@string/onscreenAddCallText"/>  

(2)初始化文件

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

(3)工作流程

控件初始化代碼如下:

addButton控件初始化:

[java] view plaincopy
  1. private CompoundButton mAddButton;  
  2. mAddButton = (CompoundButton) mInCallControls.findViewById(R.id.addButton);  
  3. mAddButton.setOnClickListener(this);  
  4. mAddButton.setOnLongClickListener(this);  

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

[java] view plaincopy
  1. @Override  
  2. public void onClick(View view) {  
  3.     int id = view.getId();  
  4.     ... ...省略  
  5.     switch (id) {  
  6.         ... ...省略  
  7.         case R.id.addButton:  
  8.         ... ...省略  
  9.             mInCallScreen.handleOnscreenButtonClick(id);  
  10.             break;  
  11.         ... ...省略  
  12.     }  
  13. }  

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

[java] view plaincopy
  1. public void handleOnscreenButtonClick(int id) {  
  2.    ... ...省略  
  3.    switch (id) {  
  4.        ... ...省略  
  5.        case R.id.addButton:  
  6.            onAddCallClick();  
  7.            break;  
  8.        ... ...省略  

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

[java] view plaincopy
  1. private void onAddCallClick() {  
  2.     PhoneUtils.startNewCall(mCM);  
  3. }  

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

[java] view plaincopy
  1. /* package */ static boolean startNewCall(final CallManager cm) {  
  2.     final PhoneGlobals app = PhoneGlobals.getInstance();  
  3.     ... ...省略  
  4.     // 將當前的通話靜音  
  5.     if (cm.hasActiveFgCall()) {  
  6.         setMuteInternal(cm.getActiveFgCall().getPhone(), true);  
  7.         app.setRestoreMuteOnInCallResume(true);  
  8.     }  
  9.     Intent intent = new Intent(Intent.ACTION_DIAL);  
  10.     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  11.     intent.putExtra(ADD_CALL_MODE_KEY, true);  
  12.     try {  
  13.         //跳轉到撥號界面  
  14.         app.startActivity(intent);  
  15.     } catch (ActivityNotFoundException e) {  
  16.         ... ...省略  
  17.     }  
  18.     return true;  
  19. }  

需要注意的是,當我們點擊addButton後,當前通話會開啓靜音功能,在我們撥打新一路通話時,先前的通話將會自動切換到呼叫保持狀態。這裏的Intent.ACTION_DIAL查找對應的具體字符串爲“android.intent.action.DIAL”,查找對應的Activity找到Contacts中的NoPhoneActivity,該Activity用於加載撥號盤。

(4)時序圖


圖 15 新增一路通話時序圖

endButton掛斷當前通話

endButton就是掛斷電話的那個按鈕,點擊之後將掛斷當前通話。該圖標在InCallTouchUi佈局上顯示爲:

控制流程

(1)佈局文件

Phone/res/layout/incall_touch_ui.xml

[html] view plaincopy
  1. <ImageButton android:id="@+id/endButton"  
  2.             style="@style/InCallEndButton"  
  3.             android:layout_width="match_parent"  
  4.             android:layout_weight="1"  
  5.             android:src="@drawable/ic_end_call"  
  6.             android:background="@drawable/end_call_background"  
  7.             android:contentDescription="@string/onscreenEndCallText" />  

(2)初始化文件

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

(3)工作流程

控件初始化代碼如下:

endButton控件初始化:

[java] view plaincopy
  1. private CompoundButton mEndButton;  
  2. mEndButton = (CompoundButton) mInCallControls.findViewById(R.id.addButton);  
  3. mEndButton.setOnClickListener(this);  

這裏只註冊了onClick的監聽事件,沒有LongOnClick的監聽事件。找到endButton的onClick實現方法,如下:

[java] view plaincopy
  1. @Override  
  2. public void onClick(View view) {  
  3.     int id = view.getId();  
  4.     ... ...省略  
  5.     switch (id) {  
  6.         ... ...省略  
  7.         case R.id.endButton:  
  8.         ... ...省略  
  9.             mInCallScreen.handleOnscreenButtonClick(id);  
  10.             break;  
  11.         ... ...省略  
  12.     }  
  13. }  

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

[java] view plaincopy
  1. public void handleOnscreenButtonClick(int id) {  
  2.    ... ...省略  
  3.    switch (id) {  
  4.        ... ...省略  
  5.        case R.id.endButton:  
  6.            internalHangup();  
  7.            break;  
  8.        ... ...省略  

然後調用internalHangup方法來實現掛斷當前通話,代碼如下:

[java] view plaincopy
  1. private void internalHangup() {  
  2.     ... ...省略  
  3.     PhoneUtils.hangup(mCM);  
  4.     ... ...省略  
  5.     }  
  6. }  

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

[java] view plaincopy
  1. public static boolean hangup(CallManager cm) {  
  2.     ... ...省略  
  3.         ringing = cm.getFirstActiveRingingCall();  
  4.         fg = cm.getActiveFgCall();  
  5.         bg = cm.getFirstActiveBgCall();  
  6.     ... ...省略  
  7.     //因爲選擇的是掛斷當前通話,因此fg.isIdle()爲false  
  8.     } else if (!fg.isIdle() || fg.state == Call.State.DISCONNECTING) {  
  9.         if (DBG) log("hangup(): hanging up foreground call");  
  10.         hungup = hangup(fg);  
  11.     ... ...省略  
  12. }  

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

[java] view plaincopy
  1. static boolean hangup(Call call) {  
  2.     ... ...省略  
  3.     //掛斷當前通話  
  4.     call.hangup();  
  5.     ... ...省略  
  6. }  

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

[java] view plaincopy
  1. public void  
  2. hangup() throws CallStateException {  
  3.     owner.hangup(this);  
  4. }  

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

(4)時序圖


圖 16 掛斷當前通話時序圖

InCallScreen CallCard通話信息顯示

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


圖 17 CallCard通話信息

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

CallCard佈局文件

通話信息界面實際上也在incall_screen.xml的佈局中,但實際引用的佈局爲call_card.xml,整個call_card的佈局較爲複雜,因爲通話可以是一路通話但也可以是兩路通話,可以是來電也可以是去電,因此CallCard的佈局較爲繁雜,通過Hierarchy Viewer可以查看到。

CallCard界面更新

因爲CallCard界面主要用於告知用戶當前的通話狀態,主要反映的是一些狀態信息,因此我們主要查看其更新的代碼,在InCallScreen的onResume中,我們可以看到有關於同步Phone狀態的代碼如下:

[java] view plaincopy
  1. SyncWithPhoneStateStatus status = syncWithPhoneState();  

而這裏的syncWithPhoneState方法就是用於同步Phone的狀態,代碼如下:

[java] view plaincopy
  1. private SyncWithPhoneStateStatus syncWithPhoneState() {  
  2.     ... ...省略  
  3.     if (mCM.hasActiveFgCall() || mCM.hasActiveBgCall() || mCM.hasActiveRingingCall()  
  4.             || hasPendingMmiCodes || hasPendingMmiCodes2 || showProgressIndication || showScreenEvenAfterDisconnect) {  
  5.         if (VDBG) log("syncWithPhoneState: it's ok to be here; update the screen...");  
  6.         updateScreen();  
  7.         return SyncWithPhoneStateStatus.SUCCESS;  
  8.     }  
  9.     ... ...省略  
  10. }  

這裏會執行到updateScreen方法中,繼續查看代碼:

[java] view plaincopy
  1. private void updateScreen() {  
  2.     ... ...省略  
  3.     mCallCard.updateState(mCM);  
  4.     ... ...省略  
  5. }  

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

[java] view plaincopy
  1.     /* package */ void updateState(CallManager cm) {  
  2.         ... ...省略  
  3.             //更新來電信息  
  4.             updateRingingCall(cm);  
  5.         ... ...省略  
  6.             //更新當前通話信息  
  7.             updateForegroundCall(cm);  
  8.         ... ...省略  
  9.             //更新已斷開連接的通話信息  
  10.             updateAlreadyDisconnected(cm);  
  11.         ... ...省略  
  12.             //更新沒有通話的界面信息  
  13.             updateNoCall(cm);  
  14.         ... ...省略  
  15. }  

在updateState中會根據不同的條件選擇更新不同的界面,從而在InCallScreen中展示不同的結果。有以下四個方法用於更新不同狀態下的CallCard信息:

  • updateRingingCall:顯示/更新來電界面信息;
  • updateForegroundCall:顯示/更新當前通話界面信息;
  • updateAlreadyDisconnected:更新/顯示已斷開連接的通話界面信息,該界面只是一瞬間狀態;
  • updateNoCall:更新/顯示沒有通話時的通話界面信息;該界面一般不會出現,只爲以防萬一;

CallCard更新時序圖


圖18 CallCard更新時序圖

小結

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

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

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

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