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.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.os.Handler;
26 import android.os.Message;
27 import android.service.notification.NotificationListenerService;
28 import android.service.notification.NotificationListenerService.Ranking;
29 import android.service.notification.NotificationListenerService.RankingMap;
30 import android.service.notification.StatusBarNotification;
31 import android.support.v4.content.LocalBroadcastManager;
32 import android.text.TextUtils;
33 import android.util.Log;
34 
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.Comparator;
38 import java.util.List;
39 
40 public class Listener extends NotificationListenerService {
41     private static final String TAG = "SampleListener";
42 
43     // Message tags
44     private static final int MSG_NOTIFY = 1;
45     private static final int MSG_CANCEL = 2;
46     private static final int MSG_STARTUP = 3;
47     private static final int MSG_ORDER = 4;
48     private static final int MSG_DISMISS = 5;
49     private static final int MSG_LAUNCH = 6;
50     private static final int PAGE = 10;
51 
52     static final String ACTION_DISMISS = "com.android.example.notificationlistener.DISMISS";
53     static final String ACTION_LAUNCH = "com.android.example.notificationlistener.LAUNCH";
54     static final String ACTION_REFRESH = "com.android.example.notificationlistener.REFRESH";
55     static final String EXTRA_KEY = "key";
56 
57     private static ArrayList<StatusBarNotification> sNotifications;
58 
getNotifications()59     public static List<StatusBarNotification> getNotifications() {
60         return sNotifications;
61     }
62 
63     private final Ranking mTmpRanking = new Ranking();
64 
65     private class Delta {
66         final StatusBarNotification mSbn;
67         final RankingMap mRankingMap;
68 
Delta(StatusBarNotification sbn, RankingMap rankingMap)69         public Delta(StatusBarNotification sbn, RankingMap rankingMap) {
70             mSbn = sbn;
71             mRankingMap = rankingMap;
72         }
73     }
74 
75     private final Comparator<StatusBarNotification> mRankingComparator =
76             new Comparator<StatusBarNotification>() {
77 
78                 private final Ranking mLhsRanking = new Ranking();
79                 private final Ranking mRhsRanking = new Ranking();
80 
81                 @Override
82                 public int compare(StatusBarNotification lhs, StatusBarNotification rhs) {
83                     mRankingMap.getRanking(lhs.getKey(), mLhsRanking);
84                     mRankingMap.getRanking(rhs.getKey(), mRhsRanking);
85                     return Integer.compare(mLhsRanking.getRank(), mRhsRanking.getRank());
86                 }
87             };
88 
89     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
90         @Override
91         public void onReceive(Context context, Intent intent) {
92             String key = intent.getStringExtra(EXTRA_KEY);
93             int what = MSG_DISMISS;
94             if (ACTION_LAUNCH.equals(intent.getAction())) {
95                 what = MSG_LAUNCH;
96             }
97             Log.d(TAG, "received an action broadcast " + intent.getAction());
98             if (!TextUtils.isEmpty(key)) {
99                 Log.d(TAG, "  on " + key);
100                 Message.obtain(mHandler, what, key).sendToTarget();
101             }
102         }
103     };
104 
105     private final Handler mHandler = new Handler() {
106         @Override
107         public void handleMessage(Message msg) {
108             Delta delta = null;
109             if (msg.obj instanceof Delta) {
110                 delta = (Delta) msg.obj;
111             }
112 
113             switch (msg.what) {
114                 case MSG_NOTIFY:
115                     Log.i(TAG, "notify: " + delta.mSbn.getKey());
116                     synchronized (sNotifications) {
117                         boolean exists = mRankingMap.getRanking(delta.mSbn.getKey(), mTmpRanking);
118                         if (!exists) {
119                             sNotifications.add(delta.mSbn);
120                         } else {
121                             int position = mTmpRanking.getRank();
122                             sNotifications.set(position, delta.mSbn);
123                         }
124                         mRankingMap = delta.mRankingMap;
125                         Collections.sort(sNotifications, mRankingComparator);
126                         Log.i(TAG, "finish with: " + sNotifications.size());
127                     }
128                     LocalBroadcastManager.getInstance(Listener.this)
129                             .sendBroadcast(new Intent(ACTION_REFRESH)
130                             .putExtra(EXTRA_KEY, delta.mSbn.getKey()));
131                     break;
132 
133                 case MSG_CANCEL:
134                     Log.i(TAG, "remove: " + delta.mSbn.getKey());
135                     synchronized (sNotifications) {
136                         boolean exists = mRankingMap.getRanking(delta.mSbn.getKey(), mTmpRanking);
137                         if (exists) {
138                             sNotifications.remove(mTmpRanking.getRank());
139                         }
140                         mRankingMap = delta.mRankingMap;
141                         Collections.sort(sNotifications, mRankingComparator);
142                     }
143                     LocalBroadcastManager.getInstance(Listener.this)
144                             .sendBroadcast(new Intent(ACTION_REFRESH));
145                     break;
146 
147                 case MSG_ORDER:
148                     Log.i(TAG, "reorder");
149                     synchronized (sNotifications) {
150                         mRankingMap = delta.mRankingMap;
151                         Collections.sort(sNotifications, mRankingComparator);
152                     }
153                     LocalBroadcastManager.getInstance(Listener.this)
154                             .sendBroadcast(new Intent(ACTION_REFRESH));
155                     break;
156 
157                 case MSG_STARTUP:
158                     fetchActive();
159                     Log.i(TAG, "start with: " + sNotifications.size() + " notifications.");
160                     LocalBroadcastManager.getInstance(Listener.this)
161                             .sendBroadcast(new Intent(ACTION_REFRESH));
162                     break;
163 
164                 case MSG_DISMISS:
165                     if (msg.obj instanceof String) {
166                         final String key = (String) msg.obj;
167                         mRankingMap.getRanking(key, mTmpRanking);
168                         StatusBarNotification sbn = sNotifications.get(mTmpRanking.getRank());
169                         if ((sbn.getNotification().flags & Notification.FLAG_AUTO_CANCEL) != 0 &&
170                                 sbn.getNotification().contentIntent != null) {
171                             try {
172                                 sbn.getNotification().contentIntent.send();
173                             } catch (PendingIntent.CanceledException e) {
174                                 Log.d(TAG, "failed to send intent for " + sbn.getKey(), e);
175                             }
176                         }
177                         cancelNotification(key);
178                     }
179                     break;
180 
181                 case MSG_LAUNCH:
182                     if (msg.obj instanceof String) {
183                         final String key = (String) msg.obj;
184                         mRankingMap.getRanking(key, mTmpRanking);
185                         StatusBarNotification sbn = sNotifications.get(mTmpRanking.getRank());
186                         if (sbn.getNotification().contentIntent != null) {
187                             try {
188                                 sbn.getNotification().contentIntent.send();
189                             } catch (PendingIntent.CanceledException e) {
190                                 Log.d(TAG, "failed to send intent for " + sbn.getKey(), e);
191                             }
192                         }
193                         if ((sbn.getNotification().flags & Notification.FLAG_AUTO_CANCEL) != 0) {
194                             cancelNotification(key);
195                         }
196                     }
197                     break;
198             }
199         }
200     };
201 
202     private RankingMap mRankingMap;
203 
204     @Override
onCreate()205     public void onCreate() {
206         super.onCreate();
207         Log.d(TAG, "registering broadcast listener");
208         final IntentFilter intentFilter = new IntentFilter();
209         intentFilter.addAction(ACTION_DISMISS);
210         intentFilter.addAction(ACTION_LAUNCH);
211         LocalBroadcastManager.getInstance(this).registerReceiver(mBroadcastReceiver, intentFilter);
212     }
213 
214     @Override
onDestroy()215     public void onDestroy() {
216         LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver);
217         super.onDestroy();
218     }
219 
220     @Override
onListenerConnected()221     public void onListenerConnected() {
222         Message.obtain(mHandler, MSG_STARTUP).sendToTarget();
223     }
224 
225     @Override
onNotificationRankingUpdate(RankingMap rankingMap)226     public void onNotificationRankingUpdate(RankingMap rankingMap) {
227         Message.obtain(mHandler, MSG_ORDER,
228                 new Delta(null, rankingMap)).sendToTarget();
229     }
230 
231     @Override
onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap)232     public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
233         Message.obtain(mHandler, MSG_NOTIFY,
234                 new Delta(sbn, rankingMap)).sendToTarget();
235     }
236 
237     @Override
onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap)238     public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
239         Message.obtain(mHandler, MSG_CANCEL,
240                 new Delta(sbn, rankingMap)).sendToTarget();
241     }
242 
fetchActive()243     private void fetchActive() {
244         mRankingMap = getCurrentRanking();
245         sNotifications = new ArrayList<StatusBarNotification>();
246         for (StatusBarNotification sbn : getActiveNotifications()) {
247             sNotifications.add(sbn);
248         }
249         Collections.sort(sNotifications, mRankingComparator);
250     }
251 }
252