Media StoreでcreateDeleteRequestを使ったファイルの削除

投稿日:  更新日:

既存のメディアファイルへ、アクセスする許可を取得するためのリクエストが、Media Store APIに準備されています。

このリクエストで許可を得たアプリは、ファイルの所有者に関係なく、アクセスが可能になります。

このリクエストの中の、createDeleteRequestについて、まとめます。

削除する許可を取得するリクエストです。

※環境:Android Studio Narwhal Feature Drop | 2025.1.2 Patch 1

スポンサーリンク

Media Storeのリクエスト

Media Storeに、表のようなリクエストが準備されています。

リクエスト
(MediaStore.***)
概要許可後の動作
createWriteRequest書き込み許可を要求URIへ書き込み(更新)可能
createDeleteRequest削除する許可を要求即時に削除
createTrashRequest廃棄する許可を要求ゴミ箱へ廃棄したと見なされる
(is_trashedカラムを設定)
一定時間経過後にシステムが削除
createFavoriteRequestお気に入り登録する許可を要求お気に入り登録したと見なされる
(is_favoriteカラムを設定)
※許可はアプリに対して与えられる

いずれも、アプリが既存ファイル(自アプリ・他アプリが所有するファイル)へアクセスする許可を取得するためのリクエストです。

このリクエストを使うと、ダイアログが表示されて、ユーザーに許可・拒否の判定を求めます。

許可されたら、アクセスが実行されます。

スポンサーリンク

許可の取得手順

許可の取得は、次のような手順で行います。

URIのアクセス許可の取得

 手順1 

削除対象のURIを準備。Clientアプリを起動します。

 手順2 

Clientアプリはダイアログを表示して、削除する許可・拒否の判定を求めます。

 手順3 

ダイアログによる判定の結果をコールバックで返します。

手順1~3はアプリケーションコンポーネント(Activity)の連携と同じ動作です。
※詳細は「App component:Activity」を参照

スポンサーリンク

削除の例

削除の具体的な例です。※Activity Result APIで記述しています。

コールバックの定義と登録

registerForActivityResultはコールバックの定義と登録を行います。

ラムダ式がコールバックです。「許可・拒否」はresultCodeで判断できます。

そして、このコールバックを利用するコンポーネントのランチャー(_MediaDeleteDialog : ActivityResultLauncher)を返します。

        val _MediaDeleteDialog = registerForActivityResult(
            ActivityResultContracts.StartIntentSenderForResult()
        ) { result ->
            if(result.resultCode == RESULT_OK) {    // アクセス許可
                /* ここが実行される時点で、すでに削除済み */
            }
            else { }                                // アクセス拒否
        }

registerForActivityResultは、LifecycleOwnerのcurrentStateが「STARTED」より前のタイミングで実行して下さい。Activityのライフサイクルと密接に関係しています。
※LifecycleOwnerについては「ライフサイクル対応コンポーネント作成」を参照

スポンサーリンク

許可チェックとダイアログ起動

削除対象のURIを準備します。その後、Clientアプリを起動(launch)し、ダイアログを表示させます。

launchの引数は取得したい許可のリクエスト(MediaDeleteReq : IntentSenderRequest)です。削除を行いたいのでMediaStore.createDeleteRequestを使って、リクエストを構築しています。

        @RequiresApi(Build.VERSION_CODES.R)
        fun onDelete(uri: Uri, id: Long) {
            val _uri = ContentUris.withAppendedId(uri, id)  // URIを準備
            _MediaDeleteDialog.launch(MediaDeleteReq(contentResolver, _uri))
        }
@RequiresApi(Build.VERSION_CODES.R)
fun MediaDeleteReq(resolver: ContentResolver, uri: Uri): IntentSenderRequest {
    val _pendingIntent = MediaStore.createDeleteRequest(resolver, listOf(uri))
    val _intentSenderRequest = IntentSenderRequest.Builder(
        _pendingIntent.intentSender
    ).build()
    return _intentSenderRequest
}

MediaStore.createDeleteRequestはAPI≧30から利用が可能です。

スポンサーリンク

削除の実行

削除の実行(onDelete)を行うと、ダイアログが表示されて、ユーザに「許可・拒否」の判定を求めます。

val _uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val _id = 1000000033

// val _uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
// val _id = 1000000034

// val _uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
// val _id = 1000000035

