Android监听消息(二)——电话及短信监听

这篇具有很好参考价值的文章主要介绍了Android监听消息(二)——电话及短信监听。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

学更好的别人,

做更好的自己。

——《微卡智享》

本文长度为2747,预计阅读6分钟

前言

前面一篇《Android监听消息(一)——应用消息捕获》我们使用NotificationListenerService实现了应用的消息监听,但是电话和短信是接收不到的,所以这一篇我们就来解决怎么监听电话及短信,电话主要就是在响铃时发送来电人消息,短信的话是捕获到消息内容直接发送出来。

Android监听消息(二)——电话及短信监听

微卡智享

实现思路

在Android中实现电话捕获用到的还是TelephonyManager,在Android12前TelephonyManager可以使用PhoneStateListener来进行监听,里面的onCallStateChanged可以直接获取到来电状态和来电号码,比较方便,如下图:

Android监听消息(二)——电话及短信监听

但是在Android12(sdk31)后,listen已经不能用了,需要使用registerTelephonyCallback来获取到来电状态,但是这里无法获取到来电号码了,所以为了实现接到到来电状态并能获取到来电人的信息,需要使用BroadcastReceiver来实现。

而捕获短信的消息则是使用android.telephony.SmsMessage,并且短信的接收也是通过BroadcastReceiver来实现,这样我们就把电话和短信接收直接写在一个BroadcastReceiver中即可。

代码实现

Android监听消息(二)——电话及短信监听

微卡智享

01

权限申请

接着上一篇的Demo,我们在这个基础上再增加相关的设置,首先要捕获电话及短信,那相关的权限必须要先申请出来。

Android监听消息(二)——电话及短信监听

<uses-permission android:name="android.permission.ACTION_NOTIFICATION_LISTENER_SETTINGS" />
    <uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.READ_CALL_LOG" />
    <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />

在Manifest中加入权限申请,当然Android6.0后需要动态申请权限了,所以要在MainActivity中加入动态申请权限。

//权限申请
    companion object {
        private const val REQUEST_CODE_PERMISSIONS = 10
        private val REQUIRED_PERMISSIONS = arrayOf(
            Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_PHONE_NUMBERS,
            Manifest.permission.ANSWER_PHONE_CALLS, Manifest.permission.CALL_PHONE,
            Manifest.permission.RECEIVE_SMS,Manifest.permission.SEND_SMS,
            Manifest.permission.READ_SMS, Manifest.permission.READ_CALL_LOG,
            Manifest.permission.WRITE_CALL_LOG, Manifest.permission.READ_CALL_LOG,
            Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS
        )
    }


    private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
        ContextCompat.checkSelfPermission(BaseApp.mContext, it) == PackageManager.PERMISSION_GRANTED
    }


    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                //监听开关按钮
                isListened = DataStoreHelper.getData(ISLISTENMSG, false)
                Log.i("pkg", "NLSrv ${isListened}")
                val status = if (isListened) 0 else 2
                NLSrvUtil.updateStatus(status)
            } else {
                Toast.makeText(this, "未开启权限.", Toast.LENGTH_SHORT).show()
                finish()
            }
        }
    }
    
    //onCreate中再加入申请权限的动作
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //申请权限
        if (!allPermissionsGranted()) {
            requestPermissions(REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
        }
    }

02

电话及短信的BroadcastReceiver

这个广播接收是这篇的一个重点,先上代码:

package vac.test.notificationdemo


import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.provider.Telephony
import android.service.notification.NotificationListenerService
import android.telecom.TelecomManager
import android.telephony.PhoneStateListener
import android.telephony.SmsMessage
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import android.util.Log
import com.jeremyliao.liveeventbus.LiveEventBus
import vac.test.notificationdemo.bean.CMessage
import java.util.Hashtable
import java.util.Objects


class PhoneStateReceiver : BroadcastReceiver() {


    //当前来电号码
    var mPhoneNum: String? = null
    var mLastPhoneNum: String? = null
    //当前短信号码
    var mLastSmsPhoneNum: String? = null
    var mLastSmsContent: String? = null


    val contactsht: Hashtable<String, String> = NLSrvUtil.getContactsHashTable()


