Android 10でIntentを使ってキャプチャした画像を外部フォルダに任意のディレクトリを作って保存する

今更ながらAndroid 10以降でIntent(MediaStore.ACTION_IMAGE_CAPTURE)を使ってキャプチャした画像を、各メディア直下のディレクトリではなくアプリ専用のディレクトリを作って保存する方法について調べました。

準備

まずxmlの設定から。AndroidManifest.xmlをいじります。
AndroidManifest.xml<application>タグ内にFileProviderの記述を追加します。

1
2
3
4
5
6
7
8
9
10
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">

<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider"/>
</provider>

<meta-data>タグ内で参照しているfile_provider.xmlはres/xml配下に作成し中身は下記のようになっています。

1
2
3
<paths>
<external-path name="images" path="Pictures/Hoge" />
</paths>

Uri取得

次に画像の保存先をMediaStoreから取得します。

1
2
3
4
5
6
7
8
9
10
val fileName = "hoge.jpg"
val values = ContentValues()
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
values.put(
MediaStore.Images.Media.RELATIVE_PATH,
Environment.DIRECTORY_PICTURES + File.separator + "Hoge"
)
values.put(MediaStore.Images.Media.MIME_TYPE, "image/*")
val contentUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
imageUri = contentResolver.insert(contentUri, values)

RELATIVE_PATHfile_provider.xmlで記述したpathの値と同じにします。
imageUriはプロパティとして保持しておきonActivityResultで再利用します。

Intentを投げる

事前に取得しておいたUriをIntentにくっつけて投げます。

1
2
3
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
startActivityForResult(intent, REQUEST_CODE)

カメラアプリがいくつか候補に出てくると思うので好きなアプリを選択して写真を撮ります。

戻り値を受け取る

ImageDecoderというクラスを使ってBitmapを生成して、ImageViewにセットすれば終わりです。

1
2
3
4
5
if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
val source = ImageDecoder.createSource(contentResolver, imageUri!!)
val bitmap = ImageDecoder.decodeBitmap(source)
findViewById<ImageView>(R.id.image).setImageBitmap(bitmap)
}

感想

公式動画やブログでも再三言われていたことですが、Google的にはファイルパスは極力使わないでUriを使って画像を扱って欲しいのだなと改めて思う内容でした。
近い将来、動画をファイルパスでしか読み込めないライブラリたちがUriをサポートしたらファイルパスの参照も禁止になってしまうのかもしれません。
そう考えるとできるだけファイルパスに依存しない実装にしておくのが吉なのでしょう。知らんけど。

参考サイト