以下にRemoteViewsを使用してNotificationを作成している個所を抜粋します。
RemoteViews views = new RemoteViews(getPackageName(), R.layout.remote_views); notification = builder.setContent(views) .setSmallIcon(R.drawable.earth) .setContentIntent(pendingIntent) .setWhen(System.currentTimeMillis()) .getNotification();
RemoteViewsのインスタンスはコンストラクタにパッケージ名とレイアウトを指定して生成します。このレイアウトは、ADTのレイアウトエディタを使用してレイアウトできるので、App Widgetsよりは快適に作業が行えます。
Notificationは、ここでは「Notification.Builder」クラスを使用して作成しています。ちなみに、Notificationは、コンストラクタとsetLatestEventInfo()メソッド、Notification.Builderクラスを使用してインスタンスを生成・設定する他に、フィールドにオブジェクトを直接設定するバリエーションもあります。
今回のサンプルアプリではすべての方法でNotificationを生成・設定しているので、ソースコードを参照してみてください。
動画を見て気が付いたかもしれませんが、このNotificationはクリックしても自動的に消えませんでした。これは、Notificationに以下のフラグを設定していないためです。
// このフラグ設定がない notification.flags = Notification.FLAG_AUTO_CANCEL;
このフィールドは、複数のフラグをXORで設定可能です。
フラグ | 説明 |
---|---|
FLAG_AUTO_CANCEL | Notificationがクリックされた際に、通知をキャンセル |
FLAG_FOREGROUND_SERVICE | 現在実行中のサービスを表す |
FLAG_HIGH_PRIORITY | ステータスバーが非表示でもNotificationを表示することがある |
FLAG_INSISTENT | Notificationが開かれたりキャンセルされたりするまで、音声によって繰り返し通知 |
FLAG_NO_CLEAR | ユーザーがNotificationをクリアしてもクリアされないようにする |
FLAG_ONGOING_EVENT | 現在進行中のイベントを表す |
FLAG_ONLY_ALERT_ONCE | Notificationの通知のたびにサウンドまたはバイブレーションを作動させたい場合に設定 |
FLAG_SHOW_LIGHTS | LEDを点灯させたい場合に設定 |
通常はFLAG_AUTO_CANCELだけが設定されていればいいと思いますが、必要に応じて他のフラグも効果的に使用してみてください。
今回と次回の目標は、Notificationを使用してオーディオリモコンを実装することでした。
デザインは下記のように「前の曲」「再生/一時停止」「次の曲」「停止」の操作を提供し、現在再生中の曲情報を表示する感じにします。
トラック再生時間はChronometerで自動的に更新しますが、その他の曲情報は、ボタンのイベントに応じて表示し直さなければなりません。また、「再生/一時停止」は押された際にイメージの表示を切り替えなければなりません。
RemoteViews上のボタンは、押された際にイベントを直接取得できず、代わりに「android.app.PendingIntent」というIntentを使用します。
PendingIntentを使うには、「どのコンテキストで動作させるのか」というのを考えてアプリ全体を設計する必要があります。PendingIntent.getActivity()メソッド、PendingIntent.getService()メソッド、PendingIntent.getBroadcast()メソッドでPendingIntentを生成しますが、それぞれandroid.app.Activity、android.app.Service、android.content.BroadcastReceiverがPendingIntentを実行するコンテキストです。
今回のデモは、簡単のためにBroadcastReceiverを使用していますが、おそらく本当にオーディオリモコンを実装するのであれば、Serviceにする方が簡単になるはずです。
以下にBroadcastReceiverでNotificationを操作している個所をソースコードを抜粋して説明します。
@Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // 【1】 Notification notification = intent.getParcelableExtra("notification"); // 【2】 boolean isPlaying = intent.getBooleanExtra("isPlaying", false); long baseTime = intent.getLongExtra("baseTime", 0); // 【3】 NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); if (ACTION_INIT.equals(action)) { intent.setAction(ACTION_PLAY_PAUSE); } else if (ACTION_PLAY_PAUSE.equals(intent.getAction())) { if (isPlaying) { long current = System.currentTimeMillis() - baseTime; // 【4】 notification.contentView.setImageViewResource( R.id.playpause, R.drawable.media_play_s); notification.contentView.setChronometer( R.id.chronometer, SystemClock.elapsedRealtime() - current, null, false); intent.putExtra("current", current); } else { long current = intent.getLongExtra("current", 0); notification.contentView.setImageViewResource( R.id.playpause, R.drawable.media_pause_s); notification.contentView.setChronometer( R.id.chronometer, SystemClock.elapsedRealtime() - current, null, true); intent.putExtra("baseTime", System.currentTimeMillis() - current); } intent.putExtra("isPlaying", !isPlaying); intent.putExtra("notification", notification); } // 【5】 PendingIntent pendingIntent = PendingIntent.getBroadcast( context, R.id.playpause, intent, PendingIntent.FLAG_UPDATE_CURRENT); // 【6】 notification.contentView.setOnClickPendingIntent(R.id.playpause, pendingIntent); // 【7】 manager.notify(R.id.button3, notification); }
【1】では、IntentからNotificationを取り出します。これができるため、Notificationのカスタマイズした動作が手軽に行えます。
【2】では、現在再生中かどうかをIntentから取得します。BroadcastReceiverではなくServiceであれば、状態を保持できるため、このような受け渡しは不要です。
【3】では、android.app.NotificationManagerを取得します。BroadcastReceiverは状態が保持できないため、毎回取得する必要があります。
【4】では、RemoteViewsに値を設定します。RemoteViewsには、サポートするウィジェットに対する汎用的な設定メソッドが多数用意されている一方、ウィジェットに設定されている値を取得するメソッドは提供されていないため、現在の値が何であるか、というのはRemoteViewsを管理する側で把握しておかなければなりません。
【5】では、PendingIntentをBoradcastReceiverのコンテキストで生成します。第4引数のフラグは、以下のような意味を持ちます。
フラグ | 説明 |
---|---|
FLAG_CANCEL_CURRENT | すでにPendingIntentが存在する場合、古い方をキャンセル |
FLAG_NO_CREATE | PendingIntentがまだ存在する場合、生成せずにnullを返す |
FLAG_ONE_SHOT | 1度だけ使用可能なPendingIntentを生成 |
FLAG_UPDATE_CURRENT | Intent内のエクストラデータだけを差し替えたPendingIntentを返す |
例えば、Intentのアクションが変わる場合は、FLAG_UPDATE_CURRENTは使用できませんが、今回のようにエクストラデータのみ変わる場合は、FLAG_CANCEL_CURRENTよりもFLAG_UPDATE_CURRENTを使用する方がいいようです。
第2引数にはウィジェット単位でユニークな値を指定するようにします。PendingIntentを設定する先のViewのIDを指定すると簡単です。この第2引数はApp widgetsを含めRemoteViewsにPendingIntentを設定する際の落とし穴なので注意してください。
【6】では、ボタンにPendingIntentを設定しています。このようにすることで、ボタンがクリックされた際に、PendingIntent内のIntentがブロードキャストされる形になります。
【7】では、Notifiationを通知します。第一引数のIDはアプリ内でユニークな値を指定し、同一のIDでNotifiationを通知することにより、既存のNotificationを更新できます。
Notificationは、実はAndroid 1.0からある基本的な機能で、Androidユーザーならなじみのあると思いますが、RemoteViewsを使うことで、さらに便利な機能をユーザーに提供可能になります。
RemoteViewsを使用してカスタムレイアウトを作成する際には、以下のガイドラインも参考にしてみてください。
書かれている内容をいくつかピックアップします。
次回は、今回作成したNotificationを使用したリモートコントロールを紹介します。
Copyright © ITmedia, Inc. All Rights Reserved.