CatHand Blog

アプリ開発やMac弄り

Androidでファイルオープン/セーブダイアログを出す

val intent = Intent()
        .addCategory(Intent.CATEGORY_OPENABLE)
        .setType("*/*")
        .setAction(Intent.ACTION_GET_CONTENT)
startActivityForResult(Intent.createChooser(intent, "Select a file."), REQUEST_CODE)

でファイルオープンのActivityが起動します。

setType("image/*") とかすると、画像だけを選択させることができます。

結果は onActivityResult()

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == 1 && resultCode == Activity.RESULT_OK) {
        val uri = data?.getData() ?: return
        ...
    }
}

のように Uri がとれます。

Uri からは ContentResolver を介して InputStream が取れます。

val strm = contentResolver.openInputStream(uri)

で、ストリームから読み出せばいいんですが、ファイルパスや File オブジェクトしか受け付けないAPIを呼ぶときに困ります。

Uri を path に変換する方法は、調べると↓等がみつかります。

stackoverflow.com

ここでいくつか紹介されている方法を試しましたが、Androidのバージョンや機種によって正しく動作しない場合がありました。

確実なのはアプリ内のキャッシュに一度コピーしてそれを渡す方法です。

val tmpFile = File(cacheDir, "temp.data")
Files.copy(strm, tmpFile.toPath(), StandardCopyOption.REPLACE_EXISTING)

参照するファイルのサイズが大きくなければこれで問題なさそうです。

同様にセーブダイアログも

val intent = Intent()
            .addCategory(Intent.CATEGORY_OPENABLE)
            .setAction(Intent.ACTION_CREATE_DOCUMENT)
            .putExtra(Intent.EXTRA_TITLE, fileName)
startActivityForResult(Intent.createChooser(intent, "Select save destination."), 1)

のように出すことができます。

デフォルトのファイル名を指定する場合、 putExtra(Intent.EXTRA_TITLE, fileName) のようにします。

こちらもファイルオープン時と同様に Uri が取得できますので、OutputStream を使って書き込みができます。