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…

stable-diffusion 学习笔记

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

Linux:信号

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

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

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

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…

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

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

RAG代码实操之斗气强者萧炎

&#x1f4d1;前言 本文主要是【RAG】——RAG代码实操的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一句&#x…

Netty 介绍、使用场景及案例

Netty 介绍、使用场景及案例 1、Netty 介绍 https://github.com/netty/netty Netty是一个高性能、异步事件驱动的网络应用程序框架&#xff0c;用于快速开发可扩展的网络服务器和客户端。它是一个开源项目&#xff0c;最初由JBoss公司开发&#xff0c;现在由社区维护。Netty的…

企业网络出口部署案例

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系&#xff01; 厦门微思网络​​​​​​ https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom Linux\RHCE\RHCE 9.0\RHCA\ Oracle O…

myql进阶-一条查询sql在mysql的执行过程

目录 1. 流程图 2. 各个过程 2.1 连接器 2.2 分析器 2.3 优化器 2.4 执行器 2.5 注意点 1. 流程图 2. 各个过程 假设我们执行一条sql语句如下&#xff1a; select * from t_good where good_id 1 2.1 连接器 首先我们会和mysql建立连接&#xff0c;此时就会执行到连接…

C++20结构化绑定应用实例(二百五十六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

猫头虎分享:探索TypeScript的世界 — TS基础入门 ‍

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通Golang》…

Web前端 ---- 【Vue3】vue3中的组件传值(props、自定义事件、全局事件总线)

目录 前言 props 自定义事件 全局事件总线 安装第三方库mitt 封装event-bus.js文件 使用全局事件总线 清除全局事件绑定 前言 本文介绍在vue3中的组件传值&#xff0c;props、自定义事件以及全局事件总线。相较于vue2中&#xff0c;略有变化。关于vue2中的组件传值看这篇…

鸿蒙(HarmonyOS)应用开发指南

1. 概述 1.1 简介 鸿蒙&#xff08;即 HarmonyOS &#xff0c;开发代号 Ark&#xff0c;正式名称为华为终端鸿蒙智能设备操作系统软件&#xff09;是华为公司自 2012 年以来开发的一款可支持鸿蒙原生应用和兼容 AOSP 应用的分布式操作系统。该系统利用“分布式”技术将手机、电…

windows11通过虚拟机安装Ubuntu20.04

VMware 分为 VMware Workstation Pro 和 VMware Workstation Player, Pro体验期后收费&#xff0c;Player则免费。player 早期不能创建虚拟机&#xff0c;只能Pro创建好后给Player运行&#xff0c;而现在player早已加入创建虚拟机功能&#xff0c;所以使用体验上两者相差不大&a…

物联网协议Coap之Core和NetWork简介

目录 前言 一、Coap的Core包 1、Coap对象 2、Message对象 3、Request对象 4、Response对象 二、Coap的NetWork调试 1、UDP运行模式 2、Network消息接收 3、Sender线程发送数据 三、总结 前言 在之前的博文中&#xff0c;对Californium中Coap的实现进行了简要的介绍&a…

swing快速入门(四十)JList、JComboBox实现列表框

注释很详细&#xff0c;直接上代码 上一篇 新增内容 &#x1f9e7;1.列表的属性设置与选项监听器 &#x1f9e7;2.下拉框的属性设置与选项监听器 &#x1f9e7;3.Box中组件填充情况不符合预期的处理方法 &#x1f9e7;4.LIst向Vector的转化方法 源码&#xff1a; package swing…

OpenCV 基于C++图像读取及存储API函数

OpenCV可以从存储介质中读入图像&#xff0c;也可以将摄像头(Camera)抓取的图像载入内存&#xff0c;然后进行处理。而存储图像就是将内存中的图像数据写入存储介质中&#xff0c;如写入硬盘、优盘等。 OpenCV要读入图像、操作图像。首先要用到Mat类&#xff0c;并且需创建Mat对…

理想汽车迎来新算力平台负责人,内部化名为张一粟;王者荣耀在抖音直播全面开放;陈楚生等人现身央视春晚彩排

今日精选 • 理想汽车迎来新算力平台负责人,内部化名为张一粟。目前理想内部暂未公布其内部职级• 王者荣耀在抖音直播全面开放• 陈楚生等人现身央视春晚彩排 投融资 • 2023年12月份&#xff0c;中国社会融资规模增量为1.94万亿元• OpenAI 支持的人形机器人公司 1X 完成 …