アンドロイドMediaStoreからメディアファイルを削除する

By JS | Last updated: December 01, 2019

MediaStoreでImage / Video / Audioなどのメディアファイルの削除方法を紹介します。 Query、Updateなど同様に、ContentResolver.delete()APIでMediaStoreのアイテムを削除することができます。

Android 10(TargetSDK 29)でScoped Storage適用では、 どのファイルを削除するには、ユーザーからの許可を受ける場合があります。 例外として、アプリが追加されたメディアファイルは、許可なしに削除が可能です。

一方、TargetSDK 29未満でアプリがメディアファイルを削除するには、 WRITE_EXTERNAL_STORAGE権限を持っている必要があります。

Android 10と、その下のSDKでどのようにアイテムを削除するかどうかを調べてみましょう。

MediaStoreのアイテムを照会する方法は、MediaStoreでメディアファイルの情報を読むを、 MediaStoreにアイテムを追加する方法は、MediaStoreにメディアファイルを保存する方法ご覧ください。

メディアファイルの削除(Android 10)

Android 10でScoped Storageの適用に3rd party appは WRITE_EXTERNAL_STORAGE権限を取得する必要がなくなりました。

すべてのアプリは、 WRITE_EXTERNAL_STORAGE権限があっても、ファイルを書き込むことができないからです。 ファイルを書き込むために、必要に応じてユーザーに権限が必要です。

ContentResolver.delete()はMediaStoreのアイテムを削除します。 アプリが登録されたメディアファイルは、ユーザーの許可なしに削除することができ、他のアプリで登録したメディアファイルを削除するには、ユーザーの許可が必要です。

アプリが直接登録したメディアファイル

アプリが直接登録したメディアファイルは、ユーザーの許可なしに削除することができます。

次のコードは、特定の名前のメディアファイルを探して、そのUriをMediaStoreから削除するコードです。

// fileNameと同じアイテムを見つけた後、Uriを保存
val projection = arrayOf(
    MediaStore.Images.Media._ID,
    MediaStore.Images.Media.DISPLAY_NAME
)
var displayName: String? = null
var contentUri: Uri? = null
contentResolver.query(
    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
    projection,
    null,
    null,
    null
)?.use { cursor ->
    val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
    val displayNameColumn =
        cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
    while (cursor.moveToNext()) {
        val id = cursor.getLong(idColumn)
        displayName = cursor.getString(displayNameColumn)
        if (displayName != fileName) {
            continue
        }
        contentUri = Uri.withAppendedPath(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            id.toString()
        )
    }
}

//見つかったUriをMediaStoreから削除
contentUri?.let{
    contentResolver.delete(contentUri!!, null, null)
    Log.d(TAG, "Removed $displayName from MediaStore: $contentUri")
}

他のアプリが登録されたメディアファイル

もし、他のアプリが保存したメディアファイルや、ユーザーがPCでデバイスに保存されたメディアファイルを削除したい場合があります。 このような場合には、アプリは削除する権限がありません。

もし上記のように削除しようとすると RecoverableSecurityException例外が発生し失敗します。 この例外が発生したときMedia ProviderはRemoteActionオブジェクトを生成してExceptionに保存します。 RemoteActionはIntentSenderオブジェクトがあり、これを利用して、ユーザーにこのファイルを削除してもされている確認するポップアップを浮かせることができます。

ユーザーが[OK]ボタンを押すと、アプリは削除権限を与えられれ、再び削除しようとすると、削除が成功します。

次は、先ほど述べた内容の例です。

private const val DELETE_PERMISSION_REQUEST = 0x1033

try {
    contentUri?.let{
        contentResolver.delete(contentUri!!, null, null)
        Log.d(TAG, "Removed $displayName from MediaStore: $contentUri")
    }
} catch (e: RecoverableSecurityException) {
    // 権限がないため、例外が発生します。
    // RemoteActionはExceptionと一緒に渡されます。
    // RemoteActionでIntentSenderオブジェクトを取得することができます。
    // startIntentSenderForResult()を呼び出して、ポップアップを表示させ。
    val intentSender = e.userAction.actionIntent.intentSender
    intentSender?.let {
        startIntentSenderForResult(
            intentSender,
            DELETE_PERMISSION_REQUEST,
            null,
            0,
            0,
            0,
            null
        )
    }
}

//ポップアップが終了したら、結果がonActivityResultにコールバックされます。
//このとき、再削除を試みることができます。
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (resultCode == Activity.RESULT_OK && requestCode == DELETE_PERMISSION_REQUEST) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            removeMediaFile("my_image.jpg")
        }
    }
}

IntentSenderを実行すると、次のようなポップアップがみられ、[OK]ボタンを押すと、アプリは削除権限を与えられます。 popup for removing a file from MediaStore

権限を受け取るonActivityResult()でコールバックが続き、再削除を試みることができます。

エミュレータでは、上記のコードをテストしたときMediaStoreからアイテムが削除されることを確認しました。

しかし、adb shellで、そのファイルが保存された実際のパスに入ると、ファイルは削除されずに残っています。 この部分がバグなのか、政策による動作かは確かに分かりません。 自分が登録したメディアファイルは、実際のファイルまで削除されていることから見て、実際のファイルも削除されるのが正しい動作のようです。

メディアファイルの削除(Android 10未満)

Android 10未満でMediaStoreのファイルを削除するには、 WRITE_EXTERNAL_STORAGE権限が必要です。 上記は、別の方法で権限をあらかじめ受けたため、次のようなコードに直接削除することができます。

contentResolver.delete(contentUri!!, null, null)

この記事で使用したコードは、GitHub - MediaStoreで確認することができます。

参考

Related Posts

codechachaCopyright ©2019 codechacha