onDelete(_uri, _id)
メディアテーブルの状態
# pwd
/data/data/com.google.android.providers.media.module/databases
# sqlite3 ./external.db
sqlite> .headers on
sqlite> .mode column
sqlite> select _id,_display_name,_data from images;
_id         _display_name  _data
----------  -------------  -------------------------------------
1000000033  leaf.jpg       /storage/emulated/0/Pictures/leaf.jpg
sqlite> select _id,_display_name,_data from video;
_id         _display_name  _data
----------  -------------  --------------------------------------
1000000034  myroom.mpeg    /storage/emulated/0/Movies/myroom.mpeg
sqlite> select _id,_display_name,_data from audio;
_id         _display_name  _data
----------  -------------  --------------------------------------
1000000035  akatonbo.mp3   /storage/emulated/0/Music/akatonbo.mp3

※エミュレータ(API 33)で確認
メディアデータの状態
# pwd
/storage/emulated/0
# ls -l Pictures/* Movies/* Music/* */.thumbnails/*
-rw-rw---- 1 u0_a170 media_rw   5349 2025-08-22 12:09 Movies/.thumbnails/1000000034.jpg
-rwxrwx--- 1 u0_a170 media_rw 195759 2025-08-22 12:09 Movies/myroom.mpeg
-rw-rw---- 1 u0_a170 media_rw  11472 2025-08-22 12:11 Music/.thumbnails/1000000035.jpg
-rwxrwx--- 1 u0_a170 media_rw  45366 2025-08-22 12:09 Music/akatonbo.mp3
-rw-rw---- 1 u0_a170 media_rw  34088 2025-08-22 12:08 Pictures/.thumbnails/1000000033.jpg
-rwxrwx--- 1 u0_a170 media_rw  47891 2025-08-22 12:08 Pictures/leaf.jpg

※エミュレータ(API 33)で確認

なお、ダイアログに表示される画像はサムネイル(.thumbnail/メディア情報のID.jpg)です。

imagesvideoaudio
許可・拒否ダイアログ(images)
許可・拒否ダイアログ(video)
許可・拒否ダイアログ(audio)

「許可」となったURIは、このアプリにより削除が可能です。

そして、システムにより、このURIに関係する全てのデータ(データベースのレコード、ファイル、サムネイル)が即時に削除されます。ユーザ側で削除処理をする必要はありません。

判定の結果をコールバックで返す時点で、すでに削除済みです。注意して下さい。

スポンサーリンク

対象をグループで指定

URIをリストで指定すると、複数のURIをグループとして、一括で削除できます。

        @RequiresApi(Build.VERSION_CODES.R)
        fun onDelete(uri: Uri, ids: Array<Long>) {
            val _uris = ids.map { ContentUris.withAppendedId(uri, it) } // URIを準備
            _MediaDeleteDialog.launch(MediaDeleteReq(contentResolver, _uris))
        }
@RequiresApi(Build.VERSION_CODES.R)
fun MediaDeleteReq(resolver: ContentResolver, uris: List<Uri>): IntentSenderRequest {
    val _pendingIntent = MediaStore.createDeleteRequest(resolver, uris)
    val _intentSenderRequest = IntentSenderRequest.Builder(
        _pendingIntent.intentSender
    ).build()
    return _intentSenderRequest
}
val _uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val _ids = arrayOf(1000000033, 1000000034, 1000000035, 1000000036)

onDelete(_uri, _ids)
メディアテーブルの状態
# pwd
/data/data/com.google.android.providers.media.module/databases
# sqlite3 ./external.db
sqlite> .headers on
sqlite> .mode column
sqlite> select _id,_display_name,_data from images;
_id         _display_name  _data
----------  -------------  -------------------------------------
1000000033  leaf.jpg       /storage/emulated/0/Pictures/leaf.jpg
1000000034  donguri.jpg    /storage/emulated/0/Pictures/donguri.
1000000035  bike.jpg       /storage/emulated/0/Pictures/bike.jpg
1000000036  fall.jpg       /storage/emulated/0/Pictures/fall.jpg

