スマホから送信したNotificationでAndroid Wear上のActivityを開く方法
最近Android Wearをいじっているのでその話題。スマホからWearに対して送信したNotificationで、Android Wear上のActivityを開く方法について書きます。
Google カメラアプリの挙動
先日、Googleのカメラアプリがアップデートされて、Wearからシャッターを切れるようになりました。この挙動がわりと面白くて、スマホ側でカメラアプリを起動するとWearに通知が表示され、その通知をクリックすると、Wear上でシャッターを押すための専用Activityが起動される流れになっています。
この挙動を実装しようとすると、なかなか一筋縄ではいきません。現状のNotification APIの仕様ではスマホ側から発行したNotificationは、スマホ側のActivityしか開くことができないからです。
これを解決する方法の一つが、スマホからWearへの通知をNotification APIではなくData APIなどのデータ送受信APIで行い、Wear側でNotification APIを使って通知を発行するという方法です。Googleのカメラアプリもこの方法を使っているようです。
実装方法
実際のコードは以下のようになります。(プロジェクト全体はthorikawa/WearNotificationSample · GitHubにあげてあります)
スマホ側では、Data APIを利用してデータ変更を行います。
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(Constants.NOTIFICATION_PATH); dataMapRequest.getDataMap().putString(Constants.NOTIFICATION_TITLE, "This is the title"); dataMapRequest.getDataMap().putString(Constants.NOTIFICATION_CONTENT, "This is a notification with some text."); // Set timestamp so that it always trigger onDataChanged event dataMapRequest.getDataMap().putLong(Constants.NOTIFICATION_TIME, System.currentTimeMillis()); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi.putDataItem(mGoogleApiClient, putDataRequest);
一方、Wear側ではデータの変更イベントをServiceで受け取るようにします。
AndrodManifest.xmlでデータ変更時に発行されるActionを受け取るServiceを定義します。
<service android:name=".NotificationUpdateService" android:exported="true"> <intent-filter> <action android:name="com.google.android.gms.wearable.BIND_LISTENER" /> </intent-filter> </service>
このServiceはWearableListenerServiceを継承しており、onDataChangedでスマホ側からのデータ変更を検知し、実際にWear上で表示されるNotificationを発行します。その際にaddActionやsetContentIntentなどで、Wear上のActivityに対するPendingIntentを設定してやれば、NotificationをクリックしてWear上でActivityを開くことができます。
public class NotificationUpdateService extends WearableListenerService { ... @Override public void onDataChanged(DataEventBuffer dataEvents) { for (DataEvent dataEvent : dataEvents) { if (dataEvent.getType() == DataEvent.TYPE_CHANGED) { if (Constants.NOTIFICATION_PATH.equals(dataEvent.getDataItem().getUri().getPath())) { DataMapItem dataMapItem = DataMapItem.fromDataItem(dataEvent.getDataItem()); String title = dataMapItem.getDataMap().getString(Constants.NOTIFICATION_TITLE); String content = dataMapItem.getDataMap().getString(Constants.NOTIFICATION_CONTENT); sendNotification(title, content); } } } } private void sendNotification(String title, String content) { // this intent will open the activity when the user taps the "open" action on the notification Intent viewIntent = new Intent(this, MyActivity.class); PendingIntent pendingViewIntent = PendingIntent.getActivity(this, 0, viewIntent, 0); NotificationCompat.Builder builder = new NotificationCompat.Builder(this) .setSmallIcon(R.drawable.ic_launcher) .setContentTitle(title) .setContentText(content) .addAction(R.drawable.ic_launcher, "Open", pendingViewIntent) .setLocalOnly(true) .extend(new NotificationCompat.WearableExtender().setContentAction(0).setHintHideIcon(true)); Notification notification = builder.build(); NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(this); notificationManagerCompat.notify(notificationId++, notification); } ... }
まとめ
このような感じで、Wearで利用できるAPIは限られていますが、組み合わせによってはまだまだ面白い使い方ができそうです。