    var telMng: TelephonyManager =
        BaseApp.mContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager


    fun createPhone(phonenum: String?): CMessage {
        val msg = CMessage()
        msg.packagename = "来电"
        msg.appname = "来电"
        msg.title = contactsht[phonenum] ?: "未知号码"
        msg.content = phonenum ?: "未知号码"
        return msg
    }


    fun createSms(phonenum: String?, content:String?): CMessage {
        val msg = CMessage()
        msg.packagename = "短信"
        msg.appname = "短信"
        msg.title = contactsht[phonenum] ?: "未知号码"
        msg.content = content ?: "未解析内容"
        return msg
    }


    init {
        telMng = BaseApp.mContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            telMng.registerTelephonyCallback(
                BaseApp.mContext.mainExecutor,
                object : TelephonyCallback(), TelephonyCallback.CallStateListener {
                    override fun onCallStateChanged(state: Int) {
                        when (state) {
                            TelephonyManager.CALL_STATE_IDLE -> {
                                Log.d("pkg", "挂断")
                                mLastPhoneNum = null
                                mPhoneNum = null
                            }
                            TelephonyManager.CALL_STATE_OFFHOOK -> {
                                Log.d("pkg", "接听")
                                mLastPhoneNum = null
                                mPhoneNum = null
                            }
                            TelephonyManager.CALL_STATE_RINGING -> {
                                Log.d("pkg", "CALL_STATE_RINGING")
                                mPhoneNum?.let {
                                    if (mLastPhoneNum != it) {
                                        mLastPhoneNum = it
                                        val msg = createPhone(mPhoneNum)
                                        LiveEventBus.get<CMessage>(MESSAGE_RECV)
                                            .post(msg)
                                        Log.d("pkg", "响,号${mPhoneNum}")
                                    }
                                }
                            }
                        }


                    }
                })
        } else {
            telMng.listen(object : PhoneStateListener() {
                override fun onCallStateChanged(state: Int, phoneNumber: String?) {
                    when (state) {
                        TelephonyManager.CALL_STATE_IDLE ->
                            Log.d("log", "挂断")
                        TelephonyManager.CALL_STATE_OFFHOOK ->
                            Log.d("log", "接听")
                        TelephonyManager.CALL_STATE_RINGING -> {
                            Log.d("log", "响铃,来电号码:${phoneNumber}")
                            val msg = createPhone(phoneNumber)
                            LiveEventBus.get<CMessage>(MESSAGE_RECV)
                                .post(msg)
                        }
                    }
                }
            }, PhoneStateListener.LISTEN_CALL_STATE)
        }
    }


    override fun onReceive(context: Context, intent: Intent) {
        Log.d("pkg", "Action:${intent.action}")
        when (intent.action) {
            //监听电话状态
            "android.intent.action.PHONE_STATE" -> {
                mPhoneNum = intent.extras?.getString("incoming_number")
                Log.d("pkg", "号码:${mPhoneNum}")
            }
            Telephony.Sms.Intents.SMS_RECEIVED_ACTION -> {
                var curphonenum: String? = null
                val content = StringBuilder()
                val smsbundle = intent.extras
                val format = intent.getStringExtra("format")
                smsbundle?.let {
                    val pdus = smsbundle.get("pdus") as Array<*>
                    pdus?.let {
                        for (item in it) {
                            val message = SmsMessage.createFromPdu(item as ByteArray, format)
                            //短信电话号码
                            curphonenum = message.originatingAddress
                            content.append(message.messageBody)
                            val mills = message.timestampMillis
                            val status = message.status
                            Log.i("pkg", "phonenum:${curphonenum}, mills:${mills}, status:${status}")
                        }
                    }
                }
                //判断相同的消息就不再发送,防止接收过多
                if(curphonenum == mLastSmsPhoneNum && content.toString() == mLastSmsContent) return
                //记录最后一次接收短信的号码和内容
                mLastSmsPhoneNum = curphonenum
                mLastSmsContent = content.toString()


                Log.i("pkg", "phone:${mLastSmsPhoneNum},Content:${mLastSmsContent}")
                val msg = createSms(mLastSmsPhoneNum,mLastSmsContent)
                LiveEventBus.get<CMessage>(MESSAGE_RECV)
                    .post(msg)
            }
        }
    }
}