※エミュレータ(API 33)で確認
メディアデータの状態
# pwd
/storage/emulated/0
# ls -l Pictures/* */.thumbnails/*
-rw-rw---- 1 u0_a163 media_rw  34088 2025-08-23 00:50 Pictures/.thumbnails/1000000033.jpg
-rw-rw---- 1 u0_a163 media_rw  16914 2025-08-23 00:51 Pictures/.thumbnails/1000000034.jpg
-rw-rw---- 1 u0_a163 media_rw  22749 2025-08-23 00:51 Pictures/.thumbnails/1000000035.jpg
-rw-rw---- 1 u0_a163 media_rw  32504 2025-08-23 00:52 Pictures/.thumbnails/1000000036.jpg
-rwxrwx--- 1 u0_a163 media_rw 126143 2025-08-23 00:51 Pictures/bike.jpg
-rwxrwx--- 1 u0_a163 media_rw  54206 2025-08-23 00:51 Pictures/donguri.jpg
-rwxrwx--- 1 u0_a163 media_rw  82076 2025-08-23 00:52 Pictures/fall.jpg
-rwxrwx--- 1 u0_a163 media_rw  47891 2025-08-23 00:50 Pictures/leaf.jpg

※エミュレータ(API 33)で確認

ダイアログに表示されるサムネイルは、表示範囲から溢れたものが、重ねられて表示されます。つまり、省略されてしまいます。

「許可・拒否」という重大な判断をユーザーに求めているのに、判断材料になる情報を省略してもよいのか?!、疑問です。ですので、私としてはグループ指定をお勧めしません。

許可・拒否ダイアログ(group)

「許可」となった(グループ指定された)URIは、このアプリにより削除が可能です。

そして、システムにより一括で消去されます。

スポンサーリンク

関連記事:

