他のアプリからシンプルなデータを受信する

1 つのアプリから別のアプリへデータを送信できるのと同様に、他のアプリからデータを受信することもできます。ユーザーがアプリをどのように操作するのか、他のアプリからどのデータタイプを受け取るのかを検討する必要があります。たとえば、ソーシャル ネットワーキング アプリの場合、興味深いウェブ URL など、テキスト コンテンツを他のアプリから受信できると便利です。

Android Sharesheet やインテント リゾルバを通じてユーザーがアプリにデータを送信することがよくあります。受信データはすべて、提供元アプリによって MIME タイプが設定されます。他のアプリから送信されたデータを受信するには、次の 3 つの方法があります。

  • マニフェスト内の intent-filter タグに合致する Activity
  • ChooserTargetService によって返される 1 つまたは複数の ChooserTarget オブジェクト
  • アプリが公開している共有ショートカット。この方法は、ChooserTarget オブジェクトよりも優先されます。共有ショートカットを利用できるのは、アプリが Android 10(API レベル 29)上で稼働している場合に限られます。

共有ショートカットと ChooserTarget オブジェクトは、アプリ内の特定の Activity へのダイレクト シェア ディープリンクです。多くの場合、このようなディープリンクはユーザーを指し示し、Android Sharesheet に表示されます。たとえば、メッセージ アプリの場合、各ユーザーを対象とする共有ショートカットを提供すると便利です。共有ショートカットをタップすると、ディープリンクの機能により、直接そのユーザーとのスレッドを開始することができます。

MIME タイプをサポートする

アプリは、可能な限り幅広い MIME タイプを受信できる必要があります。たとえば、テキスト、画像、動画の送信に使用するメッセージ アプリの場合、text/*image/*video/* の受信をサポートする必要があります。Android 内でシンプルなデータを送信する際の一般的な MIME タイプを以下に示します。

  • text/* - 多くの場合、text/plaintext/rtftext/htmltext/json が送信されます。
  • image/* - 多くの場合、image/jpgimage/pngimage/gif が送信されます。
  • video/* - 多くの場合、video/mp4video/3gp が送信されます。
  • 受信側アプリはサポート対象のファイル拡張子を登録しておく必要があります。送信側アプリから application/pdf が送信されることもよくあります。

MIME メディアタイプの詳細については、IANA 公式レジストリをご覧ください。*/* の MIME タイプは受信可能ですが、あらゆるタイプの受信コンテンツを完全に処理できる場合を除き、推奨されません。

適切な共有ターゲットを作成する

特定のアクティビティに関連付けられた共有ターゲットをユーザーがタップしたときは、共有コンテンツの使用を開始する前に、その確認や編集が可能になっている必要があります。この機能は、特にテキストデータの場合に重要になります。

ダイレクト シェア ターゲットをユーザーがタップしたときは、そのターゲットのサブジェクトに対してアクションを直接実行できるインターフェースにユーザーを誘導する必要があります。確認ダイアログを表示したり、タップされたターゲットとは関係のないインターフェースにユーザーを誘導したりしないでください。特に、共有する連絡先の確認や再選択を求める連絡先確認インターフェースにユーザーを誘導しないでください。このようなユーザー確認は、Android Sharesheet 内でターゲットをタップした段階ですでに完了しています。たとえば、メッセージ アプリの場合、ダイレクト シェア ターゲットをタップすると、選択したユーザーとのスレッドをユーザーに表示する必要があります。また、キーボードも表示され、メッセージ部分には共有データが事前に入力されている必要があります。

アクティビティを使用してデータを受信する

マニフェストを更新する

インテント フィルタは、アプリ コンポーネントが受け入れるインテントをシステムに通知します。レッスン「他のアプリにシンプルなデータを送信する」で ACTION_SEND アクションを使用してインテントを作成した方法と同じように、このアクションを使用してインテントを受信できるようにインテント フィルタを作成します。<intent-filter> 要素を使用して、マニフェスト内でインテント フィルタを定義します。たとえば、テキスト コンテンツ、任意のタイプの単一の画像、任意のタイプの複数の画像という 3 種のデータタイプを受信するアプリの場合、マニフェストは次のようになります。

    <activity android:name=".ui.MyActivity" >
        <intent-filter>
            <action android:name="android.intent.action.SEND" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="image/*" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.SEND" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.SEND_MULTIPLE" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="image/*" />
        </intent-filter>
    </activity>
    