Android监听消息(二)——电话及短信监听

我的Demo程序使用的sdk是33,,TelephonyManager中使用registerTelephonyCallback来注册监听电话,上图红框中TelephonyManager.CALL_STATE_RINGING代表着响铃,也就是这个状态时直接使用LiveEventBus进行消息通讯。

Android监听消息(二)——电话及短信监听

定义了四个变量,主要是用于处理当前响铃的电话及收到短信的信息和号码,因为在测试过程中,使用广播接收时可能会触发多次,所以这里定义了变量用于处理多次接收相同的不再重复推送消息。

Android监听消息(二)——电话及短信监听

通过修改onReceive来判断是电话还是短信,短信中也加入了相同信息不再重复推送。

03

关于联系人信息

使用BroadcastReceiver接收到的都是来电的号码,现在很少有人去背号码了,所以这里需要将号码转换成联系人的信息再推送过来。

Android监听消息(二)——电话及短信监听

我们在Demo的工具类中加入了一个getContactsHashTable的函数,用于导出联系人信息存放于HashTable中,这样通过号码查找也快。

//获取联系人信息
        fun getContactsHashTable() : Hashtable<String,String> {
            val contactsht = Hashtable<String, String>()
            var cursor: Cursor? = null
            try{
                cursor = BaseApp.mContext.contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                    null,null,null,null)
                cursor?.let {
                    while (it.moveToNext()){
                        val phonename = it.getString(it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
                        val phonenum = it.getString(it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER))
                        contactsht.put(phonenum, phonename)
                    }
                }
            }catch (e: Exception) {
                e.printStackTrace()
            }finally {
                cursor?.let {
                    it.close()
                }
            }
            return contactsht
        }

Android监听消息(二)——电话及短信监听

而在PhoneStateReceiver中直接再写两个函数,通过来电号码和短信相关信息直接生成我们的CMessage类,再进行消息组件的通讯,这样电话和短信的接收通讯也就可以实现了。测试的来电和短信图片我就不再发上来了,主要是还要P图麻烦。

Tips

消息监听和模拟推送的Demo就已经完成了,测试正常,因为即然是想实时监听,就要保证锁屏也能正常使用,原来我考虑要再做成前台服务,不过我在手机系统中加入耗电设置后,也一直能实现锁屏的监听情况了,所以前台服务就暂时不加入进去了,有必要的时候再考虑。

另外就是应用把锁定也勾选上,这样杀后台的时候也不会将当前应用杀掉,正好做了下测试,手机待机一晚上,第二天再发消息,还是能正常接收消息,说明应用程序一直在后台运行中。

Android监听消息(二)——电话及短信监听

我的是Oppo Find N2 Flip,在设置的电池中,把当前应用的允许完成后台行为打上勾后,监听可以一直正常没问题。监听这块Demo基本就告一段落了,下一步就要开始做蓝牙通讯的相关Demo,用于监听到消息后的手机间通讯。

Android监听消息(二)——电话及短信监听

Android监听消息(二)——电话及短信监听

往期精彩回顾

 文章来源地址https://www.toymoban.com/news/detail-495498.html

Android监听消息(二)——电话及短信监听

Android监听消息(一)——应用消息捕获

 

 

Android监听消息(二)——电话及短信监听

智能手表接收两台手机消息?最近计划

 

 

Android监听消息(二)——电话及短信监听

测试新版Android Studio的手机镜像效果

 

