最近做一个关于来去电监听然后挂断发短信功能的项目,碰到不知如何判断去电是否接通的问题,多方查询,网上的答案不一而足,最后 ,在借鉴网上的答案和自己的修改后,得出解决方案记录如下:
这个好判断。
1. 当为来电时,电话状态首先进入TelephonyManager.CALL_STATE_RINGING 也就是 响铃 状态
2. 接通时 进入 TelephonyManager.CALL_STATE_OFFHOOK 状态,也就是接通状态
3. 当挂断时,进入TelephonyManager.CALL_STATE_IDLE 状态 ,也就是挂断状态。
所以,我们只需判断电话状态由 RINGING—>OFFHOOK时,就可以知道电话接通了。
但是当我们是拨出电话时,一拨的时候电话就会处于OFFHOOK状态,接通之后也是OFFHOOK状态,那咋办呢??
多方查询,最后通过查询 calllog 也就是通话记录的方法,来判断电话是否拨通!
然后就可以查询到通话记录中的duration ,这就是最关键的,当duration>0的时候就说明电话接通了哈哈哈是不是也不难?!
光说不练假把式 且看代码!
下面给出我整个类的代码,里面关于数据库啊什么的,各位就不用深究,关键看如何得到去电是否接通。
注释也较详细,各位有不懂,欢迎留言或私信
public class EndCallReceiver extends BroadcastReceiver {
private RefuseComeMsgDao refuseComeMsgDao;
private String content;
@Override
public void onReceive(final Context context, Intent intent) {
//获取来电号码
final String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
TelephonyManager tm = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
int phoneState = tm.getCallState();
switch (phoneState) {
//如果电话的状态是来电响铃
case TelephonyManager.CALL_STATE_RINGING:
LogUtil.log("响铃!!!!");
// 这里保存的是如果是来电响铃 则说明当前是来电 保存为TRUE
context.getSharedPreferences("blacklist", MODE_PRIVATE).edit().putBoolean("iscome", true).commit();
context.getSharedPreferences("blacklist", MODE_PRIVATE).edit().putBoolean("offhook", false).commit();
//这里设置匹配黑名单号码 或者此时是否处于拒接时间段
if (isInBlackList(number, context) || DateUtil.isRefuse(context)) {
Class telephonyManagerClass = TelephonyManager.class;
try {
//通过反射获取getITelephony方法
Method method = telephonyManagerClass.getDeclaredMethod("getITelephony", new Class[0]);
//设置该方法可访问
method.setAccessible(true);
//调用getITelephony方法获取ITelephony的实例
ITelephony itelephony = (ITelephony) method.invoke(tm, new Object[]{});
//挂断电话
itelephony.endCall();
//由黑名单或者拒接时间段挂断的电话,存储状态以防止重复发送短信
//这里储存的状态true 表示 是主动拒接的!
context.getSharedPreferences("refuseTime", MODE_PRIVATE).edit()
.putBoolean("dorefuse", true)
.commit();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
case TelephonyManager.CALL_STATE_IDLE: //电话状态 挂断, 在这里发送 来去电短信
LogUtil.log("挂断!!");
//如果是来电 发送当前来电模版短信
SharedPreferences preferences = context.getSharedPreferences("blacklist", MODE_PRIVATE);
SharedPreferences preferences2 = context.getSharedPreferences("refuseTime", MODE_PRIVATE);
boolean iscome = preferences.getBoolean("iscome", false);//是否为来电
if (iscome) { //------------------------如果是来电----------------------------
//--------如果是通过黑名单或者时间段拒绝的 发送拒接短信-------
if (preferences2.getBoolean("dorefuse", false)) { //这个if里判断是否为主动拒接!!
//先得到当前设置的拒接短信
DaoSession daoSession = DBUtil.initDb(new GreenDaoContext());
refuseComeMsgDao = daoSession.getRefuseComeMsgDao();
RefuseComeMsg refuse1 = refuseComeMsgDao.queryBuilder().where(RefuseComeMsgDao.Properties.Type.eq("refuse")).build().unique();
if (refuse1 == null) {
content = "";
} else {
content = refuse1.getContent();
}
//发送拒接短信
if (!content.equals("") && !content.equals("不发送")) {
sendSMS(number, content, context);
}
// 拒接状态恢复
preferences2.edit().putBoolean("dorefuse", false).commit();
} else { // ---------否则发送来电短信---------
String content = doGetComeMsg();//得到当前的来电回复短信
if (!content.equals("不发送")) {
sendSMS(number, content, context);
}
preferences.edit().putBoolean("iscome", false).commit();
}
} else if (!iscome) {
//--------------------如果是去电 发送去电短信--------------------//
/*休眠一秒 等待通话记录写入数据库
* 如果不休眠这一秒 直接查询数据库 会查询不到当前打的这个电话的记录
* 因为在系统将通话记录写入数据库之前就开始查询操作 所以查到的最近的记录实际上是上一次拨打的记录
* 因此休眠一秒 可能在性能垃圾的手机上1s不够 ? 不至于把 !
* */
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
boolean callLogState = getCallLogState(context,number);
if (callLogState) {
//----------------去电接通 执行发送去电短信操作!--------------------
String content = doGetOutMsg(); //得到当前的去电回复短信
if (!content.equals("") && !content.equals("不发送")) {
sendSMS(number, content, context);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
LogUtil.log("OFFHOOK");
break;
}
}
/**
* 获得当前的去电回复短信
* @return 当前设置的去电回复模板
*/
private String doGetOutMsg() {
String string;
DaoSession daoSession = DBUtil.initDb(GreenDaoContext.getInstance());
Database database = daoSession.getDatabase();
CurrentTemplateDaoDao currentTemplateDaoDao = daoSession.getCurrentTemplateDaoDao();
currentTemplateDaoDao.createTable(database, true); //无表就建表
//查询数据库中type为out的数据
CurrentTemplateDao out = currentTemplateDaoDao.queryBuilder().where(CurrentTemplateDaoDao.Properties.Type.eq("out")).build().unique();
if (out != null) {
string = out.getContent();
} else {
string = "不发送";
}
return string;
}
/**
* 获得当前的来电回复短信
* @return 当前设置的来电模板内容
*/
private String doGetComeMsg() {
String string;
DaoSession daoSession = DBUtil.initDb(GreenDaoContext.getInstance());
Database database = daoSession.getDatabase();
CurrentTemplateDaoDao currentTemplateDaoDao = daoSession.getCurrentTemplateDaoDao();
currentTemplateDaoDao.createTable(database, true); //无表就建表
//查询数据库中type为come的数据
CurrentTemplateDao come = currentTemplateDaoDao.queryBuilder().where(CurrentTemplateDaoDao.Properties.Type.eq("come")).build().unique();
if (come != null) {
string = come.getContent();
} else {
string = "不发送";
}
return string;
}
//判断号码是否在黑名单中
private boolean isInBlackList(String number, Context context) {
List blackLists = DBUtil.getBlackListDao().loadAll();
for (BlackList blackList : blackLists) {
if (blackList.getNumber().equals(number)) {
//匹配成功
return true;
}
}
return false;
}
private void sendSMS(String phoneNum, String message, Context context) {
//在这里判断此号码在设置重复时段内是否重复
//获取短信管理器
android.telephony.SmsManager smsManager = android.telephony.SmsManager.getDefault();
//拆分短信内容(手机短信长度限制)
List divideContents = smsManager.divideMessage(message);
for (String text : divideContents) {
/**
* 参数4和5:
* sentIntent——如果不为空,当消息成功发送或失败这个PendingIntent就广播。结果代码是Activity.RESULT_OK表示成功,或RESULT_ERROR_GENERIC_FAILURE、RESULT_ERROR_RADIO_OFF、RESULT_ERROR_NULL_PDU之一表示错误。对应RESULT_ERROR_GENERIC_FAILURE,sentIntent可能包括额外的“错误代码”包含一个无线电广播技术特定的值,通常只在修复故障时有用。
* 每一个基于SMS的应用程序控制检测sentIntent。如果sentIntent是空,调用者将检测所有未知的应用程序,这将导致在检测的时候发送较小数量的SMS。
* deliveryIntent——如果不为空,当消息成功传送到接收者这个PendingIntent就广播。
*/
smsManager.sendTextMessage(phoneNum, null, text, null, null);
//将发送的信息添加到 发送记录 数据库中
DBUtil.initDb(new GreenDaoContext()).getSendRecordDao().insert(new SendRecord(null, phoneNum, DateUtil.getDate(), message));
}
}
private boolean getCallLogState(Context context,String number) {
boolean isLink = false;
ContentResolver cr = context.getContentResolver();
PermissionChecker.checkSelfPermission(context, Manifest.permission.READ_CALL_LOG);
final Cursor cursor = cr.query(CallLog.Calls.CONTENT_URI,
new String[]{CallLog.Calls.NUMBER,CallLog.Calls.TYPE,CallLog.Calls.DURATION},
CallLog.Calls.NUMBER +"=?",
new String[]{number},
CallLog.Calls.DATE + " desc");
int i = 0;
while(cursor.moveToNext()){
if (i == 0) {//第一个记录 也就是当前这个电话的记录
int durationIndex = cursor.getColumnIndex(CallLog.Calls.DURATION);
long durationTime = cursor.getLong(durationIndex);
// Log.d("test", "getCallLogState: -----------------duration= " + durationTime);
if(durationTime > 0){
LogUtil.log("到这里了 这是if里 durationTime = "+durationTime);
isLink = true;
} else {
LogUtil.log("到这里了 这是else里");
isLink = false;
}
}
i++;
// int durationIndex = cursor.getColumnIndex(CallLog.Calls.DURATION);
// long durationTime = cursor.getLong(durationIndex);
}
return isLink;
}
}
**代码中的获取电话状态是通过AIDL实现的。
代码不是啥完美的代码,有瑕疵也欢迎吐槽。
以上。**