Android 实现手机接通电话后振动提示的功能
作者:lqh 发布时间:2022-01-10 23:56:58
有些手机在电话接通后会有振动提示,这有个好处就是可以等到接通后再放到耳边接听,减少辐射。本文就讲讲如何在Android手机中实现这种接通电话后的振动提示功能,这里主要针对拨出的电话。
Android SDK提供的通话状态
很明显,要在电话接通的时候产生振动提示,首先需要知道电话在何时被接通。而Android SDK并没有给出直接读取这种状态的方法。下面是Android SDK的电话服务类TelephonyManager提供的三种电话状态:
CALL_STATE_IDLE 空闲状态
CALL_STATE_OFFHOOK 摘机状态
CALL_STATE_RINGING 响铃状态
这几个状态很容易理解:摘机状态即拿起话筒(对于座机电话而言的动作),但这个状态可能发生在拨入电话接通时,也可能是拨出电话时,但是却不能说明拨出电话接通时。通过以上3种状态我们仅能组合出挂机和来电接通这两个状态。而今天我们要实现的功能却无法做到。
看来我们需要寻找其他方法来实现了,SDK靠不住啊……
Android运行log分析
还好Android在运行时会有大量的log产生,看看我们能不能从这上面找到突波口呢?我们选择Android的Radio模块的日志来分析。首先我们需要写一段代码来读取Radio相关的log,读取log就不得不用到logcat了。
Process process;
InputStream inputstream;
BufferedReader bufferedreader;
try {
process = Runtime.getRuntime().exec("logcat -v time -b radio");
inputstream = process.getInputStream();
InputStreamReader inputstreamreader = new InputStreamReader(
inputstream);
bufferedreader = new BufferedReader(inputstreamreader);
String str = "";
while ((str = bufferedreader.readLine()) != null) {
log.i("mLogcat",str);
}
} catch (Exception e) {
}
另外,要让程序能够读取系统log需要指定权限,在AndroidManifest.xml文件中加入一下内容。
XML/HTML代码
<uses-permission android:name="android.permission.READ_LOGS"></uses-permission>
通过上面这段代码我们就可以将Radio的log输出到了,这样我们就可以通过在DDMS中查看这些log,分析其中的通话过程。具体抓到的log就不贴出来了,大家可以自己编写程序通过上面的代码来抓取和分析。我只说一下我的分析结果。
通过分析log发现了一些蛛丝马迹。其中有几条日志很有用:
GET_CURRENT_CALLS id=1,DIALING
GET_CURRENT_CALLS id=1,ALERTING
GET_CURRENT_CALLS id=1,ACTIVE
由于log较长我只拿了每条log的开头部分,真实的会多很多内容。当我们拨出电话的时候,会输入这么几条log。
拨号->提醒->活动
大致是这么个过程。经过几次测试发现,电话接通时会进入活动状态,并会输出:GET_CURRENT_CALLS id=1,ACTIVE 这条log,至此我们已经接近成功了。
不过之后我又发现在拨号开始到电话接通这段时间内会经过多次的“拨号->提醒->活动”这样的状态变化,仅当话筒中嘟声响起后GET_CURRENT_CALLS这条日志会锁定在ALERTING。在电话接通前便不再出现GET_CURRENT_CALLS日志了。
可能上面的这段表述大家不是很清楚,换句话说在通话接通之前会出现多次的GET_CURRENT_CALLS ACTIVE 这样的日志,而仅有一次是电话接通产生的。这就给我们造成了麻烦。不能只是单纯的抓取GET_CURRENT_CALLS ACTIVE 这样的信息来判断了。
我们只能通过一些逻辑上的判断来实现了。
实例代码讲解
下面看我的代码:
class TestThread implements Runnable {
//振动器
Vibrator mVibrator;
//电话服务
TelephonyManager telManager;
public TestThread(Vibrator mVibrator, TelephonyManager telManager) {
this.mVibrator = mVibrator;
this.telManager = telManager;
}
@Override
public void run() {
//获取当前话机状态
int callState = telManager.getCallState();
Log.i("TestService", "开始.........." + Thread.currentThread().getName());
//记录拨号开始时间
long threadStart = System.currentTimeMillis();
Process process;
InputStream inputstream;
BufferedReader bufferedreader;
try {
process = Runtime.getRuntime().exec("logcat -v time -b radio");
inputstream = process.getInputStream();
InputStreamReader inputstreamreader = new InputStreamReader(
inputstream);
bufferedreader = new BufferedReader(inputstreamreader);
String str = "";
long dialingStart = 0;
boolean enableVibrator = false;
boolean isAlert = false;
while ((str = bufferedreader.readLine()) != null) {
//如果话机状态从摘机变为空闲,销毁线程
if (callState == TelephonyManager.CALL_STATE_OFFHOOK
&& telManager.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
break;
}
// 线程运行5分钟自动销毁
if (System.currentTimeMillis() - threadStart > 300000) {
break;
}
Log.i("TestService", Thread.currentThread().getName() + ":"
+ str);
// 记录GSM状态DIALING
if (str.contains("GET_CURRENT_CALLS")
&& str.contains("DIALING")) {
// 当DIALING开始并且已经经过ALERTING或者首次DIALING
if (!isAlert || dialingStart == 0) {
//记录DIALING状态产生时间
dialingStart = System.currentTimeMillis();
isAlert = false;
}
continue;
}
if (str.contains("GET_CURRENT_CALLS")
&& str.contains("ALERTING")&&!enableVibrator) {
long temp = System.currentTimeMillis() - dialingStart;
isAlert = true;
//这个是关键,当第一次DIALING状态的时间,与当前的ALERTING间隔时间在1.5秒以上并且在20秒以内的话
//那么认为下次的ACTIVE状态为通话接通.
if (temp > 1500 && temp < 20000) {
enableVibrator = true;
Log.i("TestService", "间隔时间....." + temp + "....."
+ Thread.currentThread().getName());
}
continue;
}
if (str.contains("GET_CURRENT_CALLS") && str.contains("ACTIVE")
&& enableVibrator) {
mVibrator.vibrate(100);
enableVibrator = false;
break;
}
}
Log.i("TestService", "结束.........."
+ Thread.currentThread().getName());
} catch (Exception e) {
// TODO: handle exception
}
}
}
我的这个方法比较牵强,是通过判断第一次DIALING与每一次ALERTING之间的间隔,如果间隔大于1.5秒,那么认为已经进入了“嘟”声提示的时候了,那么下一个ACTIVE将是电话接通。这个1.5秒是通过分析日志得出的。但是这种方法我始终觉得不太靠谱。如果大家有好的方法可以交流交流。
剩下的就是让这个线程在电话拨出时触发,并且常驻在电话中时候准备这就可以了。可以采用Service配合Receiver来实现。Service来实现常驻,Receiver来实现监听拨出电话。基本就可以完成我们想要的功能了。
以上代码我都测试过,99%有效,哈哈。这里面提到了一些Android的基础内容,像logcat、Service、Receiver,这些如果大家不了解的话可以找相关文章资料学习下。
通过此文希望能帮助Android 开发的朋友,谢谢大家对本站的支持!


