Wear OS 上で通知を作成する

このページでは、スマートウォッチ用の通知を作成する方法について説明します。また、接続されているスマートフォンからスマートウォッチにブリッジされる各通知にコンテンツを追加する方法についても説明します。

スマートウォッチの通知の構造はスマートフォンの通知と同じです。 また、Wear OS by Google では、ユーザー エクスペリエンスを最大限に高めるために、ウェアラブル固有の機能を通知に追加するための API を提供しています。

アプリから発行された各通知は、カードとして通知ストリームに表示されます。

図 1. スマートフォンとスマートウォッチに表示された同じ通知

通知はスマートウォッチとスマートフォンのどちらでも作成できます。ウェアラブル用の通知を作成するには、NotificationCompat.Builder クラスを使用します。このクラスを使用して通知を作成すると、システムによって通知を適切に表示するための処理が行われます。

注: RemoteViews を使用した通知では、カスタム レイアウトが取り除かれ、ウェアラブルにはテキストとアイコンだけが表示されます。ただし、スマートウォッチ上で動作するウェアラブル アプリを作成することにより、カスタムのカード レイアウトを使用するカスタム通知を作成できます。

通常、すべての通知はスマートフォンからスマートウォッチにブリッジされます。ただし、次の場合、通知はブリッジされません。

以下の関連リソースをご覧ください。

必要なクラスをインポートする

必要なパッケージをインポートするには、build.gradle ファイルに次の行を追加します。

    implementation 'androidx.core:core:1.2.0'
    

これによりプロジェクトが必要なパッケージにアクセスできるようになったので、サポート ライブラリから必要なクラスをインポートします。

Kotlin

    import androidx.core.app.NotificationCompat
    import androidx.core.app.NotificationManagerCompat
    import androidx.core.app.NotificationCompat.WearableExtender
    

Java

    import androidx.core.app.NotificationCompat;
    import androidx.core.app.NotificationManagerCompat;
    import androidx.core.app.NotificationCompat.WearableExtender;
    

通知ビルダーで通知を作成する

Jetpack コアライブラリを使用すると、Android 1.6(API レベル 4)以上との互換性を維持しつつ、操作ボタンやアイコンなどの最新の通知機能を備えた通知を作成できます。

注: Android 8.0(API レベル 26)以降では、表示する通知のタイプごとに通知チャンネルを作成する必要があります。

通知を作成するには:

  1. NotificationCompat.Builder のインスタンスを作成します。

    Kotlin

        val notificationId = 1
        // The channel ID of the notification.
        val id = "my_channel_01"
        // Build intent for notification content
        val viewPendingIntent = Intent(this, ViewEventActivity::class.java).let { viewIntent ->
            viewIntent.putExtra(EXTRA_EVENT_ID, eventId)
            PendingIntent.getActivity(this, 0, viewIntent, 0)
        }
    
        // Notification channel ID is ignored for Android 7.1.1
        // (API level 25) and lower.
        val notificationBuilder = NotificationCompat.Builder(this, id)
                .setSmallIcon(R.drawable.ic_event)
                .setContentTitle(eventTitle)
                .setContentText(eventLocation)
                .setContentIntent(viewPendingIntent)
        

    Java

        int notificationId = 001;
        // The channel ID of the notification.
        String id = "my_channel_01";
        // Build intent for notification content
        Intent viewIntent = new Intent(this, ViewEventActivity.class);
        viewIntent.putExtra(EXTRA_EVENT_ID, eventId);
        PendingIntent viewPendingIntent =
                PendingIntent.getActivity(this, 0, viewIntent, 0);
    
        // Notification channel ID is ignored for Android 7.1.1
        // (API level 25) and lower.
        NotificationCompat.Builder notificationBuilder =
            new NotificationCompat.Builder(this, id)
                .setSmallIcon(R.drawable.ic_event)
                .setContentTitle(eventTitle)
                .setContentText(eventLocation)
                .setContentIntent(viewPendingIntent);
        
  2. 通知 ID を指定して通知オブジェクトを notify() に渡すことにより、通知を発行します。

    Kotlin

        NotificationManagerCompat.from(this).apply {
            notify(notificationId, notificationBuilder.build())
        }
        

    Java

        // Get an instance of the NotificationManager service
        NotificationManagerCompat notificationManager =
                NotificationManagerCompat.from(this);
    
        // Issue the notification with notification manager.
        notificationManager.notify(notificationId, notificationBuilder.build());
        

