我最近有个短信读取与恢复的需求,现在对其核心实现进行以下总结。
一、短信读取
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.
这个权限申请不到,因此这两种方案不适合。