連載
» 2012年02月16日 00時00分 公開

Androidで動く携帯Javaアプリ作成入門(29):Androidのウィジェットにノーティフィケーションするには (2/2)

[緒方聡,株式会社イーフロー]
前のページへ 1|2       

RemoteViewsでNotificationを作成

 以下に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の8つのフラグ

 動画を見て気が付いたかもしれませんが、この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の実装

 今回と次回の目標は、Notificationを使用してオーディオリモコンを実装することでした。

オーディオリモコンのデザイン

 デザインは下記のように「前の曲」「再生/一時停止」「次の曲」「停止」の操作を提供し、現在再生中の曲情報を表示する感じにします。

図5 リモートコントロールのモック 図5 リモートコントロールのモック

 トラック再生時間はChronometerで自動的に更新しますが、その他の曲情報は、ボタンのイベントに応じて表示し直さなければなりません。また、「再生/一時停止」は押された際にイメージの表示を切り替えなければなりません。

「PendingIntent」とは

 RemoteViews上のボタンは、押された際にイベントを直接取得できず、代わりに「android.app.PendingIntent」というIntentを使用します。

 PendingIntentを使うには、「どのコンテキストで動作させるのか」というのを考えてアプリ全体を設計する必要があります。PendingIntent.getActivity()メソッド、PendingIntent.getService()メソッド、PendingIntent.getBroadcast()メソッドでPendingIntentを生成しますが、それぞれandroid.app.Activityandroid.app.Serviceandroid.content.BroadcastReceiverがPendingIntentを実行するコンテキストです。

 今回のデモは、簡単のためにBroadcastReceiverを使用していますが、おそらく本当にオーディオリモコンを実装するのであれば、Serviceにする方が簡単になるはずです。

BroadcastReceiverでNotificationを操作

 以下に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を更新できます。

RemoteViewsを使ったカスタムレイアウトの5カ条

 Notificationは、実はAndroid 1.0からある基本的な機能で、Androidユーザーならなじみのあると思いますが、RemoteViewsを使うことで、さらに便利な機能をユーザーに提供可能になります。

 RemoteViewsを使用してカスタムレイアウトを作成する際には、以下のガイドラインも参考にしてみてください。

 書かれている内容をいくつかピックアップします。

  1. 乱用してはいけない
  2. コンテンツの場所は適切に
  3. 同種の複数の通知はまとめる
  4. Androidが提供する通知アイコンとは異なるアイコンデザインにする
  5. トーストで済むならトーストで

 次回は、今回作成したNotificationを使用したリモートコントロールを紹介します。


「Androidで動く携帯Javaアプリ作成入門」バックナンバー
前のページへ 1|2       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。