Android如何实现短信的读取与恢复?

我最近有个短信读取与恢复的需求,现在对其核心实现进行以下总结。

一、短信读取

    fun getAllSms(context: Context) {
        val cv = context.contentResolver
        val cursor =
            cv.query(Telephony.Sms.CONTENT_URI, null, null, null, Telephony.Sms.DEFAULT_SORT_ORDER)
        if (cursor == null) {
            Log.e(TAG, "cursor is null")
            return
        }

        while (cursor.moveToNext()) {
            val type = cursor.getInt(cursor.getColumnIndex(Telephony.Sms.TYPE))
            val address = cursor.getString(cursor.getColumnIndex(Telephony.Sms.ADDRESS))
            val date = cursor.getLong(cursor.getColumnIndex(Telephony.Sms.DATE))
            val body = cursor.getString(cursor.getColumnIndex(Telephony.Sms.BODY))
            val person = cursor.getInt(cursor.getColumnIndex(Telephony.Sms.PERSON))
            val protocol = cursor.getInt(cursor.getColumnIndex(Telephony.Sms.PROTOCOL))
            Log.i(TAG, "sms = " + Sms(type, address, date, body, person, protocol).toString())
        }

        cursor.close()
    }

二、设置系统默认短信APP

从Android5.0开始,默认短信应用外的软件不能以写入短信数据库的形式(write sms)发短信,也就是说插入短信到短信数据库方法行不通了。

我的解决方法是将应用设置为默认短信(事实上,并没有成为真正的短信App,只是应用现在具有了创建短信的能力了,就满足我的需求了),操作如下:

0、首先要进行如下配置

这些配置是系统任务一个短信App应该有的功能,要想设置成默认短信App,就必须得有这些。当然创建一个空的就可以,因为我目前的需求是设置成默认短信App,然后能创建短信就行。

        <!-- 接收SMS -->
        <receiver
            android:name=".SmsReceiver"
            android:exported="false"
            android:permission="android.permission.BROADCAST_SMS">
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_DELIVER" />
            </intent-filter>
        </receiver>
        <!-- 接收MMS-->
        <receiver
            android:name=".MmsReceiver"
            android:exported="false"
            android:permission="android.permission.BROADCAST_WAP_PUSH">
            <intent-filter>
                <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
                <data android:mimeType="application/vnd.wap.mms-message" />
            </intent-filter>
        </receiver>
        <service
            android:name=".HeadlessSmsSendService"
            android:exported="true"
            android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE">
            <intent-filter>
                <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="sms" />
                <data android:scheme="smsto" />
                <data android:scheme="mms" />
                <data android:scheme="mmsto" />
            </intent-filter>
        </service>

        <activity
            android:name=".activity.MainActivity"
            android:exported="true"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
                <category android:name="android.intent.category.DEFAULT" />
                <action android:name="android.intent.action.SEND" />
                <action android:name="android.intent.action.SENDTO" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="sms" />
                <data android:scheme="smsto" />
                <data android:scheme="mms" />
                <data android:scheme="mmsto" />
            </intent-filter>
        </activity>

这里我还遇到了一个问题:我在MainActivity中添加了一系列配置后,图标消失了。解决方法是将这些配置移到另一个Activity中就可以了。

然后开始设置应用为默认短信。在进行了一系列搜索后与试验后,最后终于在Rolemanager的源码注释中找到了答案:

1、Android29及以上

Then the application will need user consent to become a role holder, which can be requested using android.app.Activity.startActivityForResult(Intent, int) with the Intent obtained from createRequestRoleIntent(String).

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                val rm = SystemServiceHelper.getRoleManager() as RoleManager
                val intentSms = rm.createRequestRoleIntent(RoleManager.ROLE_SMS)
                startActivityForResult(intentSms, REQUEST_CODE_DEFAULT_SMS)
            }

/**
         * 获取 RoleManager,用于指定默认的应用
         */
        @RequiresApi(Build.VERSION_CODES.Q)
        fun getRoleManager(): RoleManager? {
            val rm =
                AppContext.appContext.getSystemService(Context.ROLE_SERVICE) as RoleManager?
            if (rm == null) {
                Log.e(TAG, "[RoleManager is null]")
            }
            return rm
        }

核心在于:createRequestRoleIntent。

2、低版本设置

val defaultSmsApp = Telephony.Sms.getDefaultSmsPackage(this)
        if (packageName != defaultSmsApp) {
            val intent = Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT)
            intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, packageName)
            startActivity(intent)
        }

三、创建短信

    fun addSms(context: Context) {
        val cv = context.contentResolver
        var value = ContentValues()
        value.put(Telephony.Sms.BODY, "测试短信创建")
        value.put(Telephony.Sms.PERSON, 11111111111)
        value.put(Telephony.Sms.ADDRESS, "11111111111")
        value.put(Telephony.Sms.DATE, System.currentTimeMillis())
        value.put(Telephony.Sms.TYPE, 1)
        value.put(Telephony.Sms.READ, 0)
        cv.insert(Telephony.Sms.CONTENT_URI, value)
    }

小结

1、需要注意的是,像上面这样设置过后,我们的应用就有了创建短信的能力,但事实上,我们的App还不是默认应用。(有需求的朋友可以继续探索一下)

参考

1、Android 读取所有短信
2、android黑科技之读取用户短信+插入短信到系统短信数据库
3、Android设为系统默认的短信应用

在 Android11(小米)上,没有生效

4、Android如何将第三方信息应用设置为默认信息应用?
5、Android10.0(Q) 默认应用设置(电话、短信、浏览器、主屏幕应用)

4/5反射方式,都报错:Caused by: java.lang.SecurityException: addRoleHolderAsUser: Neither user 10318 nor current process has android.permission.MANAGE_ROLE_HOLDERS.
这个权限申请不到,因此这两种方案不适合。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,710评论 4 376
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,839评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,295评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,776评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,198评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,074评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,200评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,986评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,733评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,877评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,348评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,675评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,393评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,209评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,212评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,003评论 2 280

推荐阅读更多精彩内容