- Adding Wearable Features to Notifications
- Creating a Notification
- Receiving Voice Input in a Notification
- Adding Pages to a Notification
- Stacking Notifications
概要
- スマホと Wear が接続されていると、Notification が Wear にも表示(同期)される。
- Wear では通知はカードとして表示され、このカードが表示されるところを context stream という。
- これまでの通知でももちろん Wear に表示されるが、Wear 用に Notification を拡張することができる。
Notification を作る
Notification の作成には NotificationCompat.Builder を使う。これで作っておけば、システムがかってにスマホと Wear で通知の見た目を変えてくれる。
通知の発行には NotificationManagerCompat を使う(NotificationManager ではなく)。
■ SmallIcon だけの Notification
- int notificationId = 001;
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notif);
- // Notification を発行
- NotificationManagerCompat notificationManager =
- NotificationManagerCompat.from(this);
- notificationManager.notify(notificationId, notificationBuilder.build());

Wear(左: ホーム画面、右: タップした状態)


Wearのカードの右上に表示されるアイコンは setSmallIcon() で指定したものではなく、アプリアイコンになる。 タップしたときの背景色はどこから来てるのかよくわからない。アプリアイコンのカラーパレット?
■ タイトルとメッセージありの Notification
- int notificationId = 001;
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- // Notification を発行
- NotificationManagerCompat notificationManager =
- NotificationManagerCompat.from(this);
- notificationManager.notify(notificationId, notificationBuilder.build());

Wear(左: ホーム画面、右: タップした状態)


■ Content Intent ありの Notification
- int notificationId = 001;
- // Content Intent 用の PendingIntent を作成
- Intent intent = new Intent(this, MainActivity.class);
- PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent);
- // Notification を発行
- NotificationManagerCompat notificationManager =
- NotificationManagerCompat.from(this);
- notificationManager.notify(notificationId, notificationBuilder.build());

Wear(左: ホーム画面、中央: タップした状態、右: タップしたあと左にスワイプ)



setContentIntent() で PendingIntent を指定すると、Open on phone(携帯で開く)が追加され、それをタップすると指定した PendingIntent が実行される。
■ InboxStyle の Notification
- Intent intent = new Intent(this, MainActivity.class);
- PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent)
- .setStyle(new NotificationCompat.InboxStyle()
- .addLine("1行目")
- .addLine("2行目")
- .setContentTitle("インボックス タイトル")
- .setSummaryText("+3 more"));
- // Notification を発行
- NotificationManagerCompat notificationManager =
- NotificationManagerCompat.from(this);
- notificationManager.notify(notificationId, notificationBuilder.build());

Wear(左: ホーム画面、中央: タップした状態、右: タップしたあと左にスワイプ)



■ BigTextStyle の Notification
- Intent intent = new Intent(this, MainActivity.class);
- PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent)
- .setStyle(new NotificationCompat.BigTextStyle()
- .bigText(getString(R.string.long_text)
- .setContentTitle("ビッグテキスト")
- .setSummaryText("サマリー"));
- // Notification を発行
- NotificationManagerCompat notificationManager =
- NotificationManagerCompat.from(this);
- notificationManager.notify(notificationId, notificationBuilder.build());


Wear(上: ホーム画面、中: タップした状態、下: 中でタップすると全文が見れる)




■ BigPictureStyle の Notification
- Intent intent = new Intent(this, MainActivity.class);
- PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent)
- .setStyle(new NotificationCompat.BigPictureStyle()
- .bigPicture(bitmap)
- .setContentTitle("ビッグピクチャー")
- .setSummaryText("サマリー"));
- // Notification を発行
- NotificationManagerCompat notificationManager =
- NotificationManagerCompat.from(this);
- notificationManager.notify(notificationId, notificationBuilder.build());


Wear(上: ホーム画面、下: タップした状態)




bitPicture() で指定した画像が背景になる。カードがなく背景にセットされた画像を見れるページが追加される。
タイトルには BigPictureStyle.setContentTitle() が表示されるが、その下は setSummaryText() ではなく setContentText() が表示される。
Action ボタンを追加する
Gmail の Notification の Archive に相当するもの。

