Android12 Framework電話功能UI定制

文章目錄

  • 簡介
  • 代碼
    • 中間按鈕
    • 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中監聽按鍵并進行處理。這里實現按返回鍵進行掛斷。

  1. 先看一下掛斷電話的按鈕處理函數
    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();}}}
  1. 監聽按鍵
    在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位就能錯誤匹配問題
作者:帥得不敢出門

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93106.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93106.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93106.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

高并發場景下分布式ID生成方案對比與實踐指南

高并發場景下分布式ID生成方案對比與實踐指南 在分布式系統中&#xff0c;唯一且全局有序的ID生成器是很多業務的底層組件。隨著系統并發量不斷攀升&#xff0c;如何在高并發場景下保證ID的唯一性、性能、可用性和可擴展性&#xff0c;成為后端架構師需要重點考慮的問題。本文將…

Emscripten 指南:概念與使用

Emscripten 指南&#xff1a;概念與使用 什么是 Emscripten&#xff1f; Emscripten 是一個開源的編譯器工具鏈&#xff0c;用于將 C/C 代碼編譯成高效的 WebAssembly&#xff08;Wasm&#xff09;和 JavaScript。它基于 LLVM 編譯器架構&#xff0c;允許開發者&#xff1a; ?…

使用鏡像網站 打開克隆 GitHub 網站倉庫內容 git clone https://github.com/

GitHub 網站有時因 DNS 解析問題或網絡限制&#xff0c;國內訪問可能會受限。使用鏡像網站打開網站 使用鏡像網站&#xff1a;GitHub 有一些鏡像網站&#xff0c;可替代官網訪問&#xff0c;如https://hub.fastgit.org、https://gitclone.com、https://github.com.cnpmjs.org等…

Linux隨記(二十二)

一、redhat6.5 從openssh5.3 升級到openssh10 - 報錯處理【升級后賬號密碼一直錯誤 和 sshd dead but subsys locked】 虛擬機測試情況 - 正常&#xff1a;情況一、 升級后賬號密碼一直錯誤 情況二、 執行service sshd status出現 sshd dead but subsys locked

機器學習之TF-IDF文本關鍵詞提取

目錄 一、什么是 TF-IDF&#xff1f; 1.語料庫概念理解 二、TF-IDF 的計算公式 1. 詞頻&#xff08;TF&#xff09; 2. 逆文檔頻率&#xff08;IDF&#xff09; 3. TF-IDF 值 三、關鍵詞提取之中文分詞的實現 四、TF-IDF簡單案例實現 &#xff08;1&#xff09;數據集…

Flutter屏幕和字體適配(ScreenUtil)

一、簡介 flutter_screenutil 是一個 Flutter 插件&#xff0c;專門用于處理屏幕適配問題。它簡化了不同設備間尺寸差異的處理&#xff0c;確保你的應用在各種屏幕上都能保持良好的顯示效果。開發者可以通過簡單的調用來設置基于設計圖尺寸的控件寬高和字體大小。 項目地址&a…

mimiconda+vscode

安裝miniconda實現python包管理&#xff0c;并通過vscode進行編寫python代碼 miniconda簡單介紹 Miniconda 是 Anaconda 公司的一個輕量級 Python 發行版本&#xff0c;它包含了最基本的包管理器 conda 和 Python 環境&#xff0c;只帶最核心的組件&#xff0c;沒有額外的大量科…

Windows文件時間修改指南:從手動到自動化

修改文件的時間屬性可以滿足多種需求。比如&#xff0c;它可以幫助整理文件&#xff0c;使得文件按照特定的時間順序排列&#xff0c;有助于更好地管理資料。它的體積真小&#xff0c;才300多KB。能用來調整文件的創建時間、最后訪問和修改時間。文件時間屬性修改_NewFileTime.…

能刷java題的網站

以下是一些適合刷Java題的優質網站&#xff0c;涵蓋從基礎到進階、算法面試及實戰項目等多種需求&#xff1a; ?一、綜合編程練習平臺? ?LeetCode?&#xff08;leetcode.com&#xff09; ?特點?&#xff1a;全球最知名的算法題庫&#xff0c;含海量Java題目&#xff0c;分…

掘金數據富礦,永洪科技為山東黃金定制“數智掘金”實戰營

在黃金開采的轟鳴聲中&#xff0c;另一場靜水深流的“掘金行動”正悄然展開。山東黃金集團&#xff0c;這個行業的巨頭&#xff0c;在深挖地層寶藏的同時&#xff0c;也敏銳捕捉到數據洪流中蘊藏的價值富礦。然而&#xff0c;當海量業務數據匯聚&#xff0c;如何從中精準提煉決…