到了这里,关于Android监听消息(二)——电话及短信监听的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【群控】通过adb命令实现接打电话&发短信脚本

    由于电信局有规定,手机号码长期不使用时会进入锁定状态(能接电话不能打电话,能接短信不能发短信),所以为了避免手机号被判定为僵尸号需要做一个定期互相拨打电话并且接通的脚本。 声明:本文只用作技术讨论,技术无罪。请勿使用该方法进行违法活动!!! 呼

    2024年02月16日
    浏览(35)
  • SIM800C通过串口AT指令拨打电话与发送短信

    一、更新时间 二、相关器件 三、硬件连接 四、现象与事件 五、具体操作:  5.1 准备工作(查询器件是否正常)  5.2 拨打电话  5.3 发送短信   5.3.1 短信消息模式为文本模式   5.3.2 短信消息模式为PDU模式          2022.1.18         ch340、sim800c          注意VIN与GN

    2023年04月08日
    浏览(39)
  • 解决小米5手机使用电信或联通卡不能VoLTE电话短信的问题(2)

    我是阿清,一名电子电脑爱好者,也是一名”万能维修工\\\",有20多年的维修经验。下面是一些我的维修故事。 前情回顾:解决小米5手机使用电信或联通卡不能VoLTE电话短信的问题(1)_AQing阿清的博客-CSDN博客 上一篇文章提到,电信已经取消了2G3G网络,我的小米5,只有移动卡

    2024年02月08日
    浏览(74)
  • 解决小米5手机使用电信或联通卡不能VoLTE电话短信的问题(1)

    我是阿清,一名电子电脑爱好者,也是一名”万能维修工\\\",有20多年的维修经验。下面是一些我的维修故事。 大家都知道2G网络时代分GSM和CDMA1X。移动专做GSM网络,联通做GSM和CDMA1X网络。那时候联通混的挺惨,不但资费要比移动便宜10%,用户估计也就是移动的10%。还运营着两

    2024年02月07日
    浏览(148)
  • 小米5手机设置电信联通VoLTE,解决4G网络不能接打电话收发短信的问题(3)

    我是阿清,一名电子电脑爱好者,也是一名”万能维修工\\\",有20多年的维修经验。下面是一些我的维修故事。 前情回顾:  解决小米5手机使用电信或联通卡不能VoLTE电话短信的问题(1)_AQing阿清的博客-CSDN博客 解决小米5手机使用电信或联通卡不能VoLTE电话短信的问题(2)

    2024年02月08日
    浏览(88)
  • Swift 技术 监听电话中断,音乐(用于恢复播放音乐)(源码)

    Swift 技术 音频,音乐(AVAudioSession设置,音乐中断) Swift 技术 监听电话中断,音乐(用于恢复播放音乐)(源码) Swift 第三方 播放器AliyunPlayer(阿里云播放器)(源码) Swift 需求 音乐播放暂停淡出淡放(声音逐渐消失)(视频)(源码) OC 技术 DOUAudioStreamer音乐播放器的使用(源码) Swift 基础 AVPlaye

    2024年02月02日
    浏览(43)
  • Flutter:使用url_launcher打开外部浏览器、拨打电话、发送短信、打开第三方app、打开应用商店下载应用

    Flutter中的 url_launcher 是一个用于打开URL的插件。它允许在Flutter应用程序中打开网址、发送电子邮件、拨打电话等操作。使用 url_launcher 插件,可以轻松地在应用程序中集成各种URL操作。 官方地址 https://pub-web.flutter-io.cn/packages/url_launcher 安装 打开网址 这里有两个注意点: 模拟

    2024年02月08日
    浏览(82)
  • STM32使用SIM900A、SIM800C、SIM800A完成短信发送、连接onenet上传数据、拨打电话_完整教程

    本篇文章介绍SIM800C 、SIM800A、SIM900A 等等系列的模块的常用AT指令,讲解模块的使用方法,演示短信发送、拨打电话、网络连接,与服务器通信等常用案例。 如果只是用到发送短信、拨打电话、连接网络通信、这些模块的AT指令是兼容的。 文章最后贴了完整的STM32代码,通过

    2024年02月02日
    浏览(47)
  • Sms开源短信及消息转发器,不仅只转发短信,备用机必备神器

    Sms开源短信及消息转发器,不仅只转发短信,备用机必备神器。 短信转发器——不仅只转发短信,备用机必备神器! 监控Android手机短信、来电、APP通知,并根据指定规则转发到其他手机:钉钉群自定义机器人、钉钉企业内机器人、企业微信群机器人、企业微信应用消息、飞书

    2024年02月07日
    浏览(53)
  • 消息监听器和消息监听容器

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 消息监听器顾名思义用来接收消息,它是使用消息监听容器的必须条件。目前有8个消息监听器: 使用自动提交或容器管理的提交方法之一,处理从 Kafka 消费者 p

    2024年02月07日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包