Android来电监听

Posted 摸爬滚打的程序媛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android来电监听相关的知识,希望对你有一定的参考价值。


⭐ 最近项目中有个需求是:监听手机来电,如果是来电号码是本地已存储的联系人,则显示本地联系人的名字;否则,就直接显示来电号码。

🖊🖊🖊因为这个需求平时遇到的少,所以这里记录一下🖊🖊🖊。

1 依据来电号码查询本地联系人

先来看看如何根据来电号码查询本地联系人呢?

1.1 权限

这里需要访问手机通讯录的权限READ_CONTACTS,分别在配置文件中静态申请以及代码中动态申请。

<!--读取手机通讯录-->
<uses-permission android:name="android.permission.READ_CONTACTS"/>

动态权限申请在后面的代码中。

1.2 实现

	/**
     * 获取来电号码对应的联系人名称
     * @param context        上下文
     * @param phoneNumber   电话号码
     * @return    联系人名称(如果来电号码没有对应联系人名称,则直接返回电话号码)
     */
    public static String getIncomingCallName(Context context,String phoneNumber)
        //格式化电话号码
        if(phoneNumber == null)
            return null;
        
        phoneNumber = phoneNumber.trim().replace(" ","").replace("-","");

        //是否查询到
        boolean isSuccess = false;
        String phoneName = null;

        //联系人的Uri,也就是content://com.android.contacts/contacts
        Uri uri = ContactsContract.Contacts.CONTENT_URI;
        //指定获取_id和display_name两列数据,display_name即为姓名
        String[] projection = new String[]
                ContactsContract.Contacts._ID,
                ContactsContract.Contacts.DISPLAY_NAME
        ;
        //根据Uri查询相应的ContentProvider,cursor为获取到的数据集
        Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);

        if (cursor != null && cursor.moveToFirst()) 
            do 
                try 
                    Long id = cursor.getLong(0);
                    //获取姓名
                    String name = cursor.getString(1);
                    //指定获取NUMBER这一列数据
                    String[] phoneProjection = new String[]
                            ContactsContract.CommonDataKinds.Phone.NUMBER
                    ;

                    //根据联系人的ID获取此人的电话号码
                    Cursor phonesCursor = context.getContentResolver().query(
                            ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                            phoneProjection,
                            ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=" + id,
                            null,
                            null);

                    //因为每个联系人可能有多个电话号码,所以需要遍历
                    if (phonesCursor != null && phonesCursor.moveToFirst()) 
                        do 
                            String num = phonesCursor.getString(0);
                            num = num.trim().replace(" ","").replace("-","");
                            if(num.equals(phoneNumber))
                                //查询到号码
                                isSuccess = true;
                                phoneName = name;
                                break;  //退出
                            
                         while (phonesCursor.moveToNext());
                    

                    //已经找到,退出大循环
                    if(isSuccess)
                        break;
                    

                catch (Exception e) 
                    e.printStackTrace();
                
             while (cursor.moveToNext());
        

        //如果来电号码没有对应联系人名称,则直接返回电话号码
        if(isSuccess) 
            return phoneName;
        else
            return phoneNumber;
        
    

2 来电监听方法(有两种)

监听手机来电的方法一般有两种:使用系统广播监听 和 使用PhoneStateListener 监听器
下面分别来讲解这两种方法的使用。

2.1 系统广播监听

2.1.1 权限

通过监听广播的方式来监听来电时,必须需要以下两个权限.READ_PHONE_STATE和READ_CALL_LOG,因为是危险权限,除了静态表明外,Android6.0以后还必须要动态申请。

<!--读取手机状态-->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<!--读取手机通话记录:Android 9.0没有此权限,获取不到来电号码-->
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<!--读取手机通讯录-->
<uses-permission android:name="android.permission.READ_CONTACTS"/>

动态申请权限(此处使用郭霖的开源库PermissionX权限库进行权限申请)

//PermissionX权限库
implementation 'com.permissionx.guolindev:permissionx:1.4.0'
//广播监听方式监听来电,需要检测的权限
    private void checkBroadcastCallPermission() 
        PermissionX.init(this)
                .permissions(   //传入需要申请的权限
                        Manifest.permission.READ_PHONE_STATE,
                        Manifest.permission.READ_CONTACTS,
                        Manifest.permission.READ_CONTACTS)
                .onExplainRequestReason(new ExplainReasonCallbackWithBeforeParam() 
                    @Override
                    public void onExplainReason(ExplainScope scope, List<String> deniedList, boolean beforeRequest) 
                        scope.showRequestReasonDialog(deniedList, "来电监听需要授予'获取设备信息和访问手机通话记录'的权限,依据来电号码查询联系人需要'读取手机通讯录'权限,否则应用无法正常使用", "我已明白");
                    
                )
                .onForwardToSettings(new ForwardToSettingsCallback() 
                    @Override
                    public void onForwardToSettings(ForwardScope scope, List<String> deniedList) 
                        scope.showForwardToSettingsDialog(deniedList, "您需要去应用程序设置当中手动开启权限", "我已明白");
                    
                )
                .request(new RequestCallback() 
                    @Override
                    public void onResult(boolean allGranted, List<String> grantedList, List<String> deniedList) 
                        if (allGranted) 
                            Log.d(TAG, "所有申请的权限都已通过");
                         else 
                            Toast.makeText(MainActivity.this, "您拒绝了如下权限:" + deniedList, Toast.LENGTH_SHORT).show();
                        
                    
                );

    