この通知がスマートフォンに表示されたら、通知をタップして、setContentIntent() メソッドで指定された PendingIntent を呼び出すことができます。ウェアラブルでは、この通知は通知ストリームに表示されます。ブリッジ通知の場合、ユーザーが通知をタップすると、展開可能な通知が表示され、定義済みのアクション(開くアクションなど)がトリガーされます。通常、そのようなアクションでは、スマートフォン アプリのアクティビティを開始します。

展開可能な通知を作成する

展開可能な通知では、充実したコンテンツと操作を各通知に追加できます。ユーザーは、通知に対して指定された追加のコンテンツ ページやアクションを、展開可能な通知内で表示できます。展開可能な通知は Wear OS 用のマテリアル デザインに準拠しているため、ユーザーはアプリと同様に操作できます。

展開可能な通知の最初のアクションに RemoteInput(返信アクションなど)が含まれている場合、展開可能な通知の最初のアクションの下に、setChoices() で設定した選択肢が表示されます。

以下のいずれかの条件が成立する場合、ユーザーは通知をタップして展開可能な通知を表示できます。

  • ペア設定されたスマートフォン上のアプリによって通知が生成され、Wear にブリッジされている。
  • 通知に contentIntent が含まれていない。

注: setColor() メソッドで通知に設定されたアプリ固有の背景色は、通知を展開したときにのみ表示されます。

展開可能な通知に関するおすすめの方法

どのような場合に展開可能な通知を使用するかを決定するには、以下のガイドラインに従ってください。

  • ペア設定されたスマートフォンから Wear デバイスにブリッジするすべての通知について、展開可能な通知を使用します。
  • Wear 上でローカルに実行しているアプリによって通知が生成される場合は、setContentIntent() を呼び出すことにより、アプリ内の通知 ID を使用して通知のタップ ターゲットから通知オブジェクトを起動します。Wear 上でローカルに実行しているアプリによって生成される通知については、展開可能な通知を使用しないことをおすすめします。

展開可能な通知を追加する

展開可能な通知では、通知に追加のコンテンツとアクションを含めることができます。アプリの通知で提供する情報の詳細レベルは選択できますが、通知に含める詳細情報の量については適切な判断が必要です。

コンテンツを追加する

展開可能な通知に追加テキストを表示するには、BigTextStyle を使用します。

展開可能な通知に画像を追加するには、BigPictureStyle を使用します。展開可能な通知に複数の画像を追加する場合は、BigPictureStyle を指定して addPage() メソッドを使用します。

メイン アクション

展開可能な通知にはメイン アクションが 1 つ含まれています。setContentAction() によって別のアクションが指定されていなければ、メイン アクションが通知内の最初のアクションになります。

追加のアクション

追加のアクションを指定するには、addAction() または addActions() を使用します。すべての利用可能なアクションは、展開可能な通知のアクション ドロワーに含まれています。

通知アクションを追加する

setContentIntent() で定義されたメイン コンテンツ アクションに加えて他のアクションを追加するには、PendingIntentaddAction() メソッドに渡します。

たとえば、以下のコードは上記と同じタイプの通知を表示しますが、地図上にイベント会場を表示するアクションを追加しています。

Kotlin

    // Build an intent for an action to view a map
    val mapIntent = Intent(Intent.ACTION_VIEW)
    // The channel ID of the notification.
    val id = "my_channel_01"
    val mapPendingIntent = Intent(Intent.ACTION_VIEW).let { mapIntent ->
        mapIntent.data = Uri.parse("geo:0,0?q=" + Uri.encode(location))
        PendingIntent.getActivity(this, 0, mapIntent, 0)
    }

    val notificationBuilder = NotificationCompat.Builder(this, id)
            .setSmallIcon(R.drawable.ic_event)
            .setContentTitle(eventTitle)
            .setContentText(eventLocation)
            .setContentIntent(viewPendingIntent)
            .addAction(R.drawable.ic_map, getString(R.string.map), mapPendingIntent)
    

