Home | History | Annotate | Download | only in notificationlistener
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.example.notificationlistener;
     17 
     18 
     19 import android.app.Notification;
     20 import android.app.PendingIntent;
     21 import android.content.BroadcastReceiver;
     22 import android.content.ComponentName;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.IntentFilter;
     26 import android.os.Handler;
     27 import android.os.Message;
     28 import android.os.RemoteException;
     29 import android.service.notification.NotificationListenerService;
     30 import android.service.notification.NotificationListenerService.Ranking;
     31 import android.service.notification.NotificationListenerService.RankingMap;
     32 import android.service.notification.StatusBarNotification;
     33 import android.support.v4.content.LocalBroadcastManager;
     34 import android.text.TextUtils;
     35 import android.util.Log;
     36 
     37 import java.util.ArrayList;
     38 import java.util.Collections;
     39 import java.util.Comparator;
     40 import java.util.List;
     41 
     42 public class Listener extends NotificationListenerService {
     43     private static final String TAG = "SampleListener";
     44 
     45     // Message tags
     46     private static final int MSG_NOTIFY = 1;
     47     private static final int MSG_CANCEL = 2;
     48     private static final int MSG_STARTUP = 3;
     49     private static final int MSG_ORDER = 4;
     50     private static final int MSG_DISMISS = 5;
     51     private static final int MSG_LAUNCH = 6;
     52     private static final int MSG_SNOOZE = 7;
     53 
     54     static final String ACTION_DISMISS = "com.android.example.notificationlistener.DISMISS";
     55     static final String ACTION_LAUNCH = "com.android.example.notificationlistener.LAUNCH";
     56     static final String ACTION_REFRESH = "com.android.example.notificationlistener.REFRESH";
     57     static final String ACTION_STATE_CHANGE = "com.android.example.notificationlistener.STATE";
     58     static final String EXTRA_KEY = "key";
     59 
     60     private static ArrayList<StatusBarNotification> sNotifications;
     61     private static boolean sConnected;
     62 
     63     public static List<StatusBarNotification> getNotifications() {
     64         return sNotifications;
     65     }
     66 
     67     public static boolean isConnected() {
     68         return sConnected;
     69     }
     70 
     71     public static void toggleSnooze(Context context) {
     72         if (sConnected) {
     73             Log.d(TAG, "scheduling snooze");
     74             if (sHandler != null) {
     75                 sHandler.sendEmptyMessage(MSG_SNOOZE);
     76             }
     77         } else {
     78             Log.d(TAG, "trying to unsnooze");
     79             try {
     80                 NotificationListenerService.requestRebind(
     81                         ComponentName.createRelative(context.getPackageName(),
     82                                 Listener.class.getCanonicalName()));
     83             } catch (Exception e) {
     84                 Log.e(TAG, "failed to rebind service", e);
     85             }
     86         }
     87     }
     88 
     89     private final Ranking mTmpRanking = new Ranking();
     90 
     91     private static Handler sHandler;
     92 
     93     private RankingMap mRankingMap;
     94 
     95     private class Delta {
     96         final StatusBarNotification mSbn;
     97         final RankingMap mRankingMap;
     98 
     99         public Delta(StatusBarNotification sbn, RankingMap rankingMap) {
    100             mSbn = sbn;
    101             mRankingMap = rankingMap;
    102         }
    103     }
    104 
    105     private final Comparator<StatusBarNotification> mRankingComparator =
    106             new Comparator<StatusBarNotification>() {
    107 
    108                 private final Ranking mLhsRanking = new Ranking();
    109                 private final Ranking mRhsRanking = new Ranking();
    110 
    111                 @Override
    112                 public int compare(StatusBarNotification lhs, StatusBarNotification rhs) {
    113                     mRankingMap.getRanking(lhs.getKey(), mLhsRanking);
    114                     mRankingMap.getRanking(rhs.getKey(), mRhsRanking);
    115                     return Integer.compare(mLhsRanking.getRank(), mRhsRanking.getRank());
    116                 }
    117             };
    118 
    119     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    120         @Override
    121         public void onReceive(Context context, Intent intent) {
    122             String key = intent.getStringExtra(EXTRA_KEY);
    123             int what = MSG_DISMISS;
    124             if (ACTION_LAUNCH.equals(intent.getAction())) {
    125                 what = MSG_LAUNCH;
    126             }
    127             Log.d(TAG, "received an action broadcast " + intent.getAction());
    128             if (!TextUtils.isEmpty(key)) {
    129                 Log.d(TAG, "  on " + key);
    130                 Message.obtain(sHandler, what, key).sendToTarget();
    131             }
    132         }
    133     };
    134 
    135     @Override
    136     public void onCreate() {
    137         super.onCreate();
    138         sHandler = new Handler() {
    139             @Override
    140             public void handleMessage(Message msg) {
    141                 Delta delta = null;
    142                 if (msg.obj instanceof Delta) {
    143                     delta = (Delta) msg.obj;
    144                 }
    145 
    146                 switch (msg.what) {
    147                     case MSG_NOTIFY:
    148                         Log.i(TAG, "notify: " + delta.mSbn.getKey());
    149                         synchronized (sNotifications) {
    150                             boolean exists = mRankingMap.getRanking(delta.mSbn.getKey(), mTmpRanking);
    151                             if (!exists) {
    152                                 sNotifications.add(delta.mSbn);
    153                             } else {
    154                                 int position = mTmpRanking.getRank();
    155                                 sNotifications.set(position, delta.mSbn);
    156                             }
    157                             mRankingMap = delta.mRankingMap;
    158                             Collections.sort(sNotifications, mRankingComparator);
    159                             Log.i(TAG, "finish with: " + sNotifications.size());
    160                         }
    161                         LocalBroadcastManager.getInstance(Listener.this)
    162                                 .sendBroadcast(new Intent(ACTION_REFRESH)
    163                                         .putExtra(EXTRA_KEY, delta.mSbn.getKey()));
    164                         break;
    165 
    166                     case MSG_CANCEL:
    167                         final String cancelKey = delta.mSbn.getKey();
    168                         Log.i(TAG, "remove: " + cancelKey);
    169                         synchronized (sNotifications) {
    170                             boolean exists = mRankingMap.getRanking(cancelKey, mTmpRanking);
    171                             if (exists) {
    172                                 sNotifications.remove(mTmpRanking.getRank());
    173                             }
    174                             mRankingMap = delta.mRankingMap;
    175                             Collections.sort(sNotifications, mRankingComparator);
    176                         }
    177                         LocalBroadcastManager.getInstance(Listener.this)
    178                                 .sendBroadcast(new Intent(ACTION_REFRESH)
    179                                         .putExtra(EXTRA_KEY, cancelKey));
    180                         break;
    181 
    182                     case MSG_ORDER:
    183                         Log.i(TAG, "reorder");
    184                         synchronized (sNotifications) {
    185                             mRankingMap = delta.mRankingMap;
    186                             Collections.sort(sNotifications, mRankingComparator);
    187                         }
    188                         LocalBroadcastManager.getInstance(Listener.this)
    189                                 .sendBroadcast(new Intent(ACTION_REFRESH));
    190                         break;
    191 
    192                     case MSG_STARTUP:
    193                         sConnected = true;
    194                         fetchActive();
    195                         Log.i(TAG, "start with: " + sNotifications.size() + " notifications.");
    196                         LocalBroadcastManager.getInstance(Listener.this)
    197                                 .sendBroadcast(new Intent(ACTION_REFRESH));
    198                         LocalBroadcastManager.getInstance(Listener.this)
    199                                 .sendBroadcast(new Intent(ACTION_STATE_CHANGE));
    200                         break;
    201 
    202                     case MSG_DISMISS:
    203                         if (msg.obj instanceof String) {
    204                             final String key = (String) msg.obj;
    205                             mRankingMap.getRanking(key, mTmpRanking);
    206                             StatusBarNotification sbn = sNotifications.get(mTmpRanking.getRank());
    207                             if ((sbn.getNotification().flags & Notification.FLAG_AUTO_CANCEL) != 0 &&
    208                                     sbn.getNotification().contentIntent != null) {
    209                                 try {
    210                                     sbn.getNotification().contentIntent.send();
    211                                 } catch (PendingIntent.CanceledException e) {
    212                                     Log.d(TAG, "failed to send intent for " + sbn.getKey(), e);
    213                                 }
    214                             }
    215                             cancelNotification(key);
    216                         }
    217                         break;
    218 
    219                     case MSG_LAUNCH:
    220                         if (msg.obj instanceof String) {
    221                             final String key = (String) msg.obj;
    222                             mRankingMap.getRanking(key, mTmpRanking);
    223                             StatusBarNotification sbn = sNotifications.get(mTmpRanking.getRank());
    224                             if (sbn.getNotification().contentIntent != null) {
    225                                 try {
    226                                     sbn.getNotification().contentIntent.send();
    227                                 } catch (PendingIntent.CanceledException e) {
    228                                     Log.d(TAG, "failed to send intent for " + sbn.getKey(), e);
    229                                 }
    230                             }
    231                             if ((sbn.getNotification().flags & Notification.FLAG_AUTO_CANCEL) != 0) {
    232                                 cancelNotification(key);
    233                             }
    234                         }
    235                         break;
    236 
    237                     case MSG_SNOOZE:
    238                         Log.d(TAG, "trying to snooze");
    239                         try {
    240                             requestUnbind();
    241                         } catch (Exception e) {
    242                             Log.e(TAG, "failed to unbind service", e);
    243                         }
    244                         break;
    245                 }
    246             }
    247         };
    248         Log.d(TAG, "registering broadcast listener");
    249         final IntentFilter intentFilter = new IntentFilter();
    250         intentFilter.addAction(ACTION_DISMISS);
    251         intentFilter.addAction(ACTION_LAUNCH);
    252         LocalBroadcastManager.getInstance(this).registerReceiver(mBroadcastReceiver, intentFilter);
    253     }
    254 
    255     @Override
    256     public void onDestroy() {
    257         sConnected = false;
    258         LocalBroadcastManager.getInstance(Listener.this)
    259                 .sendBroadcast(new Intent(ACTION_STATE_CHANGE));
    260         sHandler = null;
    261         LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver);
    262         super.onDestroy();
    263     }
    264 
    265     @Override
    266     public void onListenerConnected() {
    267         Log.w(TAG, "onListenerConnected: ");
    268         Message.obtain(sHandler, MSG_STARTUP).sendToTarget();
    269     }
    270 
    271     @Override
    272     public void onListenerDisconnected() {
    273         Log.w(TAG, "onListenerDisconnected: ");
    274     }
    275 
    276     @Override
    277     public void onNotificationRankingUpdate(RankingMap rankingMap) {
    278         Log.w(TAG, "onNotificationRankingUpdate");
    279         Message.obtain(sHandler, MSG_ORDER,
    280                 new Delta(null, rankingMap)).sendToTarget();
    281     }
    282 
    283     @Override
    284     public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
    285         Log.w(TAG, "onNotificationPosted: " + sbn.getKey());
    286         Message.obtain(sHandler, MSG_NOTIFY,
    287                 new Delta(sbn, rankingMap)).sendToTarget();
    288     }
    289 
    290     @Override
    291     public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
    292         Log.w(TAG, "onNotificationRemoved: " + sbn.getKey());
    293         Message.obtain(sHandler, MSG_CANCEL,
    294                 new Delta(sbn, rankingMap)).sendToTarget();
    295     }
    296 
    297     private void fetchActive() {
    298         mRankingMap = getCurrentRanking();
    299         sNotifications = new ArrayList<StatusBarNotification>();
    300         for (StatusBarNotification sbn : getActiveNotifications()) {
    301             sNotifications.add(sbn);
    302             Log.w(TAG, "startup poll: " + sbn.getKey());
    303         }
    304         Collections.sort(sNotifications, mRankingComparator);
    305     }
    306 }
    307