android自定义来电秀UI

简单来电秀功能,效果如图:

底部附上demo 

一、新建一个PhoneCallService服务,在服务中监听来电等状态,且控制UI显示

public class PhoneCallService extends InCallService {private final Call.Callback callback = new Call.Callback() {@Overridepublic void onStateChanged(Call call, int state) {super.onStateChanged(call, state);switch (state) {case Call.STATE_ACTIVE: {break;}case Call.STATE_DISCONNECTED: {ActivityStack.getInstance().finishActivity(PhoneCallActivity.class);break;}}}};@Overridepublic void onCallAdded(Call call) {super.onCallAdded(call);call.registerCallback(callback);PhoneCallManager.call = call;CallType callType = null;if (call.getState() == Call.STATE_RINGING) {callType = CallType.CALL_IN;} else if (call.getState() == Call.STATE_CONNECTING) {callType = CallType.CALL_OUT;}if (callType != null) {Call.Details details = call.getDetails();String phoneNumber = details.getHandle().getSchemeSpecificPart();PhoneCallActivity.actionStart(this, phoneNumber, callType);}}@Overridepublic void onCallRemoved(Call call) {super.onCallRemoved(call);call.unregisterCallback(callback);PhoneCallManager.call = null;}public enum CallType {CALL_IN,CALL_OUT,}

二、在MainActivity中设置应用为默认来电主题,与权限设置

public class MainActivity extends AppCompatActivity {@SuppressLint("UseSwitchCompatOrMaterialCode")private Switch switchPhoneCall;@SuppressLint("UseSwitchCompatOrMaterialCode")private Switch switchListenCall;private CompoundButton.OnCheckedChangeListener switchCallCheckChangeListener;private final ActivityResultLauncher<Intent> dialerRequestLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {if (result.getResultCode() == Activity.RESULT_OK) {Toast.makeText(MainActivity.this, getString(R.string.app_name) + " 已成为默认电话应用",Toast.LENGTH_SHORT).show();}});@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();}private void initView() {switchPhoneCall = findViewById(R.id.switch_default_phone_call);switchListenCall = findViewById(R.id.switch_call_listenr);switchPhoneCall.setOnClickListener(v -> {// 发起将本应用设为默认电话应用的请求,仅支持 Android M 及以上if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {if (switchPhoneCall.isChecked()) {// Android 10 之后需要通过 RoleManager 修改默认电话应用if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {RoleManager roleManager = (RoleManager) getSystemService(Context.ROLE_SERVICE);Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);dialerRequestLauncher.launch(intent);} else {Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, getPackageName());startActivity(intent);}} else {// 取消时跳转到默认设置页面startActivity(new Intent("android.settings.MANAGE_DEFAULT_APPS_SETTINGS"));}} else {Toast.makeText(MainActivity.this, "Android 6.0 以上才支持修改默认电话应用!", Toast.LENGTH_LONG).show();switchPhoneCall.setChecked(false);}});// 检查是否开启了权限switchCallCheckChangeListener = (buttonView, isChecked) -> {if (isChecked && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M&& !Settings.canDrawOverlays(MainActivity.this)) {// 请求 悬浮框 权限askForDrawOverlay();// 未开启时清除选中状态,同时避免回调switchListenCall.setOnCheckedChangeListener(null);switchListenCall.setChecked(false);switchListenCall.setOnCheckedChangeListener(switchCallCheckChangeListener);return;}if (isChecked && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&ContextCompat.checkSelfPermission(this,Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {Toast.makeText(this, "缺少获取电话状态权限", Toast.LENGTH_SHORT).show();return;}Intent callListener = new Intent(MainActivity.this, CallListenerService.class);if (isChecked) {startService(callListener);Toast.makeText(this, "电话监听服务已开启", Toast.LENGTH_SHORT).show();} else {stopService(callListener);Toast.makeText(this, "电话监听服务已关闭", Toast.LENGTH_SHORT).show();}};switchListenCall.setOnCheckedChangeListener(switchCallCheckChangeListener);}private void askForDrawOverlay() {AlertDialog alertDialog = new AlertDialog.Builder(this).setTitle("允许显示悬浮框").setMessage("为了使电话监听服务正常工作,请允许这项权限").setPositiveButton("去设置", (dialog, which) -> {openDrawOverlaySettings();dialog.dismiss();}).setNegativeButton("稍后再说", (dialog, which) -> dialog.dismiss()).create();//noinspection ConstantConditionsalertDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);alertDialog.show();}/*** 跳转悬浮窗管理设置界面*/private void openDrawOverlaySettings() {if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// Android M 以上引导用户去系统设置中打开允许悬浮窗// 使用反射是为了用尽可能少的代码保证在大部分机型上都可用try {Context context = this;Class clazz = Settings.class;Field field = clazz.getDeclaredField("ACTION_MANAGE_OVERLAY_PERMISSION");Intent intent = new Intent(field.get(null).toString());intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.setData(Uri.parse("package:" + context.getPackageName()));context.startActivity(intent);} catch (Exception e) {Toast.makeText(this, "请在悬浮窗管理中打开权限", Toast.LENGTH_LONG).show();}}}@Overrideprotected void onResume() {super.onResume();switchPhoneCall.setChecked(isDefaultPhoneCallApp());switchListenCall.setChecked(isServiceRunning(CallListenerService.class));}/*** Android M 及以上检查是否是系统默认电话应用*/public boolean isDefaultPhoneCallApp() {if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {TelecomManager manger = (TelecomManager) getSystemService(TELECOM_SERVICE);if (manger != null && manger.getDefaultDialerPackage() != null) {return manger.getDefaultDialerPackage().equals(getPackageName());}}return false;}public boolean isServiceRunning(Class<?> serviceClass) {ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);if (manager == null) return false;for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {if (serviceClass.getName().equals(service.service.getClassName())) {return true;}}return false;}

三、增加一个来电展示的自定义UI页面PhoneCallActivity,可根据需求更改

public class PhoneCallActivity extends AppCompatActivity implements View.OnClickListener {private TextView tvCallNumberLabel;private TextView tvCallNumber;private TextView tvPickUp;private TextView tvCallingTime;private TextView tvHangUp;private PhoneCallManager phoneCallManager;private PhoneCallService.CallType callType;private String phoneNumber;private Timer onGoingCallTimer;private int callingTime;public static void actionStart(Context context, String phoneNumber,PhoneCallService.CallType callType) {Intent intent = new Intent(context, PhoneCallActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.putExtra(Intent.EXTRA_MIME_TYPES, callType);intent.putExtra(Intent.EXTRA_PHONE_NUMBER, phoneNumber);context.startActivity(intent);}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_phone_call);ActivityStack.getInstance().addActivity(this);initData();initView();}private void initData() {phoneCallManager = new PhoneCallManager(this);onGoingCallTimer = new Timer();if (getIntent() != null) {phoneNumber = getIntent().getStringExtra(Intent.EXTRA_PHONE_NUMBER);callType = (PhoneCallService.CallType) getIntent().getSerializableExtra(Intent.EXTRA_MIME_TYPES);}}private void initView() {int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION //hide navigationBar| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;getWindow().getDecorView().setSystemUiVisibility(uiOptions);getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);tvCallNumberLabel = findViewById(R.id.tv_call_number_label);tvCallNumber = findViewById(R.id.tv_call_number);tvPickUp = findViewById(R.id.tv_phone_pick_up);tvCallingTime = findViewById(R.id.tv_phone_calling_time);tvHangUp = findViewById(R.id.tv_phone_hang_up);tvCallNumber.setText(formatPhoneNumber(phoneNumber));tvPickUp.setOnClickListener(this);tvHangUp.setOnClickListener(this);// 打进的电话if (callType == PhoneCallService.CallType.CALL_IN) {tvCallNumberLabel.setText("来电号码");tvPickUp.setVisibility(View.VISIBLE);}// 打出的电话else if (callType == PhoneCallService.CallType.CALL_OUT) {tvCallNumberLabel.setText("呼叫号码");tvPickUp.setVisibility(View.GONE);phoneCallManager.openSpeaker();}showOnLockScreen();}public void showOnLockScreen() {this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |WindowManager.LayoutParams.FLAG_FULLSCREEN |WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |WindowManager.LayoutParams.FLAG_FULLSCREEN |WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);}@Overridepublic void onClick(View v) {if (v.getId() == R.id.tv_phone_pick_up) {phoneCallManager.answer();tvPickUp.setVisibility(View.GONE);tvCallingTime.setVisibility(View.VISIBLE);onGoingCallTimer.schedule(new TimerTask() {@Overridepublic void run() {runOnUiThread(new Runnable() {@SuppressLint("SetTextI18n")@Overridepublic void run() {callingTime++;tvCallingTime.setText("通话中:" + getCallingTime());}});}}, 0, 1000);} else if (v.getId() == R.id.tv_phone_hang_up) {phoneCallManager.disconnect();stopTimer();}}private String getCallingTime() {int minute = callingTime / 60;int second = callingTime % 60;return (minute < 10 ? "0" + minute : minute) +":" +(second < 10 ? "0" + second : second);}private void stopTimer() {if (onGoingCallTimer != null) {onGoingCallTimer.cancel();}callingTime = 0;}@Overrideprotected void onDestroy() {super.onDestroy();phoneCallManager.destroy();}

四、来电页面UI中布局文件activity_phone_call

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:background="@mipmap/sf_p1"tools:context=".phonecallui.PhoneCallActivity"><RelativeLayoutandroid:id="@+id/rl_user_info"android:layout_width="match_parent"android:layout_height="300dp"><TextViewandroid:id="@+id/tv_call_number_label"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_above="@+id/tv_call_number"android:layout_marginBottom="16dp"android:gravity="center"android:text="来电号码"android:textSize="18sp" /><TextViewandroid:id="@+id/tv_call_number"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"android:gravity="center"android:textAlignment="center"android:textColor="@android:color/white"android:textSize="28sp"android:textStyle="bold"tools:text="130-1111-1111" /></RelativeLayout><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/tv_phone_calling_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:layout_marginTop="24dp"android:text="通话中:01:33"android:textColor="@android:color/white"android:textSize="18sp"android:visibility="gone"tools:visibility="visible" /><RelativeLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/tv_phone_hang_up"android:layout_width="wrap_content"android:layout_height="wrap_content"android:drawablePadding="16dp"android:drawableTop="@mipmap/phone_hang_up"android:foreground="?android:attr/selectableItemBackground"android:gravity="center"android:padding="8dp"android:text="挂  断"android:textColor="@android:color/black"tools:visibility="visible" /><TextViewandroid:id="@+id/tv_phone_pick_up"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="50dp"android:layout_toRightOf="@id/tv_phone_hang_up"android:drawablePadding="16dp"android:drawableTop="@mipmap/phone_pick_up"android:foreground="?android:attr/selectableItemBackground"android:gravity="center"android:padding="8dp"android:text="接  听"android:textColor="@android:color/black"android:visibility="gone"tools:visibility="visible" /></RelativeLayout></RelativeLayout>
</LinearLayout>

代码为完全贴出,其他代码可下载DEMO查看

                                                                           -END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/619798.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

仿真机器人-深度学习CV和激光雷达感知(项目2)day01

文章目录 前言项目介绍功能与技术简介硬件要求环境配置虚拟机运行项目demo 前言 &#x1f4ab;你好&#xff0c;我是辰chen&#xff0c;本文旨在准备考研复试或就业 &#x1f4ab;本文内容是我为复试准备的第二个项目 &#x1f4ab;欢迎大家的关注&#xff0c;我的博客主要关注…

如何给AI下达精准的指令,哪些提示词对于AI是有效的?

刚上手那会&#xff0c;我倾向于将 prompt 翻译为“指令”&#xff0c;但这并不精确。“指令”通常对应instructions&#xff0c;属于 prompt 中的纯指令部分&#xff0c;通常是一个动宾结构&#xff08;做什么&#xff09;。剩下的部分更多是描述&#xff08;describe&#xf…

Open3D 不规则点云体积计算 (15)

Open3D 不规则点云体积计算 (15) 一、算法介绍二、算法实现1.代码2.结果黑暗笼罩万物,我将是黑暗中最后的那道曙光,以雷霆,击碎黑暗!!! 一、算法介绍 点云往往是不规则的,利用别的包围盒方法获取的体积可能不太准确,如果希望获取更准确的体积,这里介绍一种基于体素…

stable-diffusion 学习笔记

从效果看Stable Diffusion中的采样方法 参考&#xff1a;Ai 绘图日常 篇二&#xff1a;从效果看Stable Diffusion中的采样方法_软件应用_什么值得买 大概示例&#xff1a;

arm64架构编译electron长征路

文章目录 1. gn工具生成1.1 问题,找不到last_commit_position.h文件问题描述如下:解决方法1.2 ninja文件不是对应架构问题问题描述:解决方法1.3 问题3:clang++找不到问题描述解决方法2. electron 编译参数生成2.1 下载对应版本debian_bullseye_arm64-sysroot错误描述

Linux:信号

目录 1.信号 2.信号的过程 a.信号的产生 1:键盘产生, 异常产生 2:系统调用产生信号 3.软件条件产生信号 4.硬件异常产生信号 b.信号的发送 c.信号的处理 d.总结与思考 3.信号保存 1.信号及其它相关常见概念 2.在内核中的表示 3.sigset_t 4. 信号集操作函数 4.信…

【读书笔记】网空态势感知理论与模型(十)

网络安全的认知科学&#xff1a;一个推进社会-网络系统研究的框架 1.引言 网空安全理念、策略和操作的核心是对抗性的规则&#xff0c;对于攻击方来说&#xff0c;这个规则会推动一个威胁去夺取重要数据或文件的所有权。 2. 网空安全作为一个跨学科的超系统&#xff0c;其中…

Arduino开发实例-AS608光学指纹传感器驱动

AS608光学指纹传感器驱动 文章目录 AS608光学指纹传感器驱动1、AS608光学指纹传感器介绍2、硬件准备及接线3、代码实现3.1 指纹录入3.2 指纹匹配验证1、AS608光学指纹传感器介绍 AS608 光学指纹传感器可用于扫描指纹,它也可以通过串行通信将处理后的数据发送到微控制器。 所有…

50天精通Golang(第16天)

beego框架介绍和流程分析 beego官方文档&#xff1a;https://beego.me/ 一、beego框架介绍 1.1 beego框架介绍–beego简介 1.1.1 什么是beego beego是一个使用Go语言来开发WEB引用的GoWeb框架&#xff0c;该框架起始于2012年&#xff0c;由一位中国的程序员编写并进行公开…

gem5学习(12):理解gem5 统计信息和输出——Understanding gem5 statistics and output

目录 一、config.ini 二、config.json 三、stats.txt 官方教程&#xff1a;gem5: Understanding gem5 statistics and output 在运行 gem5 之后&#xff0c;除了仿真脚本打印的仿真信息外&#xff0c;还会在根目录中名为 m5out 的目录中生成三个文件&#xff1a; config.i…

第六篇 提升网页性能:深入解析HTTP请求优化策略(一)

深入浅出HTTP请求前后端交互系列专题 第一章 引言-HTTP协议基础概念和前后端分离架构请求交互概述 第二章 HTTP请求方法、状态码详解与缓存机制解析 第三章 前端发起HTTP请求 第四章 前后端数据交换格式详解 第五章 跨域资源共享&#xff08;CORS&#xff09;&#xff1a;现代W…

水仙花数(Java解法)

什么是水仙花数&#xff1f; 水仙花数是指一个 3 位数&#xff0c;它每位上的数字的 3 次幂之和等于它本身&#xff08;例如&#xff1a; 1 5 3 153 &#xff09;&#xff0c;水仙花数的取值范围在 100~1000 之间。 解题思路&#xff1a; 这个题需要把所以的数字都拿到&…

Egg框架搭建后台服务【2】

前言 接上文 Egg框架搭建后台服务【1】&#xff0c;继续优化后台服务&#xff0c;之前直接用 SQL 语句调用的数据库数据&#xff0c;既不安全&#xff0c;也比较麻烦&#xff0c;当然最重要的是“显着不专业”。 所以本文仍然是增删改查&#xff0c;重点是将原本 SQL 语句操作…

【QT】QMessageBox 弹出消息框,对话确认框(确定/取消)

1.无互动 QMessageBox::information(nullptr,"信息","登陆成功");2.互动&#xff1a;确定、取消 QMessageBox::StandardButton box; box QMessageBox::question(this, "提示", "确定要添加吗?", QMessageBox::Yes|QMessageBox::…

8个Linux软件包管理命令

软件包管理器允许在 Linux 发行版上轻松安装、更新和删除软件。常用的软件包管理器包括 APT、YUM、DNF、Pacman 和 Zypper。 1. apt – Debian/Ubuntu 软件包管理器 apt 命令使用 APT 软件库管理 Debian/Ubuntu 系统上的软件包。它允许安装、更新和删除软件包。 例子&#x…

Linux 使用小记

安装IDEA mkdir -p /home/app/idea tar -zxvf ideaIU-2022.3.3.tar.gz -C /home/app/idea cd /usr/local/IDEA/idea-IU-223.8836.41/bin sh ./idea.sh 配置 IDEA 快捷方式 sudo gedit /usr/share/applications/idea.desktop 写入下面的内容 [Desktop Entry] NameIntelliJ …

大模型学习读书笔记01——大模型基础

大模型学习读书笔记01——大模型基础 1、什么是语言模型 语言模型 评判由一些单词排列组合而成的句子是否更像真正的、自然的句子。&#xff08;通俗的说是否像人话&#xff09; 语言模型的经典定义是一种对词符&#xff08;token&#xff09;序列的概率分布。每个token在真…

Docker 镜像的详解及创建(Dockerfile详解)

目录 镜像加载的原理 联合文件系统&#xff08;UnionFS&#xff09; 镜像结构的分层 Dockerfile Dockerfile结构 dockerfile常用命令 Dockerfile 编写规范 docker创建镜像的方法 基于现有镜像创建 示例&#xff1a; 基于本地模版创建 示例 基于Dockerfile 创建 示…

关于基于STM32使用外部中断控制按键

关于基于STM32使用外部中断控制按键的相关论文&#xff0c;虽然我不能直接提供具体的论文全文&#xff0c;但可以为您描述一下这类论文可能涉及的内容和框架&#xff1a; 标题&#xff1a;《基于STM32微控制器的外部中断系统在按键控制应用中的设计与实现》 摘要&#xff1a;…

AcWing 846. 树的重心(dfs)

这是一道我一开始没怎么看懂的题目&#xff0c;然后后面看了y神的讲解就豁然开朗了 不过我们首先要有先置知识来理解这道题目 先置知识 邻接表&#xff1a;是一种表示图的数据结构&#xff0c;它通过链表的方式记录每个顶点及其相邻的顶点。在这个具体的问题中&#xff0c;使…