Storage Access Framework(SAF)は、ファイルピッカーでアクセス対処のファイルを指定する仕組みです。
ファイルピッカーの初期フォルダは、前回の状態が引き継がれます。
また、初期フォルダを意図的に指定することも可能です。
今回は「初期フォルダの指定方法」について、まとめます。
※環境:Android Studio Narwhal Feature Drop | 2025.1.2 Patch 2
目次
初期フォルダの指定方法
初期フォルダの指定方法は、リクエスト(Intent)でEXTRA_INITIAL_URIにフォルダのURIを設定します。
※ストレージへSAFでアクセスする方法については以下を参照
「外部ストレージへStorage Access Frameworkでアクセス」
「SAFのACTION_OPEN_DOCUMENT_TREEでフォルダを指定」
fun TreeOpenIntent(initUri: Uri) = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply { putExtra(EXTRA_INITIAL_URI, initUri) }
fun FileCreateIntent(filename: String, initUri: Uri) = Intent(Intent.ACTION_CREATE_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) putExtra(Intent.EXTRA_TITLE, filename) putExtra(EXTRA_INITIAL_URI, initUri) type = "*/*" }
fun FileOpenIntent(initUri: Uri) = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) putExtra(EXTRA_INITIAL_URI, initUri) type = "*/*" }
初期フォルダの指定例
以下は、初期フォルダの指定例です。※エミュレータ(API 33)で確認
例:既存のフォルダ
初期フォルダを外部ストレージ_プライマリのDCIMフォルダにします。
// ↓↓ content://com.android.externalstorage.documents/document/primary%3ADCIM val _initUri = DocumentsContract.buildDocumentUri( "com.android.externalstorage.documents", // authority "primary:${Environment.DIRECTORY_DCIM}" // documentId ) _safReadTreePicker.launch(TreeOpenIntent(_initUri)) //
# pwd /storage/emulated/0 # ls -ld * DCIM/* drwxrws--- 2 u0_a173 media_rw 4096 2025-08-29 04:54 Alarms drwxrws--x 5 media_rw media_rw 4096 2025-08-29 04:54 Android drwxrws--- 2 u0_a173 media_rw 4096 2025-08-29 04:54 Audiobooks drwxrws--- 2 u0_a173 media_rw 4096 2025-08-29 05:16 DCIM -rw-rw---- 1 u0_a173 media_rw 61009 2025-08-29 05:16 DCIM/bike.jpg -rw-rw---- 1 u0_a173 media_rw 54206 2025-08-29 05:16 DCIM/donguri.jpg -rw-rw---- 1 u0_a173 media_rw 84773 2025-08-29 05:16 DCIM/leaf1.jpg -rw-rw---- 1 u0_a173 media_rw 82076 2025-08-29 05:16 DCIM/leaf2.jpg ...
例:トップフォルダ
初期フォルダを外部ストレージ_プライマリのトップフォルダにします。
// ↓↓ content://com.android.externalstorage.documents/document/primary%3A val _initUri = DocumentsContract.buildDocumentUri( "com.android.externalstorage.documents", // authority "primary:" // documentId ) _safReadTreePicker.launch(TreeOpenIntent(_initUri)) //
注意:Documentsプロバイダー向けURIを使う
初期フォルダとして指定するURIは、Documentsプロバイダー向けURIを使います。
Documentsプロバイダー向けURIは、次のようなものです。
content://com.android.externalstorage.documents/document/primary%3ADCIM content://com.android.externalstorage.documents/document/primary%3A ※赤字の部分は以下のようになる。初期フォルダの指定に影響しない。 ACTION_CREATE_DOCUMENT -> "document" ACTION_OPEN_DOCUMENT -> "document" ACTION_OPEN_DOCUMENT_TREE -> "tree"
その他のURI(例:Mediaプロバイダー向け、など)は受け付けません。
URIを生成するユーティリティとして、DocumentsContract.buildDocumentUriが用意されています。※使用方法は上記の「初期フォルダの指定例」を参照
問題点:Authorityは埋め込み
Androidシステム内に複数のプロバイダーが存在しています。SAFが通信を行うDocumentsプロバイダーは、その中の一つです。
対象のプロバイダーはUriのAuthority部で識別されます。
DocumentsプロバイダーのAuthorityをプログラム的に参照したいところですが、API内のAuthorityパラメータが一般向けに公開されていません。
... /** * External Storage Provider's authority string * {@hide} */ @SystemApi public static final String EXTERNAL_STORAGE_PROVIDER_AUTHORITY = "com.android.externalstorage.documents"; ...
ですので、プロブラム中に値を埋め込むしかありません。
val _initUri = DocumentsContract.buildDocumentUri( "com.android.externalstorage.documents", // authority(埋め込み) "primary:${Environment.DIRECTORY_DCIM}" // documentId )
問題点:外部ストレージ_セカンダリの扱い
外部ストレージ_セカンダリのフォルダのURIは、次のようになります。
content://com.android.externalstorage.documents/document/1EEF-081A%3ADCIM content://com.android.externalstorage.documents/document/1EEF-081A%3A ※赤字の部分は以下のようになる。初期フォルダの指定に影響しない。 ACTION_CREATE_DOCUMENT -> "document" ACTION_OPEN_DOCUMENT -> "document" ACTION_OPEN_DOCUMENT_TREE -> "tree"
エミュレータでは、URIのQuery部が「1EEF-081A」です。「1EEF-081A」は外部ストレージ_セカンダリのuuidです。
ですので、次のように生成したURIで、初期フォルダが指定できます。
val _initUri = DocumentsContract.buildDocumentUri( "com.android.externalstorage.documents", // authority(埋め込み) "1EEF-081A:${Environment.DIRECTORY_DCIM}" // documentId )
しかし、この方法は問題点があります。
- ・「セカンダリがuuidで表現される」という説明がドキュメントにない
- ・uuidの計算アルゴリズムがエミュレータと同じである保証がない
つまり、端末のメーカによって、扱いが違うかも知れません。
問題点を考えると、「初期フォルダの指定を行わない」とするのが、良さそうです。
関連記事: