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