*アイコンがぼけているように見えますが、実際ぼけています。Open on phone と比較すると明らかにぼけています。
NotificationCompat.Builder の addAction() で、画像、テキスト、タップされたときの PendingIntent を指定する。
- Intent intent = new Intent(this, MainActivity.class);
- PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
- // Action 用の PendingIntent を作成
- Intent mapIntent = new Intent(Intent.ACTION_VIEW);
- 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)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent)
- .addAction(R.drawable.ic_map, "Map", mapPendingIntent);
- // Notification を発行
- NotificationManagerCompat notificationManager =
- NotificationManagerCompat.from(this);
- notificationManager.notify(notificationId, notificationBuilder.build());
つまり、 32dp x 32dp(内枠 24dp x 24dp)。
Wear 側で表示される画像は、スマホの解像度に一致したリソース。スマホが xxhdpi の場合、hdpi のリソースがあっても、xxhdpi のリソースが Wear に表示される。
スマホ

Wear(上: ホーム画面、下: タップした状態)




Wear だけのアクションを追加
Wear にだけ表示されるアクションを追加できる。スマホの Notification には表示されない。
WearableExtender.addAction() を使う。
このメソッドを Action を追加すると、Wearable では NotificationCompatBuilder.addAction() で指定された Action は表示されない。 そのため、別の機能を WearableExtender.addAction() で与えるというよりは、スマホ用に NotificationCompatBuilder.addAction() で指定した機能を、Wear では Wear 用に適したものに置き換えたい場合に使う。
例えば、Gmail の返信機能は、スマホでは返信画面を開くだけだが、Wear では音声入力の結果を返信できるようにしている。
- Intent intent = new Intent(this, MainActivity.class);
- PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
- // Action 用の PendingIntent を作成
- Intent mapIntent = new Intent(Intent.ACTION_VIEW);
- Uri geoUri = Uri.parse("geo:0,0?q=" + Uri.encode(location));
- mapIntent.setData(geoUri);
- PendingIntent mapPendingIntent = PendingIntent.getActivity(this, 0, mapIntent, 0);
- // Wear 用 Action を作成
- NotificationCompat.Action action =
- new NotificationCompat.Action.Builder(R.drawable.ic_map_for_wear,
- "Map for Wear", mapPendingIntent)
- .build();
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent)
- .addAction(R.drawable.ic_map, "Map for phone", mapPendingIntent)
- .extend(new NotificationCompat.WearableExtender().addAction(action));
- // Notification を発行
- NotificationManagerCompat notificationManager =
- NotificationManagerCompat.from(this);
- notificationManager.notify(notificationId, notificationBuilder.build());
スマホ

Wear(上: ホーム画面、下: タップした状態)




背景画像を指定する
■ setLargeIcon() を使う
setLargeIcon() を使うと、カードをタップしたときの背景に利用される。
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent)
- .setLargeIcon(bitmap);
スマホ

Wear(左: ホーム画面、右: タップした状態)



■ WearableExtender.setBackground() を使う
スマホの Notification で LargeIcon を使いたくない場合は、NotificationCompat.WearableExtender の setBackground() を使う。
- NotificationCompat.WearableExtender wearableExtender =
- new NotificationCompat.WearableExtender()
- .setBackground(BitmapFactory.decodeResource(
- getResources(), R.drawable.sample));
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent)
- .extend(wearableExtender);

Wear(左: ホーム画面、右: タップした状態)



Wear のカードでアプリアイコンを表示しない
カードの右上に表示されるアプリアイコンを消すには、NotificationCompat.WearableExtender.setHintHideIcon() を使う。
- NotificationCompat.WearableExtender wearableExtender =
- new NotificationCompat.WearableExtender()
- .setHintHideIcon(true);
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent)
- .extend(wearableExtender);



Wear のカード内にアイコンを表示する
NotificationCompat.WearableExtender.setContentIcon() を使う。
- NotificationCompat.WearableExtender wearableExtender =
- new NotificationCompat.WearableExtender()
- .setContentIcon(R.drawable.ic_droid);
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent)
- .extend(wearableExtender);
ambient 時は白の tint がかかる。
Wear(上: ホーム画面、下: タップした状態)




Wear のカード内にアイコンを左に表示する
NotificationCompat.WearableExtender. setContentIconGravity() で GravityCompat.START を指定する。 指定できるのは START と END で、デフォルトは END。
- NotificationCompat.WearableExtender wearableExtender =
- new NotificationCompat.WearableExtender()
- .setContentIcon(R.drawable.ic_droid)
- .setContentIconGravity(GravityCompat.START);
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent)
- .extend(wearableExtender);




カードの位置を変える
setGravity() を使う。指定できるのは Gravity.TOP、Gravity.CENTER_VERTICAL、Gravity.BOTTOM。デフォルトは BOTTOM。
- NotificationCompat.WearableExtender wearableExtender =
- new NotificationCompat.WearableExtender()
- .setGravity(Gravity.TOP);
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent)
- .extend(wearableExtender);
Gravity.TOP