別のアプリがインテントを作成して startActivity() に渡すことで、上記のデータタイプのいずれかを共有しようとすると、受信側アプリのリストが Android Sharesheet またはインテント リゾルバ内に表示されます。その中からユーザーが 1 つの受信側アプリを選択すると、対応するアクティビティ(上記の例の場合は .ui.MyActivity)が起動します。その後、受信側アプリが、コードや UI 内でコンテンツを適切に処理します。

注: インテント フィルタとインテント解決の詳細については、インテントとインテント フィルタをご覧ください。

受信コンテンツを処理する

Intent によって配信されたコンテンツを処理するには、getIntent() を呼び出して Intent オブジェクトを取得します。オブジェクトを取得したら、そのコンテンツを調査して、次に何をすべきかを判断します。システム内の別の場所(ランチャーなど)からこのアクティビティを起動できる場合は、インテントを調査する際にこの点を考慮する必要があります。

受信データをチェックする場合は特に注意してください。他のアプリから何が送信されたのかを事前に知ることは決してできません。たとえば、MIME タイプが誤って設定されている場合や、送信された画像が極端に大きい場合などがあります。必ず、メイン(「UI」)スレッドではなく、別個のスレッドでバイナリデータを処理するようにしてください。

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        when {
            intent?.action == Intent.ACTION_SEND -> {
                if ("text/plain" == intent.type) {
                    handleSendText(intent) // Handle text being sent
                } else if (intent.type?.startsWith("image/") == true) {
                    handleSendImage(intent) // Handle single image being sent
                }
            }
            intent?.action == Intent.ACTION_SEND_MULTIPLE
                    && intent.type?.startsWith("image/") == true -> {
                    handleSendMultipleImages(intent) // Handle multiple images being sent
            }
            else -> {
                // Handle other intents, such as being started from the home screen
            }
        }
        ...
    }

    private fun handleSendText(intent: Intent) {
        intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
            // Update UI to reflect text being shared
        }
    }

    private fun handleSendImage(intent: Intent) {
        (intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri)?.let {
            // Update UI to reflect image being shared
        }
    }

    private fun handleSendMultipleImages(intent: Intent) {
        intent.getParcelableArrayListExtra<Parcelable>(Intent.EXTRA_STREAM)?.let {
            // Update UI to reflect multiple images being shared
        }
    }
    

Java

    void onCreate (Bundle savedInstanceState) {
        ...
        // Get intent, action and MIME type
        Intent intent = getIntent();
        String action = intent.getAction();
        String type = intent.getType();

        if (Intent.ACTION_SEND.equals(action) && type != null) {
            if ("text/plain".equals(type)) {
                handleSendText(intent); // Handle text being sent
            } else if (type.startsWith("image/")) {
                handleSendImage(intent); // Handle single image being sent
            }
        } else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
            if (type.startsWith("image/")) {
                handleSendMultipleImages(intent); // Handle multiple images being sent
            }
        } else {
            // Handle other intents, such as being started from the home screen
        }
        ...
    }

    void handleSendText(Intent intent) {
        String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
        if (sharedText != null) {
            // Update UI to reflect text being shared
        }
    }

    void handleSendImage(Intent intent) {
        Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
        if (imageUri != null) {
            // Update UI to reflect image being shared
        }
    }

    void handleSendMultipleImages(Intent intent) {
        ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
        if (imageUris != null) {
            // Update UI to reflect multiple images being shared
        }
    }
    

データを受信した後の UI の更新としては、EditText を指定するだけのシンプルなケースもあれば、魅力的な写真フィルタを画像に適用するような高度なケースもあります。その後の処理方法は、アプリによって異なります。

アプリがユーザーに認識されるようにする

アプリは、Android Sharesheet やインテント リゾルバ内でアイコンラベルで示されます。これらはいずれもマニフェスト内で定義します。アクティビティ ラベルやインテント フィルタ ラベルを設定すると、詳細なコンテキストを指定できます。

Android 10(API レベル 29)以降、Android Sharesheet が使用するアイコンは、マニフェスト内で application タグに対して設定されたアイコンに限られるようになりました。intent-filter タグや activity タグに対して設定されたアイコンは無視されます。

注: 適切な共有ターゲットであれば、関連するアクティビティやインテント フィルタにラベルやアイコンは必要ありません。受信側アプリの名前とアイコンだけで、共有時に何が起こるのかユーザーが把握できるはずです。

ダイレクト シェア ターゲットを提供する

ダイレクト シェアは、Android 6.0(API レベル 23)で導入された機能で、アプリは ChooserTargetService を通じて ChooserTarget オブジェクトを提供することができます。結果は事後的にオンデマンドで取得されるため、ターゲットの読み込み時間が長くなっていました。

Android 10(API レベル 29)において、ChooserTargetService DirectShare API は、新しい Sharing Shortcuts API に置き換えられました。Sharing Shortcuts API を使用すると、事後的にオンデマンドで結果を取得するのではなく、事前にダイレクト シェア ターゲットを公開できます。ChooserTargetService ダイレクト シェア メカニズムは引き続き機能しますが、この方法で提供されたターゲットは、Sharing Shortcuts API を使用しているどのターゲットよりも下位にランク付けされます。

ShortcutManagerCompat は、旧式の ChooserTargetService DirectShare API との後方互換性を備えた共有ショートカットを提供する AndroidX API です。共有ショートカットと ChooserTargets の両方を公開する方法としておすすめします。詳細については、下記の手順をご覧ください。

ダイレクト シェア ターゲットを公開する

ダイレクト シェア ターゲットを公開する際に使用できるのは、動的ショートカットに限られます。新しい API を使用してダイレクト シェア ターゲットを公開する手順は次のとおりです。

  1. アプリの XML リソース ファイル内で、<share-target> 要素を宣言します。詳細については、下記の共有ターゲットを宣言するをご覧ください。
  2. マッチング用カテゴリを含め、宣言した共有ターゲットの動的ショートカットを公開します。AndroidX の ShortcutManager または ShortcutManagerCompat を使用することで、ショートカットの追加、アクセス、更新、削除を行うことができます。古い Android バージョンとの後方互換性を維持するため、AndroidX の互換性ライブラリを使用することをおすすめします。

Sharing Shortcuts API

共有ターゲットに関して追加情報を提供する新しいメソッドや強化されたメソッドが ShortcutInfo.Builder に追加されています。

setCategories()
これは新しいメソッドではありませんが、共有インテントや共有アクションを処理可能なショートカットをフィルタリングする際にも、カテゴリが使用されるようになりました。詳細については、下記の共有ターゲットを宣言するをご覧ください。共有ターゲットとして使用することを目的としたショートカットの場合、このフィールドは必須です。
setLongLived()

ショートカットが非公開になった場合や、アプリによって動的ショートカットや固定ショートカットとして表示されなくなった場合に、ショートカットを有効のままにするかどうかを指定します。ショートカットの有効性が継続する場合、動的ショートカットとしては非公開になった後も、さまざまなシステム サービスによってキャッシュされる可能性があります。

ショートカットを長期にわたって維持すると、ランキングが向上します。詳細については、最高のランキングを取得するをご覧ください。

setPerson()setPersons()

1 つまたは複数の Person オブジェクトをショートカットに関連付けます。これにより、さまざまなアプリを横断してユーザー行動を把握できます。また、フレームワーク内の予測サービスが ShareSheet 内で優れた候補を提示できるようになります。ショートカットに Person 情報を追加するのは必須ではありませんが、共有ターゲットをユーザーに関連付けることができる場合は強くおすすめします。クラウドなど、一部の共有ターゲットは、ユーザーに関連付けることができません。

一意のキーを持つ特定の Person オブジェクトを共有ターゲットや関連通知内に含めると、ランキングが向上します。詳細については、最高のランキングを取得するをご覧ください。

一般的なメッセージ アプリの場合、連絡先ごとに個別のショートカットを公開し、Person フィールドに連絡先の情報を格納する必要があります。グループ チャットのように 1 つのターゲットを複数のユーザーに関連付けることができる場合は、単一の共有ターゲットに複数の Person オブジェクトを追加します。

個人を対象とするショートカットを公開する場合は、setLongLabel() にフルネームを格納し、setShortLabel() にニックネームやファースト ネームなどの短縮名を格納します。

共有ショートカットを公開する方法の例については、共有ショートカット サンプルコードをご覧ください。

最高のランキングを取得する

Android Sharesheet には、一定数のダイレクト シェア ターゲットが表示されます。表示される提案は、ランキング順に並べられます。以下の措置を行うと、ショートカットのランキングを改善できる可能性があります。

  • すべての shortcutId を一意にして、別のターゲットで再利用しないようにします。
  • setLongLived(true) を呼び出して、ショートカットの永続性を高めます。
  • トレーニング データがない場合に、アプリのショートカットを比較する際に使用できるランキングを提供します。詳細については、setRank() をご覧ください。ランキングが低いと、ショートカットが重要であることを意味します。

ランキングをさらに高めるには、上記のすべての措置に加えて、以下を行うソーシャル アプリを使用することを強くおすすめします。

  • 該当する Person オブジェクトに対して、ショートカットのセットキーを指定します。詳細については、setPerson()setPersons()setKey() をご覧ください。
  • ショートカットを、同じユーザーからの関連通知にリンクします。詳細については、setShortcutId(). をご覧ください。shortcutId は、以前に setLongLived(true) を使用して公開したショートカット用の可能性があります。

ランキングを最大限に高めるために、ソーシャル アプリは上記のすべての措置に加えて、以下を実行できます。

  • 提供された Person オブジェクトで、デバイス上の関連連絡先に対して、有効な URI を指定します。詳細については、setUri() をご覧ください。

ランキングの改善措置を施したショートカットの例を以下に示します。

Kotlin

    val person = Person.Builder()
      ...
      .setName(fullName)
      .setKey(staticPersonIdentifier)
      .setUri("tel:$phoneNumber") // alternatively "mailto:$email" or CONTENT_LOOKUP_URI
      .build()

    val shortcutInfo = ShortcutInfoCompat.Builder(myContext, staticPersonIdentifier)
      ...
      .setShortLabel(firstName)
      .setLongLabel(fullName)
      .setPerson(person)
      .setLongLived(true)
      .setRank(personRank)
      .build()
    

Java

    Person person = Person.Builder()
      ...
      .setName(fullName)
      .setKey(staticPersonIdentifier)
      .setUri("tel:"+phoneNumber) // alternatively "mailto:"+email or CONTENT_LOOKUP_URI
      .build();

    ShortcutInfoCompat shortcutInfo = ShortcutInfoCompat.Builder(myContext, staticPersonIdentifier)
      ...
      .setShortLabel(firstName)
      .setLongLabel(fullName)
      .setPerson(person)
      .setLongLived(true)
      .setRank(personRank)
      .build();
    

Kotlin

    val notif = NotificationCompat.Builder(myContext, channelId)
      ...
      .setShortcutId(staticPersonIdentifier)
      .build()
    

Java

    Notification notif = NotificationCompat.Builder(myContext, channelId)
      ...
      .setShortcutId(staticPersonIdentifier)
      .build();

    

ショートカット画像を提供する

共有ショートカットを作成するには、setIcon() を通じて画像を追加する必要があります。

共有ショートカットは、システム サーフェス全体で表示される可能性があり、形状が変更されることもあります。意図どおりにショートカットを表示するには、IconCompat.createWithAdaptiveBitmap() を通じてアダプティブ ビットマップを提供します。

アダプティブ ビットマップは、アダプティブ アイコン向けに規定されているガイドラインおよびサイズと同じルールに従う必要があります。遵守するために最も一般的に行われているのは、目的の正方形ビットマップを 72×72 dp にスケーリングして、108×108 dp の透明なキャンバス内の中央に配置する方法です。

特定の形状にマスキングした画像を提供しないようにしてください。たとえば、Android 10(API レベル 29)より前の時代では、円形にマスキングしたダイレクト シェア ChooserTarget のユーザー アバターを提供するのが一般的でした。現在の Android 10 の Android Sharesheet や各種システム サーフェスでは、デザインに応じてショートカット画像の形状が自動的に変更されて配列されます。そのため、共有ショートカットを提供する場合は、ShortcutManagerCompat を通じて、後方互換ダイレクト シェア ChooserTarget の形状を自動的に円形に変更する方法をおすすめします。

共有ターゲットを宣言する

共有ターゲットは、静的ショートカットの定義と同様に、アプリのリソース ファイル内で宣言する必要があります。リソース ファイルの <shortcuts> ルート要素内に、他の静的ショートカット定義と一緒に、共有ターゲット定義を追加してください。各 <share-targets> 要素には、共有データタイプや、マッチング用カテゴリ、共有インテントを処理するターゲット クラスに関する情報が格納されます。XML コードは次のようになります。

    <shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
      <share-target android:targetClass="com.example.android.sharingshortcuts.SendMessageActivity">
        <data android:mimeType="text/plain" />
        <category android:name="com.example.android.sharingshortcuts.category.TEXT_SHARE_TARGET" />
      </share-target>
    </shortcuts>
    

