IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 【Android】来电监听 -> 正文阅读

[移动开发]【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

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-12-11 15:50:25  更:2021-12-11 15:50:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:14:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码