Oreo (8.0) - 以编程方式删除短信

Oreo (8.0) - Remove sms programmatically

我无法删除 Android 8.0

上的特定消息
    private void foo() {
        Uri dummySms = insertDummySms(context, threadId);
        removeDummySms(context, dummySms);
    }

    private Uri insertDummySms(Context context, long threadId) {
        ContentValues values = new ContentValues();
        values.put("thread_id", threadId);
        values.put("body", "Dummy SMS body.");
        Uri insert = context.getContentResolver().insert(Uri.parse("content://sms/sent"), values);
        Log.i(TAG, "insertDummySms: " + insert);
        return insert;
    }

    private void removeDummySms(Context context, Uri uri) {
        Log.i(TAG, "removeDummySms: START: " + uri.toString() + " :: " + context.getContentResolver().getType(uri));
        context.getContentResolver().delete(uri, null, null);
        Log.i(TAG, "removeDummySms: END!!!");
    }

当我使用 运行 foo() 方法时,日志显示:

I/Test: insertDummySms: content://sms/sent//8
I/Test: removeDummySms: START: content://sms/sent//8 :: vnd.android.cursor.item/sms,

然后它崩溃了:

java.lang.IllegalArgumentException: Unknown URL
    at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:165)
    at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)

相同的代码适用于 android 6.x 和 7.x

有趣的是:如果我像这样删除每条短信:

context.getContentResolver().delete(uri, null, null); // where uri is equal: content://sms

它就像一个魅力。

知道失败的原因吗?

目前我只有一个 phone 带有 Android O,所以我不知道它是否在每个 Android O 设备上崩溃。我有 Nokia TA-1004

最近对 SmsProvider class 的更改有点破坏了那里的预期功能。来自提供商 insert() 调用的 Uri returned 以前非常简单地计算为:

Uri uri = Uri.parse("content://" + table + "/" + rowID);

其中 rowID 是来自对 SQLiteDatabaseinsert() 调用的 return。

对于插入常规的完整 SMS 消息,如您的情况,table"sms"。但是,还有其他几个表,主要由系统和默认消息传递应用程序使用,它们使用相同的 insert() 方法;例如,"raw""attachments" 等。对这些表的插入导致对 returned Uri 的无效权限;例如content://raw/123.

较新的版本现在对无效权限抛出异常,而不是静默失败,因此 Uri 结构更改为:

Uri uri;
if (table == TABLE_SMS) {
    uri = Uri.withAppendedPath(url, "/" + rowID);
} else {
    uri = Uri.withAppendedPath(url, "/" + table + "/" + rowID );
}

以后到:

Uri uri = Uri.withAppendedPath(url, String.valueOf(rowID));

首先解决问题。

在这两个版本中,url 是您在 ContentResolver#insert() 调用中传递的版本。对于您的代码段中的 insert() 调用,这最终会 returning 一个类似于 content://sms/sent/123 的 URI,这就是问题所在。当它在 delete() 调用中传递时,它不匹配 Provider 的相应方法认为有效的任何 URI,因此它抛出 IllegalArgumentException of Unknown URL.

要解决此问题,您可以从 return 构建您自己的有效 URI,因为您只需要知道 ID。例如:

Uri del = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, dummySms.getLastPathSegment());

但是,可以说在 "content://sms/sent" (Sms.Sent.CONTENT_URI) 上开始插入是不正确的。该 URI 和其他类似的 "box" URI - 即 Sms.Inbox.CONTENT_URISms.Drafts.CONTENT_URI 等 - 旨在用于查询,而不是写入操作。

您应该改为在基数 Sms.CONTENT_URI ("content://sms") 上插入,并在 ContentValues 中将 TYPE 设置为 MESSAGE_TYPE_SENT。例如,推荐修复:

ContentValues cv = new ContentValues();
cv.put(Telephony.Sms.ADDRESS, ...);
cv.put(Telephony.Sms.BODY, ...);
cv.put(Telephony.Sms.TYPE, Telephony.Sms.MESSAGE_TYPE_SENT);

Uri uri = getContentResolver().insert(Telephony.Sms.CONTENT_URI, cv);

这将导致 UriSmsProviderdelete() 方法识别,并且您的删除应该像以前一样成功。