Gravity.CENTER_VERTICAL



オリジナルのカードを表示する
これは Wear アプリから Notification を発行するときにだけ使えます。
setDisplayIntent() で、カードをタップしたときに表示する Activity を持った PendingIntent を指定します。
Wear 上のコードなので、NotificationCompat ではなく Notification を使っています。
- // この MainActivity は Wear アプリの MainActivity
- Intent intent = new Intent(this, MainActivity.class);
- PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
- // カードをタップしたときに表示される Activity
- // ここでは ImageView 1つだけのレイアウト
- Intent displayIntent = new Intent(this, DisplayActivity.class);
- PendingIntent displayPendingIntent = PendingIntent.getActivity(this,
- 0, displayIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- NotificationCompat.WearableExtender wearableExtender =
- new NotificationCompat.WearableExtender()
- .setDisplayIntent(displayPendingIntent);
- Notification.Builder notificationBuilder =
- new Notification.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent)
- .extend(wearableExtender);
- NotificationManager notificationManager =
- (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- notificationManager.notify(notificationId, notificationBuilder.build());
setContentIntent() で指定した PendingIntent は Open(開く)という Action になる。
Wear(左: ホーム画面、右: タップした状態)



オリジナルのカードの大きさを変える
setCustomSizePreset()を使う。
指定できるのは、SIZE_DEFAULT、SIZE_FULL_SCREEN、SIZE_LEARGE、SIZE_MEDIUM、SIZE_SMALL、SIZE_XSMALL。
- NotificationCompat.WearableExtender wearableExtender =
- new NotificationCompat.WearableExtender()
- .setDisplayIntent(displayPendingIntent)
- .setCustomSizePreset(NotificationCompat.WearableExtender.SIZE_FULL_SCREEN);
- Notification.Builder notificationBuilder =
- new Notification.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent)
- .extend(wearableExtender);
SIZE_FULL_SCREEN



SIZE_LARGE



SIZE_MEDIUM



SIZE_SMALL



SIZE_XSMALL



Notification から音声入力を使う
Notification に返信するなどテキストを入力するアクションがある場合、Wear にはキーボードがない代わりに RemoteInput を使って、音声入力を使うことができる。
*エミュレータには音声入力がないので、AVDの設定で Hardware keyboard present を有効にしておくと、代わりにキーボードで入力できる。
RemoteInput は RemoteInput.Builder を使って作る。コントラクタには音声入力結果に紐づけるキー(文字列)を渡す。
- 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();
例えば、"Ok google, remind me" というと、About what? と When? を別々に入力する画面になる。


RemoteInput を Notification に組み込むには、NotificationCompat.Action.Builder の addRemoteInput() を使う。
- // 音声入力の結果を受けとるための PendingIntent を作る
- Intent replyIntent = new Intent(this, ReplyActivity.class);
- PendingIntent replyPendingIntent =
- PendingIntent.getActivity(this, 0, replyIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- // RemoteInput 用の Action を作る
- NotificationCompat.Action action =
- new NotificationCompat.Action.Builder(R.drawable.ic_reply,
- "Reply message", replyPendingIntent)
- .addRemoteInput(remoteInput)
- .build();
- // WearableExtender に addAction() で RemoteInput の Action を追加
- NotificationCompat.WearableExtender wearableExtender =
- new NotificationCompat.WearableExtender()
- .addAction(action);
- Notification.Builder notificationBuilder =
- new Notification.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("タイトル")
- .setContentText("メッセージ")
- .setContentIntent(pendingIntent)
- .extend(wearableExtender);
PendingIntent に指定された Activity もしくは Service では、getIntent() で取得した Intent を RemoteInput.getResultsFromIntent() に渡して Bundle を取得し、RemoteInput に指定したキーで文字列を取り出す。
- /**
- * Activity.getIntent() を渡す
- */
- private CharSequence getMessageText(Intent intent) {
- Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
- if (remoteInput != null) {
- return remoteInput.getCharSequence(EXTRA_VOICE_REPLY);
- }
- return null;
- }
Wear(上: ホーム画面、中: タップした状態、下: reply actionをタップ&入力した状態)






setLabel() を指定しない場合。


■ 音声入力時に選択肢を与える
選択肢をあらかじめ用意しておくこともできる。選択肢は5つまで。setChoices() で文字列の配列を渡して指定する。
- <?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>
- public static final 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();



■ 選択肢だけから選択させる
音声入力を使わず、選択肢だけから選択させることもできる。 そのためには、setAllowFreeFormInput() で false を指定する。
この場合、setChoices() で選択肢を指定しておかないと IllegalArgumentException になる。
- public static final 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)
- .setAllowFreeFormInput(false)
- .build();

