X

NotificationListenerServiceを使ってステータスバーを監視する

Android 4.3の新機能、NotificationListenerServiceを解説します。NotificationListenerServiceは、ステータスバーへの通知を監視でき、ノーティフィケーションの取得や削除のタイミングでイベントを受け取れます。ステータスバーの内容をすべて読み取れる強力な機能ですが、その分セキュリティリスクも高くなります。そのためAndroid 4.3では設定画面から利用者が自分で許可しないかぎり機能は有効になりません

通知内容にあわせて自動で処理できることから応用範囲の広い機能といえます。ノーティフィケーション中のテキストに特定の文字列があればバイブレーションで震える、音を鳴らす、指定のアプリを起動する、またアプリにインターネットパーミッションがついていればノーティフィケーションの内容を外部へ送信してしまうかもしれません。使い方はアプリケーション次第なので信頼できるアプリケーション(もしくは業務用など特定の範囲で利用するなど)で注意してください。実装においても十分な検討が必要なのは言うまでもありません。

NotificationListenerServiceの主なメソッドは次の通りです。

NotificationListenerServiceの主なメソッド

メソッド名 説明
onNotificationPosted() ノーティフィケーションが通知されたときに呼び出されます
onNotificationRemoved() ノーティフィケーションが消されたときに呼び出されます
getActiveNotifications() 現在表示中のノーティフィケーションをすべて取得します
cancelNotification() 特定のノーティフィケーションをステータスバーから消します。キャンセルしたノーティフィケーションについてonNotificationRemovedが1回よびだされます
cancelAllNotifications() 消去可能なすべてのノーティフィケーションを消去します。 onNotificationRemoved()が複数回呼び出されます

表のとおり、ステータスバーに表示されたノーティフィケーションの情報を取得できるほか、内容や通知を出したアプリに応じてノーティフィケーションを非表示にもできます。

詳しい操作やサンプルコードは続きから

NotificationListenerServiceに通知されるノーティフィケーションはStatusBarNotificationクラスとして扱われています。StatusBarNotificationは次のようなメソッドを持ちます。

StatusBarNotificationクラスの主なメソッド

メソッド名 説明
getId() ノーティフィケーションのIDを取得します
getPackageName() パッケージ名を取得します
getPostTime() 通知時刻を取得します
isClearable() ノーティフィケーションが消去できるか、確認できます
isOngoing() 処理中(再生中、ダウンロード中)かを判定できます
getNotification() ノーティフィケーションを取得できます。tickerTextメンバで通知内容を取得できます

StatusBarNotification#isClearable()メソッドでは、FLAG_ONGOING_EVENTフラグとFLAG_NO_CLEARフラグがどちらも立っていない場合、trueが返却されます。このフラグはノーティフィケーションを発行するときに設定されます(具体的にはNotificationManager#notify(int id, Notification notification)メソッドです)。同様にisOngoing()メソッドではFLAG_ONGOING_EVENTフラグが立っていればtrueを返却します。

getNotification()メソッドを使えば通知内容も取得できます。

ノーティフィケーションの表示/非表示を受け取る

ではサンプルコードで動作を確認してみましょう。
(実際に実行するときは設定画面から許可しなければいけませんが、手順は最後に解説します)。

NotificationListenerServiceは名前の通りサービスとして動作するため、最初にAndroidManifest.xmlへ追加します。

■AndroidManifest.xml

    ...省略...
    <application
        <activity
        ...省略...
        </activity>
        <service android:name=".NotificationService"
             android:label="@string/app_name"
             android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
             <intent-filter>
                  <action android:name=
                      "android.service.notification.NotificationListenerService" />
             </intent-filter>
        </service>
    </application>

サンプルコードではNotificationListenerServiceの実装名をNotificationServiceとしました。インテントフィルタでactionを指定する必要があるため、忘れないように追記しましょう(10,11行目)。

■NotificationService.java

public class NotificationService extends NotificationListenerService {

    private String TAG = "Notification";
    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public void onNotificationPosted(StatusBarNotification sbn) {
        // ステータスバーに通知があった場合
        Log.d(TAG,"onNotificationPosted");
        showLog(sbn); // 通知内容をログに出力する

        // アクティブな通知をすべて取得する
        StatusBarNotification[] array = getActiveNotifications();
        for(int i=0; i < array.length; i++){
            showLog(array[i]);
        }
    }

    @Override
    public void onNotificationRemoved(StatusBarNotification sbn) {
        // ステータスバーから通知が消された場合
        Log.d(TAG,"onNotificationRemoved");
        showLog(sbn); // 通知内容をログに出力する
    }

    private void showLog( StatusBarNotification sbn ){

        int id = sbn.getId();
        String name = sbn.getPackageName();
        long time = sbn.getPostTime();
        boolean clearable = sbn.isClearable();
        boolean playing = sbn.isOngoing();
        CharSequence text = sbn.getNotification().tickerText;

        Log.d(TAG,"id:" + id + " name:" + name + " time:" +time);
        Log.d(TAG,"isClearable:" + clearable +
                " isOngoing:" + playing + " tickerText:" +text);
    }
}

onCreate()メソッドとonDestroy()メソッドは通常のサービスと一緒ですが、ステータスバーに通知があった場合に呼び出されるonNotificationPosted()メソッド、ステータスバーから通知が消された場合に呼び出されるonNotificationRemoved()メソッドが追加されています。

getActiveNotifications()メソッドを使うとアクティブな(現在ステータスバーに表示中の)ノーティフィケーションがすべて取得できます(21行目)。サンプルコードでは実装していませんが、通常のサービスと同じふるまいなのでブロードキャストレシーバーを使えばActivity等と通信することも可能です。

実行結果は次の通りです

10-07 02:01:29.782: D/Notification(5855): onNotificationPosted
10-07 02:01:29.782: D/Notification(5855): id:17040501 name:android time:1381078889793
10-07 02:01:29.782: D/Notification(5855): isClearable:false isOngoing:true
                                                 tickerText:USBデバッグが接続されました

10-07 02:01:28.181: I/Notification(5855): onNotificationRemoved
10-07 02:01:28.181: D/Notification(5855): id:17040501 name:android time:1381078460010
10-07 02:01:28.181: D/Notification(5855): isClearable:false isOngoing:true
                                                 tickerText:USBデバッグが接続されました

通知へのアクセスを許可する

サンプルコードを動かすためにはAndroidの設定>セキュリティ>通知へのアクセスから利用者自身でアプリケーションを許可する必要があります。

設定>セキュリティ セキュリティ>通知へのアクセス 通知へのアクセス>チェックボックスをON

以上、おつかれさまでした。

mhidaka: Software Engineerだよ。DroidKaigi Organizer / Androidと組込とRe:VIEW。techbooster主宰。mhidaka's writings http://booklog.jp/users/mhidaka 技術書典! http://techbookfest.org