猜你喜欢
- 引言对使用 lombok 还是有很多争议的,有些公司不建议使用,有些公司又大量使用。我们的想法是:可以使用,但是不要滥用。什么是 lombo
- RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样。RP
- Android 调用系统相机拍摄获取照片的两种方法实现实例在我们Android开发中经常需要做这个一个功能,调用系统相机拍照,然后获取拍摄的
- 前端网络访问,主流方案就是 Ajax,Vue 也不例外,在 Vue2.0 之前,网络访问较多的采用 vue-resources,Vue2.0
- 实例描述现有某班学生的两份成绩,两份成绩中存在一些不一致的记录。需借助于编程方法找出这些不一致的记录。实例代码using System;us
- 你是一名体育老师,在某次课距离下课还有五分钟时,你决定搞一个游戏。此时有100名学生在上课。游戏的规则是:1. 你首先说出三个不同的特殊数,
- 一、过滤器(filter)过滤器处于客户端与Web资源(Servlet、JSP、HTML)之间,客户端与Web资源之间的请求和响应都要通过过
- Profile多环境配置我们在开发项目时,通常同一套程序会被发布到几个不同的环境,比如:开发、测试、生产等。其中每个环境的数据库地址、red
- 写了一个java数组排序示例,这里分享给大家共同学习package com.yonyou.test;import java.util.Arr
- 前言当同一类型的很多对象组成一个树结构的时候,可以考虑使用组合模式,组合模式涉及三个类:Component接口:定义树的各个节点的一些操作L
- 在某种场景下,可能我们需要获取app的图标名称和启动图片的名称。比如说app在前台时,收到了远程通知但是通知栏是不会有通知提醒的,这时我想做
- 本文以实例代码实现了C#根据数字序号输出星期几,用户可通过输入数字0~6,输出星期各天的英语单词,程序中主要是演示if语句和switch语句
- 本文实例讲述了Java删除二叉搜索树最大元素和最小元素的方法。分享给大家供大家参考,具体如下:在前面一篇《Java二叉搜索树遍历操作》中完成
- 1.加入jackson的jar包2.在响应的方法上加上@ResponseBody:把java对象转化为json对象3.方法的返回值可以是对象
- 本文实例讲述了Java面向接口编程之简单工厂模式。分享给大家供大家参考,具体如下:一 代码interface Output{ /
- 以下内容归纳了通过Java程序打印PDF文档时的3种情形。即:1 静默打印2 显示打印对话框打印3 打印PDF时自定义纸张大小使用工具:Sp
- 前言对于初学者们来说,刚开始编写Java代码时,会遇到很多困难,下面来说一个比较常见的错误,如下:初学者一般都是从Hello,World开始
- 总之是用jdbc 的游标移动package com.sp.person.sql.util; import java.sql.Connecti
- 本文实例讲述了Java 8新增的方法参数反射。分享给大家供大家参考,具体如下:一 点睛Java 8在java.lang.reflect包下新
- 前言 前一段时间得闲的时候优化了一下我之前的轮子[DotNetCoreRpc]小框架,其中主要