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の計算アルゴリズムがエミュレータと同じである保証がない
つまり、端末のメーカによって、扱いが違うかも知れません。
問題点を考えると、「初期フォルダの指定を行わない」とするのが、良さそうです。
関連記事:
