添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
15
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Android: 画像ファイルの共有は FileProvider と ShareCompat を使う

Last updated at Posted at 2021-09-25

Android で画像ファイルを他のアプリへ共有するには、ACTION_SEND Intent または ShareCompat を使います。

共有するファイルの Uri は FileProvider で生成します。

この記事では、FileProvider + ShareCompat での実装についてまとめています。

画像のシェアについては以下が公式のドキュメントとなります。

AndroidX Core 1.5.0 以上を利用していること。

AndroidX Core 1.5.0-beta01 から、後述の Android 10 ShareSheet でのプレビュー問題が修正されています

実装: FileProvider の設定

画像ファイルを cacheDir へ保存し、Intent 発行時に一時的に外部のアプリに読み取り権限を与えられるようにします。

AndroidManixest.xml を設定します。 ${applicationId} もそのまま記述します。

app/src/main/AndroidManifest.xml

<manifest...> <application...> <provider android:name= "androidx.core.content.FileProvider" android:authorities= "${applicationId}.fileprovider" android:exported= "false" android:grantUriPermissions= "true" > <meta-data android:name= "android.support.FILE_PROVIDER_PATHS" android:resource= "@xml/provider_path" /> </provider>

provider_path.xml を設置します。

app/src/main/res/xml/provider_path.xml

<paths>
    <cache-path name="cache" path="." />
</paths>

今回は cacheDir 以下へ共有画像ファイルを一時保存するため、cache-path タグを設定しています。FileProvider タグ一覧は以下のドキュメントを確認してください。

サンプルコードでは、res/raw/xml/image.png が設置されている前提です。

class MyActivity: AppCompatActivity(R.layout.activity_main) {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.bind(findViewById<ViewGroup>(android.R.id.content)[0])
        binding.button.setOnClickListener {
            val imageUri = resources.openRawResource(R.raw.image).use { input ->
                val file = File("$cacheDir/image.png")
                file.createNewFile()
                file.outputStream().use { output ->
                    input.copyTo(output)
                FileProvider.getUriForFile(this, "$packageName.fileprovider", file)
            ShareCompat.IntentBuilder(this).apply {
                setChooserTitle("share title")
                setText




    
("share message")
                setStream(imageUri)
                setType("image/png")
            }.startChooser()
//...

これを実行すると、以下のような表示となります。上部に大きく表示されている画像が共有しようとした画像のプレビュー、2段目が OS によってレコメンドされた共有相手のユーザー一覧、3段目が共有を受け取れるアプリ一覧です。

ShareSheet の表示は OS バージョンによって異なります。

| OS バージョン | ShareSheet | シェア後の表示 (SMSアプリ) |
| --- | --- | --- | --- |
| Android 11 | * 画像プレビューだけが表示されている
* ChooserTitle の表示がない
* Text の表示がない
| |
| (参考) Android 7 | * ChooserTitle だけが表示されている
| |

ShareCompat.IntentBuilder の設定項目

ShareCompat.IntentBuilder は以下の設定項目があります。

setHtmlText() Intent.EXTRA_HTML_TEXT を設定し、
Intent.EXTRA_TEXT に Html.fromHtml() を適用した Spanned を設定する setStream() Intent.EXTRA_STREAM setSubject() Intent.EXTRA_SUBJECT setText() Intent.EXTRA_TEXT setType() Intent.type ShareCompat を使わず、ACTION_SEND Intent を組み立てる実装

ShareCompat を使う実装がお勧めですが、ShareCompat を使わずに ACTION_SEND Intent を組み立てる実装は以下の通りとなります。

class MyActivity: AppCompatActivity(R.layout.activity_main) {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.bind(findViewById<ViewGroup>(android.R.id.content)[0])
        binding.button.setOnClickListener {
            val imageUri = resources.openRawResource(R.raw.image).use { input ->
                val file = File("$cacheDir/image.png")
                file.createNewFile()
                file.outputStream().use { output ->
                    input.copyTo(output)
                FileProvider.getUriForFile(this, "$packageName.fileprovider", file)
            startActivity(
                Intent.createChooser(
                    Intent(Intent.ACTION_SEND).apply {
                        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                        putExtra(Intent.EXTRA_TEXT, "share message")
                        putExtra(Intent.EXTRA_STREAM, imageUri)
                        clipData = ClipData.newRawUri(null, imageUri)
                        type = "image/png"
                    "share title"
//...

Android 10 以上では、ShareSheet 上で画像のプレビュー表示がありますが、EXTRA_STREAM に設定するだけでは以下のエラーが発生し、ShareSheet 上の画像プレビューが表示されません。

E/DatabaseUtils: Writing exception to parcel
    java.lang.SecurityException: Permission Denial: reading androidx.core.content.FileProvider uri content://com.exmaple.myapplication.fileprovider/cache/image.png from pid=8822, uid=1000 requires the provider be exported, or grantUriPermission()
        at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:820)
        at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:684)
        at android.content.ContentProvider$Transport.enforceFilePermission(ContentProvider.java:674)
        at android.content.ContentProvider$Transport.openTypedAssetFile(ContentProvider.java:548)
        at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:327)
        at android.os.Binder.execTransactInternal(Binder.java:1154)
        at android.os.Binder.execTransact(Binder.java:1123)
E/DatabaseUtils: Writing exception to parcel
    java.lang.SecurityException: Permission Denial: reading androidx.core.content.FileProvider uri content://com.example.myapplication.fileprovider/cache/image.png from pid=8822, uid=1000 requires the provider be exported, or grantUriPermission()
        at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:820)
        at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:684)
        at android.content.ContentProvider$Transport.enforceFilePermission(ContentProvider.java:674)
        at android.content.ContentProvider$Transport.openTypedAssetFile(ContentProvider.java:548)
        at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:327)
        at android.os.Binder.execTransactInternal(Binder.java:1154)
        at android.os.Binder.execTransact(Binder.java:1123)

このエラーを回避するために、FLAG_GRANT_READ_URI_PERMISSION と clipData を設定しています。

ShareCompat を使う場合は ShareCompat が上記処理をしてくれるため、Android 10 以上でも問題なく動作します。

15
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?