2.1.2 注册电话状态系统广播接收器

	 //电话状态的广播
    private final String PHONE_STATE_ACTION = "android.intent.action.PHONE_STATE";

    //自定义广播接收器
    private BroadcastReceiver comingCallBR = new BroadcastReceiver() 
        @Override
        public void onReceive(Context context, Intent intent) 
            Log.d(TAG, "onReceive(): action = " + intent.getAction());
            if(intent.getAction() != null
                    && intent.getAction().equals(PHONE_STATE_ACTION))
                //电话状态
                String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
                Log.d(TAG, "onReceive(): phone state = " + state);
                // 电话正在响铃,即来电
                if (state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_RINGING)) 
                    //正在响铃...
                    //来电号码
                    String comingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
                    if(comingNumber == null ||comingNumber.isEmpty())
                        return;
                    
                    Log.d(TAG, "onReceive(): 来电号码 = " + comingNumber);
                    if(comingCallListener != null)
                        comingCallListener.onCalling(curTimeToFormatNoYearMills(),comingNumber,null);
                    
                else if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_OFFHOOK))
                    //正在通话...
                else if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_IDLE))
                    //电话挂断
                

            
        
    ;

    //注册监听来电的广播接收者
    public boolean initBroadcastReceiver()
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(PHONE_STATE_ACTION);
        registerReceiver(comingCallBR, intentFilter);
        return true;
    

    //注销广播
    public boolean unregisterReceiver()
        unregisterReceiver(comingCallBR);
        return true;
    

2.2 PhoneStateListener 监听器

2.2.1 权限

通过PhoneStateListener 监听器的方式来监听来电时,只需要一个权限READ_CALL_LOG,因为是危险权限,除了静态表明外,Android6.0以后同样需要动态申请。

<!--读取手机通话记录:Android 9.0没有此权限,获取不到来电号码-->
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<!--读取手机通讯录-->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
//PhoneStateListener监听方式监听来电,需要检测的权限
    private void checkPhoneStateListenerCallPermission() 
        PermissionX.init(this)
                .permissions(   //传入需要申请的权限
                        Manifest.permission.READ_CALL_LOG,
                        Manifest.permission.READ_CONTACTS)
                .onExplainRequestReason(new ExplainReasonCallbackWithBeforeParam() 
                    @Override
                    public void onExplainReason(ExplainScope scope, List<String> deniedList, boolean beforeRequest) 
                        scope.showRequestReasonDialog(deniedList, "来电监听需要授予'访问手机通话记录'的权限,依据来电号码查询联系人需要'读取手机通讯录'权限,否则应用无法正常使用", "我已明白");
                    
                )
                .onForwardToSettings(new ForwardToSettingsCallback() 
                    @Override
                    public void onForwardToSettings(ForwardScope scope, List<String> deniedList) 
                        scope.showForwardToSettingsDialog(deniedList, "您需要去应用程序设置当中手动开启权限", "我已明白");
                    
                )
                .request(new RequestCallback() 
                    @Override
                    public void onResult(boolean allGranted, List<String> grantedList, List<String> deniedList) 
                        if (allGranted) 
                            Log.d(TAG, "所有申请的权限都已通过");
                            Toast.makeText(MainActivity.this, "权限已授予", Toast.LENGTH_SHORT).show();
                            //权限授予成功之后,开启广播监听
                            ComingCallListenManager.getManager(MainActivity.this).initBroadcastReceiver();
                         else 
                            Toast.makeText(MainActivity.this, "您拒绝了如下权限:" + deniedList, Toast.LENGTH_SHORT).show();
                        
                    
                );
    

2.2.2 初始化PhoneStateListener

PhoneStateListener监听器类,用于监视设备上特定电话状态的变化,包括服务状态、信号强度、消息等待指示器(语音邮件)等。

TelephonyManager 提供对设备上电话服务的信息的访问。应用程序可以使用该类中的方法来确定电话服务和状态,以及访问某些类型的订阅者信息。应用程序还可以注册侦听器来接收电话状态更改的通知。

    // 电话管理者对象
    private TelephonyManager mTelephonyManager;
    // 电话状态监听者
    private PhoneStateListener mPhoneStateListener;

    //初始化PhoneStateListener
    public boolean initPhoneStateListener() 
        mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        mPhoneStateListener = new PhoneStateListener()
            @Override
            public void onCallStateChanged(int state, String phoneNumber) 
                switch(state)
                    case TelephonyManager.CALL_STATE_IDLE:
                        //电话挂断
                        break;
                    case TelephonyManager.CALL_STATE_OFFHOOK:
                        //正在通话...
                        break;
                    case TelephonyManager.CALL_STATE_RINGING:
                        //正在响铃...
                        if(phoneNumber == null ||phoneNumber.isEmpty())
                            return;
                        
                        Log.d(TAG, "onCallStateChanged(): 来电号码 = " + phoneNumber);
                        if(comingCallListener != null)
                            comingCallListener.onCalling(curTimeToFormatNoYearMills(),phoneNumber,null);
                        
                        break;
                

            
        ;
        //开启监听
        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
        return true;
    


    public boolean cancelPhoneStateListener()
        // 取消来电的电话状态监听服务
        if (mTelephonyManager != null && mPhoneStateListener != null) 
            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
            return true;
        
        return false;
    

3 完整Demo

下载地址:https://download.csdn.net/download/qq_38950819/60265473

以上是关于Android来电监听的主要内容,如果未能解决你的问题,请参考以下文章

Android来电监听

Android来电监听

Android 监听来电广播

Android 监听来电广播

Android 监听来电广播

Android 监听来电广播