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

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

判断来电是否接通

这个好判断。
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实现的。
代码不是啥完美的代码,有瑕疵也欢迎吐槽。
以上。**

你可能感兴趣的:(android,高级之旅)