Android では、ユーザーがアプリ間でデータを共有するのに、2 つの方法があります。
- Android Sharesheet は主に、アプリの外部へのコンテンツの送信、別ユーザーへの直接のコンテンツの送信、またはその両方のために設計されています。たとえば、友人との URL の共有などです。
- Android インテント リゾルバは、明確に定義されたタスクの次の段階にデータを渡すのに最適です。たとえば、アプリから PDF を開き、ユーザーにビューアを選択させるなどです。
Android Sharesheet を使用する理由
Android Sharesheet を使用して、ユーザーに対するアプリ間での一貫性を持たせる。アプリでは、共有ターゲットの独自のリストを表示すること、および独自の Sharesheet を作成することは行わないでください。
Android Sharesheet を使用すると、ユーザーは適切なユーザーと情報を共有できます。関連するアプリの候補も表示され、すべてを 1 回のタップで行えます。Sharesheet は、カスタム ソリューションでは利用できないターゲットを提案でき、一貫したランキングを使用します。これは、Sharesheet が、システムのみが利用できるアプリとユーザー アクティビティに関する情報を考慮できるためです。
また、Android Sharesheet には、デベロッパーにとって便利な機能が多数あります。たとえば、次の操作が可能です。
- ユーザーがいつ共有を完了し、どこと共有したかを知る
- カスタム ターゲットとアプリ ターゲットを追加する
- Android 10(API レベル 29)以降で、リッチテキスト コンテンツのプレビューを提供する
- 特定のコンポーネント名に一致するターゲットを除外する
Android Sharesheet を使用する
すべてのタイプの共有で、インテントを作成し、そのアクションを Intent.ACTION_SEND に設定します。Android Sharesheet を表示するには、Intent.createChooser() を呼び出して Intent オブジェクトを渡します。このメソッドは、常に Android Sharesheet を表示するインテントのバージョンを返します。
テキスト コンテンツを送信する
Android Sharesheet の最も簡単で一般的な用途は、あるアクティビティから別のアクティビティにテキスト コンテンツを送信することです。たとえば、ほとんどのブラウザは、現在表示されているページの URL をテキストとして別のアプリと共有できます。これは、メールやソーシャル ネットワーキングを通じて記事やウェブサイトを友だちと共有するのに便利です。Jetpack Compose では、LocalContext.current を使用して現在のコンテキストを取得し、context.startActivity() を呼び出します。以下はその方法の例です。
fun shareText(context: Context) { val sendIntent: Intent = Intent().apply { action = ACTION_SEND putExtra(Intent.EXTRA_TEXT, "This is my text to send.") type = "text/plain" } val shareIntent = Intent.createChooser(sendIntent, null) context.startActivity(shareIntent) }
必要に応じて、メールの受信者(EXTRA_EMAIL、EXTRA_CC、EXTRA_BCC)やメールの件名(EXTRA_SUBJECT)などの追加情報を追加できます。
注: Gmail などの一部のメールアプリでは、EXTRA_EMAIL や EXTRA_CC などのエクストラに String[] が必要です。putExtra(String, String[]) を使用して、これらをインテントに追加します。
バイナリ コンテンツを送信する
ACTION_SEND アクションを使用してバイナリデータを共有します。次の例に示すように、適切な MIME タイプを設定し、エクストラ EXTRA_STREAM にデータへの URI を配置します。これは通常、画像の共有に使用されますが、任意のタイプのバイナリ コンテンツの共有にも使用できます。
fun shareBinaryContent(context: Context) { val shareIntent: Intent = Intent().apply { action = ACTION_SEND // Example: content://com.google.android.apps.photos.contentprovider/... val imageUri: Uri = Uri.parse("content://com.google.android.apps.photos.contentprovider/0/1/mediakey/1") putExtra(Intent.EXTRA_STREAM, imageUri) type = "image/jpeg" } context.startActivity(Intent.createChooser(shareIntent, null)) }
受信側のアプリには、Uri が指すデータにアクセスする権限が必要です。これには、次の 2 つの方法が推奨されます。
- 独自の
ContentProviderにデータを保存し、他のアプリにプロバイダにアクセスするための適切な権限があることを確認します。アクセス権を付与するメカニズムとして推奨されるのは、URI ごとの権限を使用することです。これは一時的なもので、受信側のアプリにのみアクセス権を付与します。このようなContentProviderを作成するには、FileProviderヘルパークラスを使用します。 - システム
MediaStoreを使用する。MediaStoreは本来、動画、音声、画像の MIME タイプのためのものです。ただし、Android 3.0(API レベル 11)以降では、メディア以外のタイプも保存できます。詳細については、MediaStore.Filesをご覧ください。scanFile()を使用してファイルをMediaStoreに挿入でき、その後、与えられたonScanCompleted()コールバックに、共有に適したcontent://スタイルのUriが渡されます。システムMediaStoreに追加されると、そのコンテンツはデバイス上のすべてのアプリからアクセスできるようになることに注意してください。
適切な MIME タイプを使用する
送信するデータに最も適した MIME タイプを指定します。たとえば、書式なしテキストを共有する場合は text/plain を使用します。Android でデータを送信する際の一般的な MIME タイプをいくつかご紹介します。
| 受信者が登録する | 送信者が送信する |
|---|---|
text/* |
|
image/* |
|
video/* |
|
| サポートされているファイル拡張子 | application/pdf |
MIME タイプをハードコードするのではなく、実行時に動的に決定するには、次のメソッドを使用します。
content://URI(FileProviderやMediaStoreで提供される URI など)の場合は、ContentResolver.getType()を呼び出します。- ファイル拡張子の場合は、
MimeTypeMap.getSingleton().getMimeTypeFromExtension()を呼び出します。 - ストレージ アクセス フレームワークを使用する場合は、
DocumentFile.getType()をクエリすることもできます。
特定の MIME タイプを特定できない場合は、一般的なバイナリデータには application/octet-stream を使用し、最後の手段として */* を使用します。
MIME タイプの詳細については、MIME メディアタイプの IANA 公式レジストリをご覧ください。
指定された MIME タイプによっては、Android Sharesheet にコンテンツのプレビューが表示されることがあります。一部のプレビュー機能は、特定のタイプでのみ使用できます。
複数のコンテンツを共有する
複数のコンテンツを共有するには、ACTION_SEND_MULTIPLE アクションと、コンテンツを指す URI のリストを使用します。MIME タイプは、共有するコンテンツの組み合わせによって異なります。たとえば、3 つの JPEG 画像を共有する場合は、タイプ "image/jpg" を使用します。画像タイプが混在している場合は、"image/*" を使用して、あらゆるタイプの画像を処理するアクティビティを照合します。複数のタイプを混在させて共有することも可能ですが、受信側にとって送信内容が不明瞭になるため、避けてください。複数のタイプを送信する必要がある場合は、"*/*" を使用します。データの解析と処理は、受信側のアプリケーションで行われます。次の例をご覧ください。
fun shareMultiple(context: Context) { val imageUris: ArrayList<Uri> = arrayListOf( Uri.parse("content://com.google.android.apps.photos.contentprovider/0/1/mediakey/1"), Uri.parse("content://com.google.android.apps.photos.contentprovider/0/1/mediakey/2") ) val shareIntent = Intent().apply { action = Intent.ACTION_SEND_MULTIPLE putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris) type = "image/*" } context.startActivity(Intent.createChooser(shareIntent, null)) }
指定した Uri オブジェクトが、受信アプリケーションがアクセスできるデータを指すようにしてください。
テキスト プレビューにリッチ コンテンツを追加する
Android 10(API レベル 29)以降、Android Sharesheet には共有されるテキストのプレビューが表示されます。場合によっては、共有されているテキストが理解しにくいことがあります。https://www.google.com/search?ei=2rRVXcLkJajM0PEPoLy7oA4 のような複雑な URL を共有することを考えてみましょう。より詳細なプレビューを表示することで、ユーザーは共有される内容を把握できます。
テキストをプレビューする場合は、タイトル、サムネイル画像、またはその両方を設定できます。Intent.createChooser() を呼び出す前に Intent.EXTRA_TITLE に説明を追加し、ClipData を使用して関連するサムネイルを追加します。
注: 画像コンテンツの URI は FileProvider から提供されます。通常は、構成済みの <cache-path> から提供されます。詳細については、ファイルの共有をご覧ください。サムネイルとして使用する画像を読み取るための適切な権限を Sharesheet に付与してください。詳しくは、Intent.FLAG_GRANT_READ_URI_PERMISSION をご覧ください。
次の例をご覧ください。
fun richContentToTextPreviewShares(context: Context) { val share = Intent.createChooser( Intent().apply { action = ACTION_SEND putExtra(Intent.EXTRA_TEXT, "https://developer.android.com/training/sharing/") // (Optional) Here you're setting the title of the content putExtra(Intent.EXTRA_TITLE, "Introducing content previews") // (Optional) Here you're passing a content URI to an image to be displayed data = Uri.parse("content://com.google.android.apps.photos.contentprovider/0/1/mediakey/A123456789") flags = Intent.FLAG_GRANT_READ_URI_PERMISSION }, null ) context.startActivity(share) }
プレビューは次のようになります。
Sharesheet にカスタム アクションを追加する
Android 14(API レベル 34)以降では、アプリで Android Sharesheet にカスタム アクションを追加できます。カスタム アクションは、Android Sharesheet の上部に小さなアクション アイコンとして表示されます。アプリは、アイコンがクリックされたときに呼び出されるアクションとして任意の Intent を指定できます。
Android Sharesheet にカスタム アクションを追加するには、まず ChooserAction.Builder を使用して ChooserAction を作成します。アイコンがクリックされたときに呼び出されるアクションとして PendingIntent を指定できます。すべてのカスタム アクションを含む配列を作成し、共有 Intent の EXTRA_CHOOSER_CUSTOM_ACTIONS として指定します。
fun sharesheetCustomActions(context: Context, previewText: String) { val sendIntent = Intent(ACTION_SEND) .setType("text/plain") .putExtra(Intent.EXTRA_TEXT, previewText) val shareIntent = Intent.createChooser(sendIntent, null) val customActions = arrayOf( ChooserAction.Builder( Icon.createWithResource(context, R.drawable.ic_logo), "Custom", PendingIntent.getBroadcast( context, 1, Intent(Intent.ACTION_VIEW), PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_CANCEL_CURRENT ) ).build() ) shareIntent.putExtra(Intent.EXTRA_CHOOSER_CUSTOM_ACTIONS, customActions) context.startActivity(shareIntent) }
カスタム ターゲットを追加する
Android Sharesheet では、Intent.EXTRA_INITIAL_INTENTS を使用して、アプリの候補の前に表示されるカスタム アクティビティ インテントを 2 つまで指定できます。直接共有ターゲット(会話や連絡先など)の場合は、代わりに ShortcutManagerCompat を使用して動的ショートカットを公開します(直接共有ターゲットを提供するをご覧ください)。
Intent.createChooser() を呼び出した後に、共有インテントに Intent.EXTRA_INITIAL_INTENTS を追加します。
val sendIntent = Intent(ACTION_SEND).apply { type = "text/plain" putExtra(Intent.EXTRA_TEXT, previewText) } val customIntent = Intent().apply { component = ComponentName(context.packageName, "${context.packageName}.CustomActivity") } val shareIntent = Intent.createChooser(sendIntent, null).apply { putExtra( Intent.EXTRA_INITIAL_INTENTS, arrayOf(customIntent) ) } context.startActivity(shareIntent)
この機能の使用には注意が必要です。カスタムの Intent を追加するたびに、システムが提案する候補の数が少なくなります。通常、カスタム ターゲットを追加することはおすすめしません。Intent.EXTRA_INITIAL_INTENTS を追加する一般的で適切な例は、共有コンテンツに対してユーザーが実行できる追加のアクションを提供する場合です。たとえば、ユーザーが画像を共有し、Intent.EXTRA_INITIAL_INTENTS を使用して、画像の代わりにリンクを送信できるようにします。
コンポーネントごとに特定のターゲットを除外する
Intent.EXTRA_EXCLUDE_COMPONENTS を指定すると、特定のターゲットを除外できます。この操作は、管理対象のターゲットを削除する場合にのみ行ってください。一般的なユースケースは、ユーザーがアプリ内から共有する場合に、アプリの共有ターゲットを非表示にすることです。これは、ユーザーの意図がアプリ外での共有である可能性が高いためです。
Intent.createChooser() を呼び出した後、インテントに Intent.EXTRA_EXCLUDE_COMPONENTS を追加します。
fun excludeSpecificTargets(context: Context) { val share = Intent.createChooser(Intent(ACTION_SEND), null).apply { // Only use for components you have control over val excludedComponentNames = arrayOf(ComponentName("com.example.android", "ExampleClass")) putExtra(Intent.EXTRA_EXCLUDE_COMPONENTS, excludedComponentNames) } context.startActivity(share) }
共有に関する情報を取得する
ユーザーがいつ共有しているか、どのターゲットを選択しているかを知ることは有用です。Android Sharesheet では、ユーザーが IntentSender を使用して選択したターゲットの ComponentName を提供することで、この情報を取得できます。
まず、BroadcastReceiver の PendingIntent を作成し、Intent.createChooser() で IntentSender を指定します。
fun infoAboutSharing(context: Context, requestCode: Int) { var share = Intent(ACTION_SEND) // ... val pi = PendingIntent.getBroadcast( context, requestCode, Intent(context, ShareBroadcastReceiver::class.java), PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) share = Intent.createChooser(share, null, pi.intentSender) context.startActivity(share) }
MyBroadcastReceiver でコールバックを受信し、Intent.EXTRA_CHOOSER_RESULT を確認します。
override fun onReceive(context: Context?, intent: Intent) { val TAG = ShareBroadcastReceiver::class.simpleName val chooserResult: ChooserResult? = IntentCompat.getParcelableExtra( intent, Intent.EXTRA_CHOOSER_RESULT, ChooserResult::class.java, ) chooserResult?.let { Log.i(TAG, "Share callback: isShortcut: ${it.isShortcut}, type: ${typeToString(it.type)}, componentName: ${it.selectedComponent}", ) } ?: Log.i(TAG, "chooserResult is null") }
詳細については、プラットフォーム共有のサンプルをご覧ください。
Android インテント リゾルバを使用する
ACTION_SEND インテント リゾルバ。Android インテント リゾルバが最もよく使われるのは、明確に定義されたタスクフローの一部として別のアプリにデータを渡す場合です。
Android インテント リゾルバを使用するには、Android Sharesheet を呼び出す場合と同様に、インテントを作成してエクストラを追加します。ただし、Intent.createChooser() は呼び出さないでください。
ACTION_SEND および MIME タイプが一致するフィルタを持つアプリケーションが複数インストールされている場合、システムは、インテント リゾルバと呼ばれる曖昧さ回避のダイアログを表示して、ユーザーが共有先を選択できるようにします。一致するアプリケーションが 1 つであれば、そのアプリケーションが実行されます。
Android インテント リゾルバを使用してテキストを送信する方法の例を次に示します。
fun intentResolver(context: Context) { val sendIntent: Intent = Intent().apply { action = ACTION_SEND putExtra(Intent.EXTRA_TEXT, "This is my text to send.") type = "text/plain" } context.startActivity(sendIntent) }
詳細
データの送信について詳しくは、インテントとインテント フィルタをご覧ください。