共有ターゲットのデータ要素は、インテント フィルタのデータ指定と似ています。各共有ターゲットは複数のカテゴリを持つことができます。カテゴリは、アプリの公開済みショートカットと共有ターゲット定義をマッチングさせるためにのみ使用されます。カテゴリは、任意のアプリ定義値を持つことができます。

Android Sharesheet 内で上記の <share-target> 例と合致する共有ショートカットをユーザーが選択した場合、アプリは次の共有インテントを取得します。

    Action: Intent.ACTION_SEND
    ComponentName: {com.example.android.sharingshortcuts /
                    com.example.android.sharingshortcuts.SendMessageActivity}
    Data: Uri to the shared content
    EXTRA_SHORTCUT_ID: <ID of the selected shortcut>
    

ユーザーがランチャーのショートカットから共有ターゲットを開くと、アプリは、共有ショートカットを ShortcutManagerCompat に追加したときに作成されたインテントを取得します。 別のインテントであるため Intent.EXTRA_SHORTCUT_ID は利用できず、必要な場合は手動で ID を渡す必要があります。

AndroidX を使用して、共有ショートカットと ChooserTarget の両方を提供する

AndroidX 互換性ライブラリを使用できるようにするには、アプリのマニフェスト内に meta-data の chooser-target-service と intent-filter のセットが含まれている必要があります。現在の ChooserTargetService DirectShare API についてご確認ください。

このサービスは、すでに互換性ライブラリ内で宣言されているため、アプリのマニフェスト内でユーザーがサービスを宣言する必要はありません。ただし、共有アクティビティからサービスへのリンクを、チューザ ターゲット プロバイダに含める必要があります。

次の例の場合、ChooserTargetService の実装は androidx.core.content.pm.ChooserTargetServiceCompat です。これは AndroidX 内ですでに定義されています。

    <activity
        android:name=".SendMessageActivity"
        android:label="@string/app_name"
        android:theme="@style/SharingShortcutsDialogTheme">
        <!-- This activity can respond to Intents of type SEND -->
        <intent-filter>
            <action android:name="android.intent.action.SEND" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain" />
        </intent-filter>
        <!-- Only needed if you import the sharetarget AndroidX library that
             provides backwards compatibility with the old DirectShare API.
             The activity that receives the Sharing Shortcut intent needs to be
             taken into account with this chooser target provider. -->
        <meta-data
            android:name="android.service.chooser.chooser_target_service"
            android:value="androidx.sharetarget.ChooserTargetServiceCompat" />
    </activity>
    

共有ショートカットに関するよくある質問

現在の Sharing Shortcuts API と旧式の ChooserTargetService DirectShare API の主な違いは何ですか?

旧式の DirectShare API ではプルモデルが使用されていたのに対して、Sharing Shortcuts API ではプッシュモデルが使用されています。これにより、ShareSheet の準備時にダイレクト シェア ターゲットを取得するプロセスが大幅に高速化されます。アプリ デベロッパーの観点からすると、新しい API を使用する場合、アプリは、ダイレクト シェア ターゲットのリストを事前に提供し、アプリの内部状態が変化するたびにショートカットのリストを更新する必要があります(たとえば、新しい連絡先がメッセージ アプリに追加された場合など)。

Sharing Shortcuts API に移行しないとどうなりますか?

Android 10(API レベル 29)以降の Android Sharesheet では、Sharing Shortcuts API を使用して ShortcutManager 経由で提供された共有ターゲットの方が優先されるようになります。そのため、公開した共有ターゲットが、他のアプリの共有ターゲットに埋もれてしまい、共有時に表示されなくなるおそれがあります。

後方互換性を維持するため、アプリ内で ChooserTargetService DirectShare API と Sharing Shortcuts API の両方を使用することはできますか?

絶対にしないでください。代わりに、用意されているサポート ライブラリ API(ShortcutManagerCompat)を使用してください。2 つの API セットを混在させると、共有ターゲットを取得する際に、望ましくない動作や予期しない動作が発生するおそれがあります。

共有ターゲット用の公開ショートカットは、ランチャー ショートカット(ランチャー内でアプリアイコンを長押ししたときのショートカットの一般的な使用方法)とどのように異なるのですか?

「共有ターゲット」の目的で公開されているショートカットは、すべてランチャーのショートカットでもあり、アプリアイコンを長押しするとメニュー内に表示されます。アクティビティごとの最大ショートカット数の制限は、アプリが公開しているショートカットの総数(共有ターゲットと旧式のランチャー ショートカットの合計)にも適用されます。