Wear OS の通知を作成する

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

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

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

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

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

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

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

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

    compile "com.android.support:support-v13:27.0.2"
    

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

Kotlin

    import android.support.v4.app.NotificationCompat
    import android.support.v4.app.NotificationManagerCompat
    import android.support.v4.app.NotificationCompat.WearableExtender
    

Java

    import android.support.v4.app.NotificationCompat;
    import android.support.v4.app.NotificationManagerCompat;
    import android.support.v4.app.NotificationCompat.WearableExtender;
    

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

v4 サポート ライブラリを使用すると、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() を使用します。使用可能な操作は、展開可能な通知の操作ドロワーにすべて格納されています。

通知の操作を追加する

PendingIntentaddAction() メソッドに渡すことで、setContentIntent() で定義したメインのコンテンツ操作以外の操作を追加できます。

たとえば次のコードは、上と同じタイプの通知を表示しますが、地図上にイベント会場を表示する操作を追加します。

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 が指定されている他のすべての通知もスマートウォッチとコンパニオン スマートフォンから消去されます。消去操作 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 の通知のサンプルをご覧ください。

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