Java

    // Build an intent for an action to view a map
    Intent mapIntent = new Intent(Intent.ACTION_VIEW);
    // The channel ID of the notification.
    String id = "my_channel_01";
    Uri geoUri = Uri.parse("geo:0,0?q=" + Uri.encode(location));
    mapIntent.setData(geoUri);
    PendingIntent mapPendingIntent =
            PendingIntent.getActivity(this, 0, mapIntent, 0);

    NotificationCompat.Builder notificationBuilder =
            new NotificationCompat.Builder(this, id)
            .setSmallIcon(R.drawable.ic_event)
            .setContentTitle(eventTitle)
            .setContentText(eventLocation)
            .setContentIntent(viewPendingIntent)
            .addAction(R.drawable.ic_map,
                    getString(R.string.map), mapPendingIntent);
    

スマートフォンでは、通知にアタッチされた追加のボタンとしてアクションが表示されます。ウェアラブルでは、コンテンツのテキストの後の展開可能な通知内にアクションが表示されます。ユーザーがアクションをタップすると、関連するインテントがスマートフォン上で呼び出されます。

ヒント: 通知に「返信」アクションが含まれている場合(メッセージ アプリなど)、ウェアラブルから音声入力で直接返信できるようにすると、動作を拡張できます。詳細については、音声入力を通知アクションとして追加するをご覧ください。

インライン アクションを追加する

インライン アクションでは、通知ストリーム カード内から通知に対するアクションを実行できます。Wear では、インライン アクションは通知の一番下に追加されるボタンとして表示されます。

インライン アクションの使用は任意ですが、通知ストリーム カードにコンテンツが表示された後、ユーザーが(展開可能な通知を展開せずに)通知に対する操作を行う可能性が高い場合は、使用することをおすすめします。 通知のインライン アクションに適したユースケースの例として、テキスト メッセージへの返信、フィットネス アクティビティの停止、メール メッセージのアーカイブなどがあります。

表示できるインライン アクションは、1 つの通知につき 1 つだけです。インライン アクションを通知内の追加ボタンとして表示するには、setHintDisplayActionInline() メソッドを true に設定します。ユーザーがインライン アクションをタップすると、通知アクションで指定したインテントがシステムによって呼び出されます。

以下のコード スニペットは、アクションをインライン表示するためのヒントを追加し、addAction メソッドを使用してインライン アクションを通知に追加する方法を示しています。

Kotlin

    //Wear OS requires a hint to display the reply action inline.
    val actionExtender = NotificationCompat.Action.WearableExtender()
            .setHintLaunchesActivity(true)
            .setHintDisplayActionInline(true)
    wearableExtender.addAction(actionBuilder.extend(actionExtender).build())
    

Java

    //Wear OS requires a hint to display the reply action inline.
    Action.WearableExtender actionExtender =
        new Action.WearableExtender()
            .setHintLaunchesActivity(true)
            .setHintDisplayActionInline(true);
    wearableExtender.addAction(actionBuilder.extend(actionExtender).build());
    

ウェアラブル固有の機能を通知に追加する

ウェアラブル固有の機能(ウェアラブル通知でアプリアイコンを非表示にする、ユーザーが音声入力でテキスト応答を指示できるようにするなど)を通知に追加する必要がある場合は、NotificationCompat.WearableExtender クラスを使用して目的のオプションを指定できます。この API を使用する手順は次のとおりです。

  1. WearableExtender のインスタンスを作成し、ウェアラブル固有のオプションを通知に設定します。
  2. NotificationCompat.Builder のインスタンスを作成し、前述の説明に従って目的のプロパティを通知に設定します。
  3. 通知で extend() を呼び出し、WearableExtender を渡します。これにより、ウェアラブル用のオプションが通知に適用されます。
  4. build() を呼び出して通知を作成します。

