android 高级之旅 (十三) 真! 如何判断去电是否接通? 已解决!

2019-04-13 13:37发布

最近做一个关于来去电监听然后挂断发短信功能的项目,碰到不知如何判断去电是否接通的问题,多方查询,网上的答案不一而足,最后 ,在借鉴网上的答案和自己的修改后,得出解决方案记录如下:

判断来电是否接通

这个好判断。
1. 当为来电时,电话状态首先进入TelephonyManager.CALL_STATE_RINGING 也就是 响铃 状态
2. 接通时 进入 TelephonyManager.CALL_STATE_OFFHOOK 状态,也就是接通状态
3. 当挂断时,进入TelephonyManager.CALL_STATE_IDLE 状态 ,也就是挂断状态。 所以,我们只需判断电话状态由 RINGING—>OFFHOOK时,就可以知道电话接通了。 但是当我们是拨出电话时,一拨的时候电话就会处于OFFHOOK状态,接通之后也是OFFHOOK状态,那咋办呢??

判断去电是否接通

多方查询,最后通过查询 calllog 也就是通话记录的方法,来判断电话是否拨通!
  • 本来我也对 calllog了解不多,这里给个传送门,不太了解的同学可以看看关于这方面的API 。
  • 因为android平台上的通话记录是以Content Provider的形式存储在手机上的,因此你需要使用ContentResolver来查询通话记录,返回Cursor接口。
  • 然后通过这个contentresolver 来查询通话记录的数据库 ,得到一个游标 cursor,然后通过这个游标得到我们想要的通话记录
  • 然后就可以查询到通话记录中的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实现的。
代码不是啥完美的代码,有瑕疵也欢迎吐槽。
以上。**