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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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…