文章目錄
- 簡介
- 代碼
- 中間按鈕
- Fragment
- 創建VideoCallFragment
- Fragment管理
- 添加按鍵掛斷電話功能
- 相關文章
簡介
Android版本:12
芯片平臺:展銳
如下圖為通話中的UI,打電話出去時顯示的UI與此也差不多,但來電時UI是不一樣的
這個界面是InCallActivity,InCallActivity 是 Android 系統中負責通話界面顯示的宿主Activity,其核心功能包括管理多個Fragment的顯示與隱藏,并協調不同通話狀態(如來電、通話中、撥號等)的界面切換。
-
VideoCallFragment
VideoCallFragment是 InCallActivity 中用于顯示視頻通話的獨立Fragment,主要負責視頻通話界面的布局與交互邏輯。其布局文件為@layout/incall_video_call_fragment,通常包含視頻預覽窗口、控制按鈕等組件。 ? -
InCallFragment
InCallFragment是InCallActivity中負責顯示聯系人信息和通話狀態的核心Fragment,布局文件為@layout/in_call_fragment。它通過 ButtonController 動態管理通話按鍵的顯示與狀態更新,并持有 InCallButtonGridFragment 對象以控制按鈕布局。 ? -
DialpadFragment
DialpadFragment是InCallActivity中用于撥號鍵盤的獨立Fragment,布局文件為@layout/incall_dialpad_fragment。在通話界面中,它提供數字按鍵輸入功能,支持橫屏和豎屏模式下的撥號操作。 ? -
核心交互邏輯
InCallActivity通過 InCallPresenter 管理不同Fragment的顯示狀態,并根據通話狀態(如來電、通話中、掛斷等)動態調整界面布局。例如,當用戶結束通話后返回桌面,再重新啟動 Dialer 應用時,系統會檢查當前通話狀態并決定是否直接跳轉至 InCallActivity
代碼
要定位其代碼,可以通過UI上的文本來查找
中間按鈕
比如左上角的"通話錄音"
通過搜索,知道文本在這個xml文件中
vendor/sprd/platform/packages/apps/SprdDialer/java/com/android/incallui/sprd/res/values-zh-rCN/strings_ex.xml
<string name="call_recording_setting_title">"通話錄音"</string>
<string name="record_menu_title">通話錄音</string>
<string name="record_menu_title_recording">錄音中</string>
通過record_menu_title可定位到通話中的窗口
vendor/sprd/platform/packages/apps/SprdDialer/java/com/android/incallui/video/impl/VideoCallFragment.java
@Overridepublic void updateButtonStates() {LogUtil.i("VideoCallFragment.updateButtonState", null);//speakerButtonController.updateButtonState();switchOnHoldCallController.updateButtonState();/* UNISOC: Add video call option menu@{ *//* UNISOC:modify for bug608545 @ { */if (mOverflowPopup != null && mOverflowPopup.getMenu() != null && mOverflowPopup.getMenu().hasVisibleItems()) {mOverflowPopup.dismiss();}/* @} */mOverflowPopup = new PopupMenu(getActivity(), mOverflowButton);mOverflowPopup.getMenuInflater().inflate(R.menu.videocall_option_menu, mOverflowPopup.getMenu());Menu menu = mOverflowPopup.getMenu();int count = menu.size();for (int i = 0; i < count; i++) {MenuItem item = menu.getItem(i);boolean visible = false;switch (item.getItemId()) {case R.id.add_call_menu:visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_ADD_CALL) == BUTTON_VISIBLE ? true : false;break;case R.id.merge_menu:visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_MERGE) == BUTTON_VISIBLE ? true : false;break;case R.id.hold_call_menu:visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_HOLD) == BUTTON_VISIBLE ? true : false;break;case R.id.swap_call_menu:visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_SWAP) == BUTTON_VISIBLE ? true : false;break;case R.id.changeto_audio_menu:visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_DOWNGRADE_TO_AUDIO) == BUTTON_VISIBLE ? true : false;break;// UNISOC: add for bug1143842case R.id.call_record_menu:visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_RECORD) == BUTTON_VISIBLE ? true : false;break;// UNISOC: add for bug1142881case R.id.manager_conference_menu:visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_MANAGE_VIDEO_CONFERENCE) == BUTTON_VISIBLE ? true : false;break;// UNISOC: add for bug1152075case R.id.dialpad_menu:visible = mButtonVisibilityMap.get(InCallButtonIds.BUTTON_DIALPAD) == BUTTON_VISIBLE ? true : false;break;}item.setVisible(visible);}mOverflowPopup.setOnMenuItemClickListener(new OnMenuItemClickListener() {@Overridepublic boolean onMenuItemClick(MenuItem item) {selectMenuItem(item);return true;}});mOverflowButton.setVisibility((mOverflowPopup != null && mOverflowPopup.getMenu() != null && mOverflowPopup.getMenu().hasVisibleItems()) ? View.VISIBLE : View.GONE);/*@}*/}@Overridepublic void setRecord(boolean value) {LogUtil.i("VideoCallFragment.setRecord", "value: " + value);isRecording = value;if(mOverflowPopup == null){//UNISOC:add for bug1143842LogUtil.e("VideoCallFragment", "setRecord mOverflowPopup is null return");return;}// 這個R.id.call_record_menu就是對應左上角的“通話錄音”的按鈕MenuItem menuItem = mOverflowPopup.getMenu().findItem(R.id.call_record_menu);if(!value){menuItem.setTitle(R.string.record_menu_title); // 通話錄音}else{menuItem.setTitle(R.string.record_menu_title_recording); // 錄音中}}
R.id.call_record_menu就是對應左上角的“通話錄音”的按鈕, call_record_menu在
java/com/android/incallui/sprd/res/menu/videocall_option_menu.xml
文件中
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"><group><itemandroid:id="@+id/add_call_menu"android:title="@string/incall_label_add_call"/><itemandroid:id="@+id/merge_menu"android:title="@string/incall_content_description_merge_calls"/><itemandroid:id="@+id/hold_call_menu"android:title="@string/incall_content_description_hold"/><itemandroid:id="@+id/swap_call_menu"android:title="@string/incall_content_description_swap_calls"/><itemandroid:id="@+id/changeto_audio_menu"android:title="@string/incall_label_audiocall"/><itemandroid:id="@+id/call_record_menu"android:title="@string/record_menu_title"/><itemandroid:id="@+id/manager_conference_menu"android:title="@string/incall_label_manage"/><itemandroid:id="@+id/dialpad_menu"android:title="@string/incall_label_dialpad"/></group></menu>
可以看到通話時窗口中間的幾個按鈕是由這個videocall_option_menu.xml指定的
如果要屏蔽不顯示這些按鈕,要改
vendor/sprd/platform/packages/apps/SprdDialer/java/com/android/incallui/incall/impl/InCallFragment.java
isSupportedButton函數指定哪些按鈕可用可顯示,把不要的按鈕的id返回false就可以
private static boolean isSupportedButton(@InCallButtonIds int id) {return id == InCallButtonIds.BUTTON_AUDIO|| id == InCallButtonIds.BUTTON_MUTE|| id == InCallButtonIds.BUTTON_DIALPAD|| id == InCallButtonIds.BUTTON_HOLD|| id == InCallButtonIds.BUTTON_SWAP|| id == InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO|| id == InCallButtonIds.BUTTON_ADD_CALL|| id == InCallButtonIds.BUTTON_MERGE|| id == InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE|| id == InCallButtonIds.BUTTON_SWAP_SIM|| id == InCallButtonIds.BUTTON_UPGRADE_TO_RTT|| id == InCallButtonIds.BUTTON_RECORD|| id == InCallButtonIds.BUTTON_SEND_MESSAGE|| id == InCallButtonIds.BUTTON_HANGUP_ALL|| id == InCallButtonIds.BUTTON_ECT|| id == InCallButtonIds.BUTTON_INVITE|| id == InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY; //UNISOC: add for bug1201283
如下直接返回false則是所有按鈕都不顯示
private static boolean isSupportedButton(@InCallButtonIds int id) {return false;/*return id == InCallButtonIds.BUTTON_AUDIO|| id == InCallButtonIds.BUTTON_MUTE|| id == InCallButtonIds.BUTTON_DIALPAD|| id == InCallButtonIds.BUTTON_HOLD|| id == InCallButtonIds.BUTTON_SWAP|| id == InCallButtonIds.BUTTON_UPGRADE_TO_VIDEO|| id == InCallButtonIds.BUTTON_ADD_CALL|| id == InCallButtonIds.BUTTON_MERGE|| id == InCallButtonIds.BUTTON_MANAGE_VOICE_CONFERENCE|| id == InCallButtonIds.BUTTON_SWAP_SIM|| id == InCallButtonIds.BUTTON_UPGRADE_TO_RTT|| id == InCallButtonIds.BUTTON_RECORD|| id == InCallButtonIds.BUTTON_SEND_MESSAGE|| id == InCallButtonIds.BUTTON_HANGUP_ALL|| id == InCallButtonIds.BUTTON_ECT|| id == InCallButtonIds.BUTTON_INVITE|| id == InCallButtonIds.BUTTON_SWITCH_TO_SECONDARY; //UNISOC: add for bug1201283*/}
Fragment
InCallActivity(容器) → 持有 → InCallScreen(邏輯協調) → 管理 → {InCallFragment(語音通話界面),VideoInCallFragment(視頻通話界面)
}
創建VideoCallFragment
private boolean showVideoCallScreenFragment(FragmentTransaction transaction, DialerCall call) {if (didShowVideoCallScreen) {// 不顯示,移除FragmentVideoCallScreen videoCallScreen = getVideoCallScreen();if (videoCallScreen.getCallId().equals(call.getId())) {return false;}LogUtil.i("InCallActivity.showVideoCallScreenFragment","video call fragment exists but arguments do not match");hideVideoCallScreenFragment(transaction);}LogUtil.i("InCallActivity.showVideoCallScreenFragment", "call: %s", call);// 創建Fragment添加到Activity中進行顯示VideoCallScreen videoCallScreen =VideoBindings.createVideoCallScreen(call.getId(), (call.getVideoTech() != null && call.getVideoTech().shouldUseSurfaceView()));transaction.add(R.id.main, videoCallScreen.getVideoCallScreenFragment(), Tags.VIDEO_CALL_SCREEN); // 這里會與Tags.VIDEO_CALL_SCREEN綁定Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);didShowVideoCallScreen = true;return true;}
videoCallScreen.getVideoCallScreenFragment()返回VideoCallFragment的實例
VideoCallFragment.java
@Overridepublic Fragment getVideoCallScreenFragment() {return this;}
Fragment管理
findFragmentByTag() 是 FragmentManager 提供的一個方法,用于通過標簽(tag)查找已添加到布局中的 Fragment 實例。它是管理和操作 Fragment 的常用手段
private InCallScreen getCurrentInCallScreen(){if(didShowVideoCallScreen){// 前面顯示創建的時候已經與Tags.VIDEO_CALL_SCREEN綁定了VideoCalFragment這里通過tags可以訪問到它的實例return (InCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.VIDEO_CALL_SCREEN);}else {return (InCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.IN_CALL_SCREEN);}}
添加按鍵掛斷電話功能
在InCallFragment與VideoCallFragment中都有掛斷電話的按鈕,點擊按鈕就會進行掛斷并關閉通話窗口,要添加按鍵掛斷功能,可以在Fragment中監聽按鍵并進行處理。這里實現按返回鍵進行掛斷。
- 先看一下掛斷電話的按鈕處理函數
InCallFragment.java中掛斷在onClick函數中處理
@Overridepublic void onClick(View view) {if (view == endCallButton) {LogUtil.i("InCallFragment.onClick", "end call button clicked");Logger.get(getContext()).logImpression(DialerImpression.Type.IN_CALL_DIALPAD_HANG_UP_BUTTON_PRESSED);inCallScreenDelegate.onEndCallClicked();} else {LogUtil.e("InCallFragment.onClick", "unknown view: " + view);Assert.fail();}}
VideoCallFragment.java也有自己的onClick處理函數
@Overridepublic void onClick(View v) {if (v == endCallButton) {LogUtil.i("VideoCallFragment.onClick", "end call button clicked");// 這三行是掛斷處理的代碼,后面會抽到endCall函數inCallButtonUiDelegate.onEndCallClicked();videoCallScreenDelegate.resetAutoFullscreenTimer();PostCall.onDisconnectPressed(context);//add for bug1145284} else if (v == swapCameraButton) {if (swapCameraButton.getDrawable() instanceof Animatable) {/* UNISOC: add for bug1177044(1111450) {@*/Animatable swapAnime = (Animatable) swapCameraButton.getDrawable();if (swapAnime.isRunning()) {swapAnime.stop();}swapAnime.start();/* @} */}inCallButtonUiDelegate.toggleCameraClicked();videoCallScreenDelegate.resetAutoFullscreenTimer();} else if (v == mOverflowButton){ //UNISOC: Add video call option menuif (mOverflowPopup != null) {mOverflowPopup.show();}}}
- 監聽按鍵
在Frgment的onCreateView函數中添加
view.setFocusableInTouchMode(true);view.requestFocus();view.setOnKeyListener((v, keyCode, event) -> {if (event.getAction() == KeyEvent.ACTION_DOWN) {if (keyCode == KeyEvent.KEYCODE_BACK) {// 處理返回鍵endCall(); // 這里可以把原來的掛斷代碼抽出來放到新的endCall函數中return true;}}return false;});
這里以VideoCallFragment為例,上面已經知道掛斷的代碼是三行,把這三行放到endCall中
private void endCall() {inCallButtonUiDelegate.onEndCallClicked();videoCallScreenDelegate.resetAutoFullscreenTimer();PostCall.onDisconnectPressed(context);//add for bug1145284}
相關文章
Android 11代碼實現自動接聽電話
安卓設備adb執行AT指令控制電話卡
Android CTA認證電話號碼7位就能錯誤匹配問題
作者:帥得不敢出門