注: フレームワークの NotificationManager を使用する場合は、NotificationCompat.WearableExtender の一部の機能が動作しないため、必ず NotificationCompat を使用してください。

通知の消去(キャンセル)をユーザーの複数のデバイス間で同期できます。消去の同期を有効にするには、setDismissalId() メソッドを使用します。setDismissalId() メソッドを呼び出すとき、各通知に対して、グローバルに一意の ID を文字列として渡します。通知が消去されると、同じ消去 ID を持つ他のすべての通知が(1 つまたは複数の)スマートウォッチとコンパニオン スマートフォンで消去されます。消去 ID を取得するには、getDismissalId() を使用します。

注: setBackground() メソッドは Wear 2.0 ではサポートされていません。画像を含む通知には、NotificationCompat.BigPictureStyle を使用できます。

setHintHideIcon() メソッドも Wear 2.0 ではサポートされていません。

ウェアラブル専用のアクションを指定する

ウェアラブルで使用できるアクションとスマートフォンで使用できるアクションを別にしたい場合は、WearableExtender.addAction() を使用します。このメソッドでアクションを追加すると、NotificationCompat.Builder.addAction() で追加した他のアクションはウェアラブルに表示されなくなります。WearableExtender.addAction() で追加したアクションはウェアラブルにのみ表示され、スマートフォンには表示されなくなります。

音声入力を通知アクションとして追加する

音声操作は、ウェアラブルの操作における重要な要素です。音声入力をサポートするアクションを作成するには、通知アクションに追加できる RemoteInput.Builder のインスタンスを作成します。このクラスのコンストラクタは、システムが音声入力のキーとして使用する文字列を受け入れます。この文字列は、後でスマートフォン アプリ内で入力のテキストを取得する際に使用します。

次の例は、音声入力プロンプト用のカスタムラベルを提供する RemoteInput オブジェクトを作成する方法を示しています。

Kotlin

    // Key for the string that's delivered in the action's intent
    const val EXTRA_VOICE_REPLY = "extra_voice_reply"
    ...
    val remoteInput = resources.getString(R.string.reply_label).let { replyLabel ->
        RemoteInput.Builder(EXTRA_VOICE_REPLY)
                .setLabel(replyLabel)
                .build()
    }
    

Java

    // Key for the string that's delivered in the action's intent
    private static final String EXTRA_VOICE_REPLY = "extra_voice_reply";

    String replyLabel = getResources().getString(R.string.reply_label);

    RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
            .setLabel(replyLabel)
            .build();
    

事前定義済みのテキスト応答を追加する

音声入力の有効化に加えて、最大 5 つのテキスト応答の提供が可能です。ユーザーはそれらを選択することで瞬時に返信できます。テキスト応答を表示するには、setChoices() を呼び出して文字列配列を渡します。

たとえば、リソース配列で応答を定義できます。

res/values/strings.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string-array name="reply_choices">
            <item>Yes</item>
            <item>No</item>
            <item>Maybe</item>
        </string-array>
    </resources>
    

次に、文字列配列をインフレートして RemoteInput に追加します。

Kotlin

    // Key for the string that's delivered in the action's intent
    const val EXTRA_VOICE_REPLY = "extra_voice_reply"
    ...
    val remoteInput = resources.getString(R.string.reply_label).let { replyLabel ->
        resources.getStringArray(R.array.reply_choices).let { replyChoices ->
            RemoteInput.Builder(EXTRA_VOICE_REPLY)
                    .setLabel(replyLabel)
                    .setChoices(replyChoices)
                    .build()
        }
    }
    

Java

    public static final String EXTRA_VOICE_REPLY = "extra_voice_reply";
    ...
    String replyLabel = getResources().getString(R.string.reply_label);
    String[] replyChoices = getResources().getStringArray(R.array.reply_choices);

    RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
            .setLabel(replyLabel)
            .setChoices(replyChoices)
            .build();
    

音声入力を文字列として受け取る