【論文閱讀】BEVFormer論文解析及Temporal Self-Attention、Spatial Cross-Attention注意力機制詳解及代碼示例

BEVFormer: Learning Bird’s-Eye-ViewRepresentation from Multi-Camera Images via Spatiotemporal Transformers|Temporal Self-Attention、Spatial Cross-Attention注意力機制詳解 BEVFormer&#xff08;Bird’s-Eye-View Former&#xff09;是一種先進的計算機視覺模型&am…

在 Ubuntu 中docker容器化操作來使用新建的 glibc-2.32

在 Ubuntu 中使用容器化操作來使用新建的 glibc-2.32,可以通過創建自定義 Docker 鏡像來實現。以下是完整的解決方案: 方案 1:創建包含 glibc-2.32 的 Docker 鏡像 1. 創建 Dockerfile dockerfile # 使用 Ubuntu 基礎鏡像 FROM ubuntu:20.04# 安裝編譯依賴 RUN apt-get …

GOOUUU ESP32-S3-CAM 果云科技開發板開發指南(二)(超詳細!)Vscode+espidf 攝像頭拍攝視頻實時傳輸到LCD,文末附源碼

書接上回&#xff0c;上一篇blog是使用esp32s3通過ov2640攝像頭拍攝到一幀照片&#xff0c;并把它保存到了SD卡中&#xff0c;這第二篇就通過LCD將拍攝到的圖片顯示到LCD上&#xff0c;本次分享硬件使用的 ESP32-S3-CAM 果云科技開發板&#xff0c;并且使用了配套的LCD擴展板&a…

攻防世界-ics-05(遠程文件執行)

一.審題大致瀏覽一下網頁&#xff0c;發現就這邊會有東西。看一下源碼會不會有東西或者稍微點擊一下這個頁面的內容看會不會出現東西。點擊了一下這個云平臺設備維護中心發現url變了&#xff0c;是get的方法傳page參數二.嘗試漏洞類型自己這邊試了sql注入發現不是&#xff0c;試…

Dell PowerEdge: Servers by generation (按代系劃分的服務器)

Dell PowerEdge: Servers by generation {按代系劃分的服務器}1. Table of 17th, 16th, 15th, and 14th Generation PowerEdge servers2. List of all PowerEdge server models including Type, CPU vendor, Generation, and Remote ManagementReferencesPowerEdge: Servers by…

Rust學習筆記(二)|變量、函數與控制流

本篇文章包含的內容1 變量與常量2 類型2.1 標量類型2.2 復合類型3 函數4 控制流4.1 分支4.2 循環1 變量與常量 在Rust中&#xff0c;使用let關鍵字聲明一個變量&#xff0c;變量默認是不可變的。如果要聲明可變變量&#xff0c;需要使用mut關鍵字將其聲明為可變變量。 let x …

【渲染流水線】[幾何階段]-[圖元裝配]以UnityURP為例

【從UnityURP開始探索游戲渲染】專欄-直達 前情提要 【渲染流水線】主線索引-從數據到圖像以UnityURP為例-CSDN博客 圖元裝配負責將離散頂點組裝成完整幾何圖元&#xff08;如點、線、三角形、三角形條帶&#xff09; &#xff08;對渲染的探索是個持續不斷完善的過程&#x…

jvm有哪些垃圾回收器,實際中如何選擇?

7.G1收集器一款面向服務端應用的垃圾收集器。 特點如下&#xff1a; 并行與并發&#xff1a;G1能充分利用多CPU、多核環境下的硬 件優勢&#xff0c;使用多個CPU來縮短Stop-The-World停頓時間。部分收集器原本需要停頓Java線程來執行GC動作&#xff0c;G1收 集器仍然可以通過并…

多語言與隱形攻擊:LLM安全防線為何被頻頻突破?

你是否曾以為&#xff0c;只要加裝了“防火墻”&#xff0c;大型語言模型&#xff08;LLM&#xff09;就能高枕無憂&#xff1f;Trendoyl 的實際測試卻讓我大吃一驚&#xff1a;即便部署了 Meta 的 Llama Guard&#xff0c;攻擊者還是能輕松用多語種、字符混淆&#xff0c;甚至…

分布式光伏氣象站:為光伏電站的 “氣象感知眼”

分布式光伏氣象站&#xff1a;為光伏電站的 “氣象感知眼”柏峰 【BF-GFQX】在全球能源轉型的浪潮中&#xff0c;分布式光伏發電憑借其就近消納、清潔高效的優勢&#xff0c;成為能源結構優化的重要力量。而分布式光伏氣象站&#xff0c;作為光伏電站的 “智慧感知眼”&#xf…