この記事は
AndroidでSDカードに何かを保存したい
使うものは
使わないものは
- getExternalDirectoryとか
まずコード
SDカードへのアクセス権限をユーザに求める
private fun requestSecondaryStorageAccess() {
val sm = getSystemService(STORAGE_SERVICE) as StorageManager
sm.storageVolumes.find { storageVolume -> storageVolume.isRemovable }.let {
val intent = it.createAccessIntent(null)
startActivityForResult(intent, REQUEST_SDCARD_ACCESS)
}
}
権限付与されたURIをごにょごにょする
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_SDCARD_ACCESS && resultCode == RESULT_OK) {
data?.data?.let {
contentResolver.takePersistableUriPermission(
it,
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
val outUri = DocumentFile.fromTreeUri(this, it)?.createFile("text/plain", "sample")?.uri
outUri?.let{
contentResolver.openOutputStream(it)?.use{
it.write("hoge".toByteArray())
}
}
}
}
細かく
val sm = getSystemService(STORAGE_SERVICE) as StorageManager
sm.storageVolumes.find { storageVolume -> storageVolume.isRemovable }.let {
val intent = it.createAccessIntent(null)
startActivityForResult(intent, REQUEST_SDCARD_ACCESS)
}
- StorageManagerのgetStorageVolumes()で、利用可能な全てのStorageVolumeを取得し、isRemovable()で取り外し可能であることをチェックする(=SDカードである)
- SDカードであるStorageVolumeに対してcreateAccessIntent(null)でディレクトリアクセス権を要求するIntentを生成し、発行する
- createAccessIntentの引数にnullを与えるとルート権限、その他色々指定できる
- Android Q非対応(後述)
data?.data?.let {
contentResolver.takePersistableUriPermission(
it,
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
- ユーザがアクセス権限を与えたディレクトリのUriがdataに格納される
- このUriはそのままだと揮発するが、ContentResolverのtakePersistableUriPermissionでシステムにUriを覚えさせておくことが可能
- 端末を再起動するまで有効。一度覚えると再度ユーザに権限許可を求めずRESULT_OKが返される。
val outUri = DocumentFile.fromTreeUri(this, it)?.createFile("text/plain", "sample")?.uri
outUri?.let{
contentResolver.openOutputStream(it)?.use{
it.write("hoge".toByteArray())
}
}
- DocumentFileのfromTreeUriで「アクセス許可されたUri」からDocumentFileオブジェクトを生成
- この場合、SDカードのルートディレクトリを指す
- DocumentFileのcreateFileでmimeTypeとfilenameを指定してDocumentFileを生成し、Uriを取得
- 新ファイルのoutputStreamを生成し、hogeと書き込んだ
Android Q
createAccessIntentがDeprecatedとなった。余計なことしやがって!
ディレクトリピッカーを開いてユーザに権限を付けてもらう
val intent = volume.createOpenDocumentTreeIntent()
startActivityForResult(intent, REQUEST_SDCARD_ACCESS)
- SDカードの権限を付けますよ~という方法が封じられ、ユーザがディレクトリを指定することが必要になった。
- 因みにAndroidQでは見送られたが、来年(=2020?)にはSAFに完全移行しないとファイル周りは使えなくなるらしい。どこかに書いてあったけどソースは忘れました。ごめんなさい。