Page を追加する
追加の情報を表示したい場合などに、Page を追加することができる。
例えば、ハングアウトのカードでは、メインのカードに最新のメッセージが表示され、次のページに最近の投稿メッセージが表示される。
Google Now の天気カードでは、メインのカードに今日の天気、次のページに明日から四日間の天気が表示される。


Page を追加するには NotificationCompat.WearableExtender の addPage() か addPages() を使う。
- // メインの Notification の NotificationCompat.Builder を作る
- NotificationCompat.Builder notificationBuilder =
- new NotificationCompat.Builder(this)
- .setSmallIcon(R.drawable.ic_notif)
- .setContentTitle("Page 1")
- .setContentText("吾輩は猫である")
- .setContentIntent(pendingIntent);
- // 2ページ目の Notification を作る
- Notification secondPageNotification =
- new NotificationCompat.Builder(this)
- .setContentTitle("Page 2")
- .setContentText(getString(R.string.long_text))
- .build();
- // addPage() で2ページ目を追加し、extend() で1ページ目を拡張する
- Notification twoPageNotification =
- new NotificationCompat.WearableExtender()
- .addPage(secondPageNotification)
- .extend(notificationBuilder)
- .build();
- // Notification を発行
- NotificationManagerCompat notificationManager
- = NotificationManagerCompat.from(this);
- notificationManager.notify(notificationId, twoPageNotification);

Wear(上: ホーム画面、中: タップした状態)




Notification をまとめる
同じような通知はまとめて表示することが推奨されているが、Wearでは個別の通知の中がみれないのは不便。
そこで、1つのカードにグループ化し、カードをタップするとそれぞれの Notification が個別のカードに別れるよう作ることができる。
Gmailのカードはまさにこの形になっている。
カードをグループ化するには NotificationCompat.Builder.setGroup() で同じ文字列を指定する。
- final static String GROUP_KEY_EMAILS = "group_key_emails";
- // Build the notification, setting the group appropriately
- Notification notif = new NotificationCompat.Builder(mContext)
- .setContentTitle("New mail from " + sender1)
- .setContentText(subject1)
- .setSmallIcon(R.drawable.new_mail);
- .setGroup(GROUP_KEY_EMAILS)
- .build();
- // Issue the notification
- NotificationManagerCompat notificationManager =
- NotificationManagerCompat.from(this);
- notificationManager.notify(notificationId1, notif);
- Notification notif2 = new NotificationCompat.Builder(mContext)
- .setContentTitle("New mail from " + sender2)
- .setContentText(subject2)
- .setSmallIcon(R.drawable.new_mail);
- .setGroup(GROUP_KEY_EMAILS)
- .build();
- notificationManager.notify(notificationId2, notif2);




グループ化されたカードがスタックされる順番は、新しく発行されたものが上になる(デフォルト)。
setSortKey() を使うと、順番を任意に指定することができる。
setGroup() を指定した Notification はスマホでは表示されないため、Summary Notification を用意する。 Summary Notification では setGroup() に同じ文字列を指定し、setGroupSummry() に true を指定する。この Summary Notification は Wear では表示されない。
- Notification summaryNotification = new NotificationCompat.Builder(mContext)
- .setContentTitle("2 new messages")
- .setSmallIcon(R.drawable.ic_notif)
- .setStyle(new NotificationCompat.InboxStyle()
- .addLine(line1)
- .addLine(line2)
- .setBigContentTitle("2 new messages")
- .setSummaryText(summary))
- .setGroup(GROUP_KEY_EMAILS)
- .setGroupSummary(true)
- .build();
- notificationManager.notify(notificationId3, summaryNotification);

Summary Notification は Wear に直接表示されることはないが、 NotificationCompat.WearableExtender を使ってスタック全体の背景やスタック全体に対する Action を指定できる。
- Bitmap background = BitmapFactory.decodeResource(getResources(),
- R.drawable.ic_background);
- NotificationCompat.WearableExtender wearableExtender =
- new NotificationCompat.WearableExtender()
- .setBackground(background);
- // extend() で背景を指定
- Notification summaryNotificationWithBackground =
- new NotificationCompat.Builder(mContext)
- .setContentTitle("2 new messages")
- ...
- .extend(wearableExtender)
- .setGroup(GROUP_KEY_EMAILS)
- .setGroupSummary(true)
- .build();



0 件のコメント:
コメントを投稿