ストレージのリソースは、内部メモリー、外部メモリー、クラウドの3つがあります。 外部メモリーは、主にSDカードです。 このSDカードはAndroid端末の世代が進むにつれて、扱いを変えてきました。 リソースとSDカードを主眼に置いたストレージの変遷について、まとめます。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
Adoptable Storage(API≧23、Android 6.0)が導入されて、SDカードの扱い方をユーザ側で指定できるようになりました。 「Adoptable Storage」について、まとめます。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
ストレージはデータの用途別に記憶領域が分けられます。 保存先を守らないと、セキュリティリスクが発生したり、他のアプリと協調した動作が出来なくなったり、します。 ですので、適切な場所へデータを保存しましょう。 今回は「用途別記憶領域とボリューム」について、まとめます。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
ストレージへアクセスする方法は、「扱うデータの種類」「アクセス先」「セキュリティの確保」などの要件により、最適なアクセス方法が存在するので、使い分けが必要です。 今回は「アクセスする方法」について、まとめます。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
「内部ストレージ」にアクセスする方法について、まとめます。 「内部ストレージ」は、セキュリティが高いうえに、端末の内部メモリーに構築されるので、必ず存在します。 使いたい時に安心して使え、もっとも使い勝手のよいストレージと言えます。 アプリのデータを保存したいならば、保存先として真っ先に考えるストレージです。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
外部ストレージへアクセスするアプリは、パーミッション(許可)をユーザーから取得する必要があります。 パーミッションを得たアプリは、許可された権限の範囲内で、ストレージへアクセスが可能です。 これらは、ユーザーデータのプライバシー保護と密接に関係しています。 プライバシー保護をより強固にするために、パーミッションと権限の範囲の仕様は改変されてきました。 今回は、改変の歴史を辿りつつ、「パーミッションと権限の範囲」について、まとめます。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
「外部ストレージ_プライマリ」にアクセスする方法について、まとめます。 「外部ストレージ_プライマリ」は、アプリ間で共有するデータを保存するストレージです。 主にカメラ、音楽プレーヤー、動画プレーヤーで扱うデータを保存します。 「外部ストレージ_プライマリ」に保存したデータは、他のアプリに公開することになるので、秘匿性の高いデータの保存に適しません。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
「外部ストレージ_セカンダリ」にアクセスする方法について、まとめます。 「外部ストレージ_セカンダリ」は、デバイス間(または、アプリ間)で共有するデータを保存するストレージです。 デバイスとは、他の携帯端末やパソコンなどを指します。 ストレージがSDカードのような取り外し可能なリソース上に構築されているので、データの共有は取り外したストレージを他のデバイスへ取り付けて行います。 「外部ストレージ_セカンダリ」に保存したデータは、他のデバイスに公開することになるので、秘匿性の高いデータの保存に適しません。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
「外部ストレージ_プライマリ」に保存されるメディアデータは、Media Storeと呼ばれる特別な方法でアクセスします。 他の方法も可能ですが、Media Storeが最も適した方法です。 この「メディアデータへMedia Storeでアクセス」する方法を、まとめます。 この記事は、Imagesデータ編です。また、基本的なアクセス(読み書き)に的を絞って説明しています。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
メディアデータはデータベースによって管理されています。 Media Store APIを使ってメディアデータへアクセスしているのであれば、データベースの所在や構成などを意識する必要はありません。 ただ、「より複雑な制御をしたい場合」や「デバックを効率化したい場合」などに、データベースの知識が役立ちます。 今回は「メディアデータのデータベースによる管理」について、まとめます。 この記事は、Imagesデータ編です。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
「外部ストレージ_プライマリ」に保存されるメディアデータは、Media Storeと呼ばれる特別な方法でアクセスします。 他の方法も可能ですが、Media Storeが最も適した方法です。 この「メディアデータへMedia Storeでアクセス」する方法を、まとめます。 この記事は、Imagesデータ編です。また、基本的なアクセス(読み書き)に的を絞って説明しています。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
メディアデータはデータベースによって管理されています。 Media Store APIを使ってメディアデータへアクセスしているのであれば、データベースの所在や構成などを意識する必要はありません。 ただ、「より複雑な制御をしたい場合」や「デバックを効率化したい場合」などに、データベースの知識が役立ちます。 今回は「メディアデータのデータベースによる管理」について、まとめます。 この記事は、Imagesデータ編です。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
「外部ストレージ_プライマリ」に保存されるメディアデータは、Media Storeと呼ばれる特別な方法でアクセスします。 他の方法も可能ですが、Media Storeが最も適した方法です。 別の記事で「メディアデータへMedia Storeでアクセス」する方法を、まとめました。 その記事は、基本的なアクセスとして「追加(insert)&読み書き」のみを説明しています。 この記事は、上記に加えて「参照(query)・更新(update)・削除(delete)」を説明します。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
「外部ストレージ_プライマリ」に保存されるメディアデータは、データベースで管理されます。 Media Storeを使うと、このデータベースを通してメディアデータへアクセスできます。 データベース(SQLite)が持つ検索機能を使ってアクセス対象を絞り込めるので、とても便利です。 この記事は、「絞り込みアクセス」について、まとめます。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
メディアデータはMedia Storeを使ったアクセスが最適です。 その理由は、メディア特有の付加情報を使って、データを管理できるからです。 管理されたデータはブログラムから扱い易いです。また、ユーザの使い勝手(エクスペリエンス)も向上します。 バケットはメディアデータを管理する付加情報の一つです。 無くてもアクセスは可能ですが、積極的に取り入れた方が良いと思います。 今回は「ファイル名とバケットを指定したアクセス」について、まとめます。 「API≦28」と「API≧29」で動作が異なります。注意して下さい。 ※環境:Android Studio Narwhal Feature Drop | 2025.1.2 ...
メディアデータはMedia Storeを使ったアクセスが最適です。 その理由は、メディア特有の付加情報を使って、データを管理できるからです。 管理されたデータはブログラムから扱い易いです。また、ユーザの使い勝手(エクスペリエンス)も向上します。 バケットはメディアデータを管理する付加情報の一つです。 無くてもアクセスは可能ですが、積極的に取り入れた方が良いと思います。 今回は「ファイル名とバケットを指定したアクセス」について、まとめます。 「API≦28」と「API≧29」で動作が異なります。注意して下さい。 ※環境:Android Studio Narwhal Feature Drop | 2025.1.2 ...
メディアデータはアプリ間で共有されるので、アプリがデータへアクセスするには、アクセス許可が必要です。 この許可の権限の範囲は、プライバシー保護の観点から、徐々に狭められてきました。 そして、対象範囲別ストレージにおいて、他アプリが所有するファイルへの書き込みは、URI(ファイル)毎の許可が必要になっています。 許可の取得は、ユーザと対話をする形式(ダイアログ)で行われます。手順が少し複雑です。 ここに「他アプリが所有するファイルへ書き込み」する方法を、まとめます。 ※環境:Android Studio Narwhal Feature Drop | 2025.1.2 Patch 1 ...
スポンサーリンク