Android Q時点のコードです。
Bitmap->Uri
class ImageRepository( private val application: Application ) { private val IMAGE_PATH = "image" private val imagePath = application.getFileStreamPath(IMAGE_PATH).apply { // シェアのために一時的に保存するだけなので毎回消す deleteRecursively() mkdir() } // bitmapは適宜recycle()する suspend fun writeBitmap(fileNameWithoutExtension: String, bitmap: Bitmap): Uri = withContext(Dispatchers.IO) { // 重要: 画像拡張子は設定する(パーミッションエラーになる) val outFile = File(imagePath, "${fileNameWithoutExtension}.png").also { outFile -> FileOutputStream(outFile).use { it.write(bitmap.toByteArray()) } } return@withContext FileProvider.getUriForFile(application, application.packageName, outFile) } private fun Bitmap.toByteArray(): ByteArray { return ByteArrayOutputStream().use { compress(Bitmap.CompressFormat.PNG, 100, it) it.toByteArray() } } }
権限の宣言
追加するuses-permission
無し
AndroidManifest.xml
providerの部分が重要
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="your.app.package.name"> <provider android:name="androidx.core.content.FileProvider" android:authorities="your.app.package.name" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> </provider> </manifest>
provider_paths.xml
ImageRepository.IMAGE_PATHで設定したものと同じ物を設定する。
これで外部アプリが見れるディレクトリを制限できる。
<?xml version="1.0" encoding="utf-8"?> <paths> <files-path name="external_files" path="image" /> </paths>
暗黙的Intentで共有
provider_paths.xmlで外部からファイルを見られるようになりましたが、これだけではどんなアプリからでも見られてセキュリティ的によろしくないのでIntent.flagsに見られる権限を付与する事でセキュリティの問題が解決されています。
class Hoge( imageRepository : ImageRepository ) { suspend fun share(text: String) = withContext(Dispatchers.Main) { val bitmapUri = bitmap ?.let { imageRepository.writeBitmap("${System.nanoTime()}", it) } val intent = Intent.createChooser(Intent(Intent.ACTION_SEND).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION putExtra(Intent.EXTRA_TEXT, text) bitmapUri?.let { putExtra(Intent.EXTRA_STREAM, it) } type = "image/*" }, "シェア") // 後はこのIntentをstartActivity()の引数に与えるだけ } }