返信アクションのインテント内で宣言されたアクティビティで、ユーザーの音声を文字に変換したメッセージを受け取るには、getResultsFromIntent() を呼び出して「返信」アクションのインテントを渡します。このメソッドは、テキスト応答を含む Bundle を返します。これにより、Bundle をクエリして応答を取得できます。

注: 音声入力は ClipData として格納されるため、音声結果を取得する際は Intent.getExtras() を使用しないでください。getResultsFromIntent() メソッドを使用すると、ClipData を自分で処理する手間を省いて、文字シーケンスを簡単に受け取ることができます。

次のコードは、インテントを受け入れて、上記の例で使用した EXTRA_VOICE_REPLY キーによって参照される音声応答を返す方法を示しています。

Kotlin

    /**
     * Obtain the intent that started this activity by calling
     * Activity.getIntent() and pass it into this method to
     * get the associated voice input string.
     */

    private fun getMessageText(intent: Intent): CharSequence? =
            RemoteInput.getResultsFromIntent(intent)?.run {
                getCharSequence(EXTRA_VOICE_REPLY)
            }
    

Java

    /**
     * Obtain the intent that started this activity by calling
     * Activity.getIntent() and pass it into this method to
     * get the associated voice input string.
     */

    private CharSequence getMessageText(Intent intent) {
        Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
        if (remoteInput != null) {
            return remoteInput.getCharSequence(EXTRA_VOICE_REPLY);
        }
        return null;
    }
    

Kotlin

    // Create an intent for the reply action
    val actionPendingIntent = Intent(this, ActionActivity::class.java).let { actionIntent ->
        PendingIntent.getActivity(this, 0, actionIntent,
                PendingIntent.FLAG_UPDATE_CURRENT)
    }
    // Create the action
    val action = NotificationCompat.Action.Builder(
            R.drawable.ic_action,
            getString(R.string.label),
            actionPendingIntent
    ).build()

    // Build the notification and add the action via WearableExtender
    var notification = NotificationCompat.Builder(context)
            .setSmallIcon(R.drawable.ic_message)
            .setContentTitle(getString(R.string.title))
            .setContentText(getString(R.string.content))
            .extend(NotificationCompat.WearableExtender().addAction(action))
            .build()
    

Java

    // Create an intent for the reply action
    Intent actionIntent = new Intent(this, ActionActivity.class);
    PendingIntent actionPendingIntent =
            PendingIntent.getActivity(this, 0, actionIntent,
                    PendingIntent.FLAG_UPDATE_CURRENT);

    // Create the action
    NotificationCompat.Action action =
            new NotificationCompat.Action.Builder(R.drawable.ic_action,
                    getString(R.string.label), actionPendingIntent)
                    .build();

    // Build the notification and add the action via WearableExtender
    Notification notification =
            new NotificationCompat.Builder(context)
                    .setSmallIcon(R.drawable.ic_message)
                    .setContentTitle(getString(R.string.title))
                    .setContentText(getString(R.string.content))
                    .extend(new WearableExtender().addAction(action))
                    .build();
    

スタンドアロンのウェアラブル アプリから通知を起動する

スタンドアロンのスマートウォッチ アプリでの通知の作成とブリッジ通知の作成に違いはありません。スタンドアロンの Wear アプリで作成された通知はブリッジ通知に似ていますが、動作が少し異なります。contentIntent が設定されていない場合、またはペア設定されたスマートフォンから通知がブリッジされた場合、通知をタップすると展開可能な通知が開きます。一方、スタンドアロンのスマートウォッチ アプリで通知が作成された場合は、通知をタップすると contentIntent が起動して Wear アプリが開きます。スタンドアロン アプリから通知を作成して展開可能な通知の動作を模倣する方法については、Wear 通知のサンプルをご覧ください。

デフォルトでは、通知はコンパニオン スマートフォンのアプリから、ペア設定されたスマートウォッチにブリッジされます。スタンドアロンのスマートウォッチ アプリを作成する場合、コンパニオン スマートフォン アプリが存在すると、これらのアプリによって重複する通知が作成される可能性があります。通知の重複に関する問題の対処方法については、通知のブリッジモードをご覧ください。