blob: 73a890fb03085d0c0326e852c61f2559093f1c18 [file] [log] [blame]
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001/*
2 * Copyright (C) 2013 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
17package android.service.notification;
18
Svet Ganovb8f53ee2016-02-18 08:38:56 -080019import android.os.Handler;
20import android.os.Looper;
21import android.os.Message;
22
Julia Reynolds0edb50c2016-02-26 14:08:25 -050023import android.annotation.IntDef;
Jeff Brown5c507c12014-06-05 17:14:39 -070024import android.annotation.SystemApi;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070025import android.annotation.SdkConstant;
26import android.app.INotificationManager;
Christoph Studer4600f9b2014-07-22 22:44:43 +020027import android.app.Notification;
28import android.app.Notification.Builder;
John Spurlock80774932015-05-07 17:38:50 -040029import android.app.NotificationManager;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070030import android.app.Service;
Chris Wren1941fc72014-05-14 15:20:51 -040031import android.content.ComponentName;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070032import android.content.Context;
33import android.content.Intent;
Christoph Studercee44ba2014-05-20 18:36:43 +020034import android.content.pm.ParceledListSlice;
Daniel Sandlerf5a78382015-05-15 23:59:36 -040035import android.graphics.drawable.BitmapDrawable;
36import android.graphics.drawable.Drawable;
37import android.graphics.drawable.Icon;
38import android.graphics.Bitmap;
Julia Reynoldsd9228f12015-10-20 10:37:27 -040039import android.os.Build;
Chris Wren3ad4e3a2014-09-02 17:23:51 -040040import android.os.Bundle;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070041import android.os.IBinder;
Christoph Studer05ad4822014-05-16 14:16:03 +020042import android.os.Parcel;
43import android.os.Parcelable;
Chris Wrenf9536642014-04-17 10:01:54 -040044import android.os.RemoteException;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070045import android.os.ServiceManager;
Christoph Studerdda48f12014-07-29 23:13:16 +020046import android.util.ArrayMap;
47import android.util.ArraySet;
Daniel Sandler5feceeb2013-03-22 18:29:23 -070048import android.util.Log;
Adrian Roos5081c0d2016-02-26 16:04:19 -080049import android.widget.RemoteViews;
Svet Ganovb8f53ee2016-02-18 08:38:56 -080050import com.android.internal.annotations.GuardedBy;
51import com.android.internal.os.SomeArgs;
Julia Reynolds0edb50c2016-02-26 14:08:25 -050052import java.lang.annotation.Retention;
53import java.lang.annotation.RetentionPolicy;
Chris Wren24fb8942015-06-18 14:33:56 -040054import java.util.ArrayList;
Christoph Studerdda48f12014-07-29 23:13:16 +020055import java.util.Collections;
Christoph Studercee44ba2014-05-20 18:36:43 +020056import java.util.List;
57
Scott Main04667da2013-04-25 16:57:16 -070058/**
Christoph Studer05ad4822014-05-16 14:16:03 +020059 * A service that receives calls from the system when new notifications are
60 * posted or removed, or their ranking changed.
Scott Main04667da2013-04-25 16:57:16 -070061 * <p>To extend this class, you must declare the service in your manifest file with
62 * the {@link android.Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE} permission
63 * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
64 * <pre>
65 * &lt;service android:name=".NotificationListener"
66 * android:label="&#64;string/service_name"
67 * android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
68 * &lt;intent-filter>
69 * &lt;action android:name="android.service.notification.NotificationListenerService" />
70 * &lt;/intent-filter>
71 * &lt;/service></pre>
Ruben Brunke5d76e82016-02-29 19:50:15 +000072 * <p> Typically, while enabled in user settings, this service will be bound on boot or when a
73 * settings change occurs that could affect whether this service should run. However, for some
74 * system usage modes, the you may instead specify that this service is instead bound and unbound
75 * in response to mode changes by including a category in the intent filter. Currently
76 * supported categories are:
77 * <ul>
78 * <li>{@link #CATEGORY_VR_NOTIFICATIONS} - this service is bound when an Activity has enabled
79 * VR mode. {@see android.app.Activity#setVrMode(boolean)}.</li>
80 * </ul>
81 * </p>
Scott Main04667da2013-04-25 16:57:16 -070082 */
Daniel Sandler5feceeb2013-03-22 18:29:23 -070083public abstract class NotificationListenerService extends Service {
84 // TAG = "NotificationListenerService[MySubclass]"
85 private final String TAG = NotificationListenerService.class.getSimpleName()
86 + "[" + getClass().getSimpleName() + "]";
87
Christoph Studer85a384b2014-08-27 20:16:15 +020088 /**
89 * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
90 * Normal interruption filter.
91 */
John Spurlock80774932015-05-07 17:38:50 -040092 public static final int INTERRUPTION_FILTER_ALL
93 = NotificationManager.INTERRUPTION_FILTER_ALL;
John Spurlockd8afe3c2014-08-01 14:04:07 -040094
Christoph Studer85a384b2014-08-27 20:16:15 +020095 /**
96 * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
97 * Priority interruption filter.
98 */
John Spurlock80774932015-05-07 17:38:50 -040099 public static final int INTERRUPTION_FILTER_PRIORITY
100 = NotificationManager.INTERRUPTION_FILTER_PRIORITY;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400101
Christoph Studer85a384b2014-08-27 20:16:15 +0200102 /**
103 * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
104 * No interruptions filter.
105 */
John Spurlock80774932015-05-07 17:38:50 -0400106 public static final int INTERRUPTION_FILTER_NONE
107 = NotificationManager.INTERRUPTION_FILTER_NONE;
John Spurlockd8afe3c2014-08-01 14:04:07 -0400108
John Spurlock4f1163c2015-04-02 17:41:21 -0400109 /**
110 * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
111 * Alarms only interruption filter.
112 */
John Spurlock80774932015-05-07 17:38:50 -0400113 public static final int INTERRUPTION_FILTER_ALARMS
114 = NotificationManager.INTERRUPTION_FILTER_ALARMS;
John Spurlock4f1163c2015-04-02 17:41:21 -0400115
John Spurlock83104102015-02-12 23:25:12 -0500116 /** {@link #getCurrentInterruptionFilter() Interruption filter} constant - returned when
117 * the value is unavailable for any reason. For example, before the notification listener
118 * is connected.
119 *
120 * {@see #onListenerConnected()}
121 */
John Spurlock80774932015-05-07 17:38:50 -0400122 public static final int INTERRUPTION_FILTER_UNKNOWN
123 = NotificationManager.INTERRUPTION_FILTER_UNKNOWN;
John Spurlock83104102015-02-12 23:25:12 -0500124
John Spurlockd8afe3c2014-08-01 14:04:07 -0400125 /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI
126 * should disable notification sound, vibrating and other visual or aural effects.
Christoph Studer85a384b2014-08-27 20:16:15 +0200127 * This does not change the interruption filter, only the effects. **/
128 public static final int HINT_HOST_DISABLE_EFFECTS = 1;
John Spurlock1fa865f2014-07-21 14:56:39 -0400129
Julia Reynoldsd5607292016-02-05 15:25:58 -0500130 /**
131 * Whether notification suppressed by DND should not interruption visually when the screen is
132 * off.
133 */
134 public static final int SUPPRESSED_EFFECT_SCREEN_OFF =
135 NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
136 /**
137 * Whether notification suppressed by DND should not interruption visually when the screen is
138 * on.
139 */
Julia Reynolds61721582016-01-05 08:35:25 -0500140 public static final int SUPPRESSED_EFFECT_SCREEN_ON =
141 NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500142
Christoph Studerb82bc782014-08-20 14:29:43 +0200143 /**
144 * The full trim of the StatusBarNotification including all its features.
145 *
146 * @hide
147 */
148 @SystemApi
149 public static final int TRIM_FULL = 0;
150
151 /**
152 * A light trim of the StatusBarNotification excluding the following features:
153 *
154 * <ol>
155 * <li>{@link Notification#tickerView tickerView}</li>
156 * <li>{@link Notification#contentView contentView}</li>
157 * <li>{@link Notification#largeIcon largeIcon}</li>
158 * <li>{@link Notification#bigContentView bigContentView}</li>
159 * <li>{@link Notification#headsUpContentView headsUpContentView}</li>
160 * <li>{@link Notification#EXTRA_LARGE_ICON extras[EXTRA_LARGE_ICON]}</li>
161 * <li>{@link Notification#EXTRA_LARGE_ICON_BIG extras[EXTRA_LARGE_ICON_BIG]}</li>
162 * <li>{@link Notification#EXTRA_PICTURE extras[EXTRA_PICTURE]}</li>
Christoph Studer223f44e2014-09-02 14:59:32 +0200163 * <li>{@link Notification#EXTRA_BIG_TEXT extras[EXTRA_BIG_TEXT]}</li>
Christoph Studerb82bc782014-08-20 14:29:43 +0200164 * </ol>
165 *
166 * @hide
167 */
168 @SystemApi
169 public static final int TRIM_LIGHT = 1;
170
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800171 private final Object mLock = new Object();
172
173 private Handler mHandler;
174
Chris Wren51017d02015-12-15 15:34:46 -0500175 /** @hide */
176 protected NotificationListenerWrapper mWrapper = null;
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800177
178 @GuardedBy("mLock")
Christoph Studerd0694b62014-06-04 16:36:01 +0200179 private RankingMap mRankingMap;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700180
181 private INotificationManager mNoMan;
182
Chris Wren1941fc72014-05-14 15:20:51 -0400183 /** Only valid after a successful call to (@link registerAsService}. */
184 private int mCurrentUser;
185
Christoph Studer4600f9b2014-07-22 22:44:43 +0200186
187 // This context is required for system services since NotificationListenerService isn't
188 // started as a real Service and hence no context is available.
189 private Context mSystemContext;
190
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700191 /**
192 * The {@link Intent} that must be declared as handled by the service.
193 */
194 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
195 public static final String SERVICE_INTERFACE
196 = "android.service.notification.NotificationListenerService";
197
198 /**
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800199 * If this category is declared in the application manifest for a service of this type, this
200 * service will be bound when VR mode is enabled, and unbound when VR mode is disabled rather
201 * than the normal lifecycle for a notification service.
202 *
203 * {@see android.app.Activity#setVrMode(boolean)}
204 */
205 @SdkConstant(SdkConstant.SdkConstantType.INTENT_CATEGORY)
206 public static final String CATEGORY_VR_NOTIFICATIONS =
207 "android.intent.category.vr.notifications";
208
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800209 @Override
210 protected void attachBaseContext(Context base) {
211 super.attachBaseContext(base);
212 mHandler = new MyHandler(getMainLooper());
213 }
214
Ruben Brunkdd18a0b2015-12-04 16:16:31 -0800215 /**
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700216 * Implement this method to learn about new notifications as they are posted by apps.
217 *
218 * @param sbn A data structure encapsulating the original {@link android.app.Notification}
219 * object as well as its identifying information (tag and id) and source
220 * (package name).
221 */
Christoph Studerd0694b62014-06-04 16:36:01 +0200222 public void onNotificationPosted(StatusBarNotification sbn) {
223 // optional
224 }
225
226 /**
227 * Implement this method to learn about new notifications as they are posted by apps.
228 *
229 * @param sbn A data structure encapsulating the original {@link android.app.Notification}
230 * object as well as its identifying information (tag and id) and source
231 * (package name).
232 * @param rankingMap The current ranking map that can be used to retrieve ranking information
233 * for active notifications, including the newly posted one.
234 */
235 public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
236 onNotificationPosted(sbn);
237 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700238
239 /**
240 * Implement this method to learn when notifications are removed.
241 * <P>
242 * This might occur because the user has dismissed the notification using system UI (or another
243 * notification listener) or because the app has withdrawn the notification.
Daniel Sandler1a497d32013-04-18 14:52:45 -0400244 * <P>
245 * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
Scott Main04667da2013-04-25 16:57:16 -0700246 * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
Daniel Sandler1a497d32013-04-18 14:52:45 -0400247 * fields such as {@link android.app.Notification#contentView} and
248 * {@link android.app.Notification#largeIcon}. However, all other fields on
249 * {@link StatusBarNotification}, sufficient to match this call with a prior call to
250 * {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700251 *
Daniel Sandler1a497d32013-04-18 14:52:45 -0400252 * @param sbn A data structure encapsulating at least the original information (tag and id)
253 * and source (package name) used to post the {@link android.app.Notification} that
254 * was just removed.
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700255 */
Christoph Studerd0694b62014-06-04 16:36:01 +0200256 public void onNotificationRemoved(StatusBarNotification sbn) {
257 // optional
258 }
259
260 /**
261 * Implement this method to learn when notifications are removed.
262 * <P>
263 * This might occur because the user has dismissed the notification using system UI (or another
264 * notification listener) or because the app has withdrawn the notification.
265 * <P>
266 * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
267 * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
268 * fields such as {@link android.app.Notification#contentView} and
269 * {@link android.app.Notification#largeIcon}. However, all other fields on
270 * {@link StatusBarNotification}, sufficient to match this call with a prior call to
271 * {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
272 *
273 * @param sbn A data structure encapsulating at least the original information (tag and id)
274 * and source (package name) used to post the {@link android.app.Notification} that
275 * was just removed.
276 * @param rankingMap The current ranking map that can be used to retrieve ranking information
277 * for active notifications.
278 *
279 */
280 public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
281 onNotificationRemoved(sbn);
282 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700283
John Spurlocka4294292014-03-24 18:02:32 -0400284 /**
285 * Implement this method to learn about when the listener is enabled and connected to
Christoph Studercee44ba2014-05-20 18:36:43 +0200286 * the notification manager. You are safe to call {@link #getActiveNotifications()}
John Spurlocka4294292014-03-24 18:02:32 -0400287 * at this time.
John Spurlocka4294292014-03-24 18:02:32 -0400288 */
Christoph Studercee44ba2014-05-20 18:36:43 +0200289 public void onListenerConnected() {
John Spurlocka4294292014-03-24 18:02:32 -0400290 // optional
291 }
292
Chris Wrenf9536642014-04-17 10:01:54 -0400293 /**
Christoph Studer05ad4822014-05-16 14:16:03 +0200294 * Implement this method to be notified when the notification ranking changes.
Christoph Studerd0694b62014-06-04 16:36:01 +0200295 *
296 * @param rankingMap The current ranking map that can be used to retrieve ranking information
297 * for active notifications.
Chris Wrenf9536642014-04-17 10:01:54 -0400298 */
Christoph Studerd0694b62014-06-04 16:36:01 +0200299 public void onNotificationRankingUpdate(RankingMap rankingMap) {
Chris Wrenf9536642014-04-17 10:01:54 -0400300 // optional
301 }
302
John Spurlock1fa865f2014-07-21 14:56:39 -0400303 /**
304 * Implement this method to be notified when the
John Spurlockd8afe3c2014-08-01 14:04:07 -0400305 * {@link #getCurrentListenerHints() Listener hints} change.
John Spurlock1fa865f2014-07-21 14:56:39 -0400306 *
John Spurlockd8afe3c2014-08-01 14:04:07 -0400307 * @param hints The current {@link #getCurrentListenerHints() listener hints}.
John Spurlock1fa865f2014-07-21 14:56:39 -0400308 */
John Spurlockd8afe3c2014-08-01 14:04:07 -0400309 public void onListenerHintsChanged(int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -0400310 // optional
311 }
312
Christoph Studer85a384b2014-08-27 20:16:15 +0200313 /**
314 * Implement this method to be notified when the
315 * {@link #getCurrentInterruptionFilter() interruption filter} changed.
316 *
317 * @param interruptionFilter The current
318 * {@link #getCurrentInterruptionFilter() interruption filter}.
319 */
320 public void onInterruptionFilterChanged(int interruptionFilter) {
321 // optional
322 }
323
Chris Wren51017d02015-12-15 15:34:46 -0500324 /** @hide */
325 protected final INotificationManager getNotificationInterface() {
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700326 if (mNoMan == null) {
327 mNoMan = INotificationManager.Stub.asInterface(
328 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
329 }
330 return mNoMan;
331 }
332
333 /**
334 * Inform the notification manager about dismissal of a single notification.
335 * <p>
336 * Use this if your listener has a user interface that allows the user to dismiss individual
337 * notifications, similar to the behavior of Android's status bar and notification panel.
338 * It should be called after the user dismisses a single notification using your UI;
339 * upon being informed, the notification manager will actually remove the notification
340 * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback.
341 * <P>
342 * <b>Note:</b> If your listener allows the user to fire a notification's
343 * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call
344 * this method at that time <i>if</i> the Notification in question has the
345 * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set.
346 *
347 * @param pkg Package of the notifying app.
348 * @param tag Tag of the notification as specified by the notifying app in
349 * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
350 * @param id ID of the notification as specified by the notifying app in
351 * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
Kenny Guya263e4e2014-03-03 18:24:03 +0000352 * <p>
353 * @deprecated Use {@link #cancelNotification(String key)}
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700354 * instead. Beginning with {@link android.os.Build.VERSION_CODES#LOLLIPOP} this method will no longer
Kenny Guya263e4e2014-03-03 18:24:03 +0000355 * cancel the notification. It will continue to cancel the notification for applications
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700356 * whose {@code targetSdkVersion} is earlier than {@link android.os.Build.VERSION_CODES#LOLLIPOP}.
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700357 */
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400358 public final void cancelNotification(String pkg, String tag, int id) {
John Spurlockda9a3be2014-02-12 12:12:26 -0500359 if (!isBound()) return;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700360 try {
Kenny Guya263e4e2014-03-03 18:24:03 +0000361 getNotificationInterface().cancelNotificationFromListener(
362 mWrapper, pkg, tag, id);
363 } catch (android.os.RemoteException ex) {
364 Log.v(TAG, "Unable to contact notification manager", ex);
365 }
366 }
367
368 /**
369 * Inform the notification manager about dismissal of a single notification.
370 * <p>
371 * Use this if your listener has a user interface that allows the user to dismiss individual
372 * notifications, similar to the behavior of Android's status bar and notification panel.
373 * It should be called after the user dismisses a single notification using your UI;
374 * upon being informed, the notification manager will actually remove the notification
375 * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback.
376 * <P>
377 * <b>Note:</b> If your listener allows the user to fire a notification's
378 * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call
379 * this method at that time <i>if</i> the Notification in question has the
380 * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set.
381 * <p>
382 * @param key Notification to dismiss from {@link StatusBarNotification#getKey()}.
383 */
384 public final void cancelNotification(String key) {
385 if (!isBound()) return;
386 try {
387 getNotificationInterface().cancelNotificationsFromListener(mWrapper,
Daniel Sandlerf5a78382015-05-15 23:59:36 -0400388 new String[] { key });
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700389 } catch (android.os.RemoteException ex) {
390 Log.v(TAG, "Unable to contact notification manager", ex);
391 }
392 }
393
394 /**
395 * Inform the notification manager about dismissal of all notifications.
396 * <p>
397 * Use this if your listener has a user interface that allows the user to dismiss all
398 * notifications, similar to the behavior of Android's status bar and notification panel.
399 * It should be called after the user invokes the "dismiss all" function of your UI;
400 * upon being informed, the notification manager will actually remove all active notifications
401 * and you will get multiple {@link #onNotificationRemoved(StatusBarNotification)} callbacks.
402 *
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400403 * {@see #cancelNotification(String, String, int)}
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700404 */
Daniel Sandlere6f7f2e2013-04-25 15:44:16 -0400405 public final void cancelAllNotifications() {
John Spurlocka4294292014-03-24 18:02:32 -0400406 cancelNotifications(null /*all*/);
407 }
408
409 /**
410 * Inform the notification manager about dismissal of specific notifications.
411 * <p>
412 * Use this if your listener has a user interface that allows the user to dismiss
413 * multiple notifications at once.
414 *
415 * @param keys Notifications to dismiss, or {@code null} to dismiss all.
416 *
417 * {@see #cancelNotification(String, String, int)}
418 */
419 public final void cancelNotifications(String[] keys) {
John Spurlockda9a3be2014-02-12 12:12:26 -0500420 if (!isBound()) return;
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700421 try {
John Spurlocka4294292014-03-24 18:02:32 -0400422 getNotificationInterface().cancelNotificationsFromListener(mWrapper, keys);
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700423 } catch (android.os.RemoteException ex) {
424 Log.v(TAG, "Unable to contact notification manager", ex);
425 }
426 }
427
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400428 /**
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700429 * Inform the notification manager that these notifications have been viewed by the
Amith Yamasanic6ecbce2015-06-23 12:58:43 -0700430 * user. This should only be called when there is sufficient confidence that the user is
431 * looking at the notifications, such as when the notifications appear on the screen due to
432 * an explicit user interaction.
Amith Yamasanif47e51e2015-04-17 10:02:15 -0700433 * @param keys Notifications to mark as seen.
434 */
435 public final void setNotificationsShown(String[] keys) {
436 if (!isBound()) return;
437 try {
438 getNotificationInterface().setNotificationsShownFromListener(mWrapper, keys);
439 } catch (android.os.RemoteException ex) {
440 Log.v(TAG, "Unable to contact notification manager", ex);
441 }
442 }
443
444 /**
Christoph Studerb82bc782014-08-20 14:29:43 +0200445 * Sets the notification trim that will be received via {@link #onNotificationPosted}.
446 *
447 * <p>
448 * Setting a trim other than {@link #TRIM_FULL} enables listeners that don't need access to the
449 * full notification features right away to reduce their memory footprint. Full notifications
450 * can be requested on-demand via {@link #getActiveNotifications(int)}.
451 *
452 * <p>
453 * Set to {@link #TRIM_FULL} initially.
454 *
455 * @hide
456 *
457 * @param trim trim of the notifications to be passed via {@link #onNotificationPosted}.
458 * See <code>TRIM_*</code> constants.
459 */
460 @SystemApi
461 public final void setOnNotificationPostedTrim(int trim) {
462 if (!isBound()) return;
463 try {
464 getNotificationInterface().setOnNotificationPostedTrimFromListener(mWrapper, trim);
465 } catch (RemoteException ex) {
466 Log.v(TAG, "Unable to contact notification manager", ex);
467 }
468 }
469
470 /**
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400471 * Request the list of outstanding notifications (that is, those that are visible to the
John Spurlocka4294292014-03-24 18:02:32 -0400472 * current user). Useful when you don't know what's already been posted.
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400473 *
Chris Wrenf9536642014-04-17 10:01:54 -0400474 * @return An array of active notifications, sorted in natural order.
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400475 */
476 public StatusBarNotification[] getActiveNotifications() {
Christoph Studerb82bc782014-08-20 14:29:43 +0200477 return getActiveNotifications(null, TRIM_FULL);
478 }
479
480 /**
481 * Request the list of outstanding notifications (that is, those that are visible to the
482 * current user). Useful when you don't know what's already been posted.
483 *
484 * @hide
485 *
486 * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
487 * @return An array of active notifications, sorted in natural order.
488 */
489 @SystemApi
490 public StatusBarNotification[] getActiveNotifications(int trim) {
491 return getActiveNotifications(null, trim);
Dan Sandlerea75fdd2014-08-12 12:29:19 -0400492 }
493
494 /**
495 * Request one or more notifications by key. Useful if you have been keeping track of
496 * notifications but didn't want to retain the bits, and now need to go back and extract
497 * more data out of those notifications.
498 *
Christoph Studerb82bc782014-08-20 14:29:43 +0200499 * @param keys the keys of the notifications to request
Dan Sandlerea75fdd2014-08-12 12:29:19 -0400500 * @return An array of notifications corresponding to the requested keys, in the
501 * same order as the key list.
502 */
503 public StatusBarNotification[] getActiveNotifications(String[] keys) {
Christoph Studerb82bc782014-08-20 14:29:43 +0200504 return getActiveNotifications(keys, TRIM_FULL);
505 }
506
507 /**
508 * Request one or more notifications by key. Useful if you have been keeping track of
509 * notifications but didn't want to retain the bits, and now need to go back and extract
510 * more data out of those notifications.
511 *
512 * @hide
513 *
514 * @param keys the keys of the notifications to request
515 * @param trim trim of the notifications to be returned. See <code>TRIM_*</code> constants.
516 * @return An array of notifications corresponding to the requested keys, in the
517 * same order as the key list.
518 */
519 @SystemApi
520 public StatusBarNotification[] getActiveNotifications(String[] keys, int trim) {
521 if (!isBound())
522 return null;
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400523 try {
Christoph Studerb82bc782014-08-20 14:29:43 +0200524 ParceledListSlice<StatusBarNotification> parceledList = getNotificationInterface()
525 .getActiveNotificationsFromListener(mWrapper, keys, trim);
Christoph Studercee44ba2014-05-20 18:36:43 +0200526 List<StatusBarNotification> list = parceledList.getList();
Chris Wren24fb8942015-06-18 14:33:56 -0400527 ArrayList<StatusBarNotification> corruptNotifications = null;
Christoph Studer4600f9b2014-07-22 22:44:43 +0200528 int N = list.size();
529 for (int i = 0; i < N; i++) {
Chris Wren24fb8942015-06-18 14:33:56 -0400530 StatusBarNotification sbn = list.get(i);
531 Notification notification = sbn.getNotification();
532 try {
Chris Wren24fb8942015-06-18 14:33:56 -0400533 // convert icon metadata to legacy format for older clients
534 createLegacyIconExtras(notification);
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400535 // populate remote views for older clients.
536 maybePopulateRemoteViews(notification);
Chris Wren24fb8942015-06-18 14:33:56 -0400537 } catch (IllegalArgumentException e) {
538 if (corruptNotifications == null) {
539 corruptNotifications = new ArrayList<>(N);
540 }
541 corruptNotifications.add(sbn);
542 Log.w(TAG, "onNotificationPosted: can't rebuild notification from " +
543 sbn.getPackageName());
544 }
Christoph Studer4600f9b2014-07-22 22:44:43 +0200545 }
Chris Wren24fb8942015-06-18 14:33:56 -0400546 if (corruptNotifications != null) {
547 list.removeAll(corruptNotifications);
548 }
549 return list.toArray(new StatusBarNotification[list.size()]);
John Spurlocka4294292014-03-24 18:02:32 -0400550 } catch (android.os.RemoteException ex) {
551 Log.v(TAG, "Unable to contact notification manager", ex);
552 }
553 return null;
554 }
555
556 /**
John Spurlockd8afe3c2014-08-01 14:04:07 -0400557 * Gets the set of hints representing current state.
John Spurlock1fa865f2014-07-21 14:56:39 -0400558 *
559 * <p>
John Spurlockd8afe3c2014-08-01 14:04:07 -0400560 * The current state may differ from the requested state if the hint represents state
John Spurlock1fa865f2014-07-21 14:56:39 -0400561 * shared across all listeners or a feature the notification host does not support or refuses
562 * to grant.
563 *
Christoph Studer85a384b2014-08-27 20:16:15 +0200564 * @return Zero or more of the HINT_ constants.
John Spurlock1fa865f2014-07-21 14:56:39 -0400565 */
John Spurlockd8afe3c2014-08-01 14:04:07 -0400566 public final int getCurrentListenerHints() {
Christoph Studer85a384b2014-08-27 20:16:15 +0200567 if (!isBound()) return 0;
John Spurlock1fa865f2014-07-21 14:56:39 -0400568 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400569 return getNotificationInterface().getHintsFromListener(mWrapper);
John Spurlock1fa865f2014-07-21 14:56:39 -0400570 } catch (android.os.RemoteException ex) {
571 Log.v(TAG, "Unable to contact notification manager", ex);
Christoph Studer85a384b2014-08-27 20:16:15 +0200572 return 0;
573 }
574 }
575
576 /**
577 * Gets the current notification interruption filter active on the host.
578 *
579 * <p>
580 * The interruption filter defines which notifications are allowed to interrupt the user
581 * (e.g. via sound &amp; vibration) and is applied globally. Listeners can find out whether
582 * a specific notification matched the interruption filter via
583 * {@link Ranking#matchesInterruptionFilter()}.
584 * <p>
585 * The current filter may differ from the previously requested filter if the notification host
586 * does not support or refuses to apply the requested filter, or if another component changed
587 * the filter in the meantime.
588 * <p>
589 * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
590 *
John Spurlock83104102015-02-12 23:25:12 -0500591 * @return One of the INTERRUPTION_FILTER_ constants, or INTERRUPTION_FILTER_UNKNOWN when
592 * unavailable.
Christoph Studer85a384b2014-08-27 20:16:15 +0200593 */
594 public final int getCurrentInterruptionFilter() {
John Spurlock83104102015-02-12 23:25:12 -0500595 if (!isBound()) return INTERRUPTION_FILTER_UNKNOWN;
Christoph Studer85a384b2014-08-27 20:16:15 +0200596 try {
Chris Wren957ed702014-09-24 18:17:36 -0400597 return getNotificationInterface().getInterruptionFilterFromListener(mWrapper);
Christoph Studer85a384b2014-08-27 20:16:15 +0200598 } catch (android.os.RemoteException ex) {
599 Log.v(TAG, "Unable to contact notification manager", ex);
John Spurlock83104102015-02-12 23:25:12 -0500600 return INTERRUPTION_FILTER_UNKNOWN;
John Spurlock1fa865f2014-07-21 14:56:39 -0400601 }
602 }
603
604 /**
John Spurlockd8afe3c2014-08-01 14:04:07 -0400605 * Sets the desired {@link #getCurrentListenerHints() listener hints}.
John Spurlock1fa865f2014-07-21 14:56:39 -0400606 *
607 * <p>
Christoph Studer85a384b2014-08-27 20:16:15 +0200608 * This is merely a request, the host may or may not choose to take action depending
John Spurlock1fa865f2014-07-21 14:56:39 -0400609 * on other listener requests or other global state.
610 * <p>
John Spurlockd8afe3c2014-08-01 14:04:07 -0400611 * Listen for updates using {@link #onListenerHintsChanged(int)}.
John Spurlock1fa865f2014-07-21 14:56:39 -0400612 *
John Spurlockd8afe3c2014-08-01 14:04:07 -0400613 * @param hints One or more of the HINT_ constants.
John Spurlock1fa865f2014-07-21 14:56:39 -0400614 */
John Spurlockd8afe3c2014-08-01 14:04:07 -0400615 public final void requestListenerHints(int hints) {
John Spurlock1fa865f2014-07-21 14:56:39 -0400616 if (!isBound()) return;
617 try {
John Spurlockd8afe3c2014-08-01 14:04:07 -0400618 getNotificationInterface().requestHintsFromListener(mWrapper, hints);
John Spurlock1fa865f2014-07-21 14:56:39 -0400619 } catch (android.os.RemoteException ex) {
620 Log.v(TAG, "Unable to contact notification manager", ex);
621 }
622 }
623
624 /**
Christoph Studer85a384b2014-08-27 20:16:15 +0200625 * Sets the desired {@link #getCurrentInterruptionFilter() interruption filter}.
626 *
627 * <p>
628 * This is merely a request, the host may or may not choose to apply the requested
629 * interruption filter depending on other listener requests or other global state.
630 * <p>
631 * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
632 *
633 * @param interruptionFilter One of the INTERRUPTION_FILTER_ constants.
634 */
635 public final void requestInterruptionFilter(int interruptionFilter) {
636 if (!isBound()) return;
637 try {
638 getNotificationInterface()
639 .requestInterruptionFilterFromListener(mWrapper, interruptionFilter);
640 } catch (android.os.RemoteException ex) {
641 Log.v(TAG, "Unable to contact notification manager", ex);
642 }
643 }
644
645 /**
Christoph Studer05ad4822014-05-16 14:16:03 +0200646 * Returns current ranking information.
John Spurlocka4294292014-03-24 18:02:32 -0400647 *
Christoph Studer05ad4822014-05-16 14:16:03 +0200648 * <p>
649 * The returned object represents the current ranking snapshot and only
Christoph Studerd0694b62014-06-04 16:36:01 +0200650 * applies for currently active notifications.
651 * <p>
652 * Generally you should use the RankingMap that is passed with events such
653 * as {@link #onNotificationPosted(StatusBarNotification, RankingMap)},
654 * {@link #onNotificationRemoved(StatusBarNotification, RankingMap)}, and
655 * so on. This method should only be used when needing access outside of
656 * such events, for example to retrieve the RankingMap right after
657 * initialization.
Christoph Studer05ad4822014-05-16 14:16:03 +0200658 *
Christoph Studerd0694b62014-06-04 16:36:01 +0200659 * @return A {@link RankingMap} object providing access to ranking information
John Spurlocka4294292014-03-24 18:02:32 -0400660 */
Christoph Studerd0694b62014-06-04 16:36:01 +0200661 public RankingMap getCurrentRanking() {
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800662 synchronized (mLock) {
663 return mRankingMap;
664 }
Daniel Sandler25cf8ce2013-04-24 15:34:57 -0400665 }
666
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700667 @Override
668 public IBinder onBind(Intent intent) {
669 if (mWrapper == null) {
Chris Wren51017d02015-12-15 15:34:46 -0500670 mWrapper = new NotificationListenerWrapper();
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700671 }
672 return mWrapper;
673 }
674
Chris Wren51017d02015-12-15 15:34:46 -0500675 /** @hide */
676 protected boolean isBound() {
John Spurlockda9a3be2014-02-12 12:12:26 -0500677 if (mWrapper == null) {
678 Log.w(TAG, "Notification listener service not yet bound.");
679 return false;
680 }
681 return true;
682 }
683
Chris Wren1941fc72014-05-14 15:20:51 -0400684 /**
685 * Directly register this service with the Notification Manager.
686 *
687 * <p>Only system services may use this call. It will fail for non-system callers.
688 * Apps should ask the user to add their listener in Settings.
689 *
Christoph Studer4600f9b2014-07-22 22:44:43 +0200690 * @param context Context required for accessing resources. Since this service isn't
691 * launched as a real Service when using this method, a context has to be passed in.
Chris Wren1941fc72014-05-14 15:20:51 -0400692 * @param componentName the component that will consume the notification information
693 * @param currentUser the user to use as the stream filter
694 * @hide
695 */
Jeff Brown5c507c12014-06-05 17:14:39 -0700696 @SystemApi
Christoph Studer4600f9b2014-07-22 22:44:43 +0200697 public void registerAsSystemService(Context context, ComponentName componentName,
698 int currentUser) throws RemoteException {
699 mSystemContext = context;
Chris Wren1941fc72014-05-14 15:20:51 -0400700 if (mWrapper == null) {
Chris Wren51017d02015-12-15 15:34:46 -0500701 mWrapper = new NotificationListenerWrapper();
Chris Wren1941fc72014-05-14 15:20:51 -0400702 }
703 INotificationManager noMan = getNotificationInterface();
704 noMan.registerListener(mWrapper, componentName, currentUser);
705 mCurrentUser = currentUser;
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800706 mHandler = new MyHandler(context.getMainLooper());
Chris Wren1941fc72014-05-14 15:20:51 -0400707 }
708
709 /**
710 * Directly unregister this service from the Notification Manager.
711 *
712 * <P>This method will fail for listeners that were not registered
713 * with (@link registerAsService).
714 * @hide
715 */
Jeff Brown5c507c12014-06-05 17:14:39 -0700716 @SystemApi
Chris Wren1941fc72014-05-14 15:20:51 -0400717 public void unregisterAsSystemService() throws RemoteException {
718 if (mWrapper != null) {
719 INotificationManager noMan = getNotificationInterface();
720 noMan.unregisterListener(mWrapper, mCurrentUser);
721 }
722 }
723
Chris Wrenab41eec2016-01-04 18:01:27 -0500724 /**
725 * Request that the listener be rebound, after a previous call to (@link requestUnbind).
726 *
Chris Wren10c63d82016-02-05 14:55:35 -0500727 * <P>This method will fail for listeners that have
Chris Wrenab41eec2016-01-04 18:01:27 -0500728 * not been granted the permission by the user.
729 *
730 * <P>The service should wait for the {@link #onListenerConnected()} event
731 * before performing any operations.
732 */
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800733 public static void requestRebind(ComponentName componentName)
Chris Wrenab41eec2016-01-04 18:01:27 -0500734 throws RemoteException {
735 INotificationManager noMan = INotificationManager.Stub.asInterface(
736 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
737 noMan.requestBindListener(componentName);
738 }
739
740 /**
741 * Request that the service be unbound.
742 *
743 * <P>This will no longer receive updates until
744 * {@link #requestRebind(ComponentName)} is called.
745 * The service will likely be kiled by the system after this call.
746 */
747 public final void requestUnbind() throws RemoteException {
748 if (mWrapper != null) {
749 INotificationManager noMan = getNotificationInterface();
750 noMan.requestUnbindListener(mWrapper);
751 }
752 }
753
Daniel Sandlerf5a78382015-05-15 23:59:36 -0400754 /** Convert new-style Icons to legacy representations for pre-M clients. */
755 private void createLegacyIconExtras(Notification n) {
756 Icon smallIcon = n.getSmallIcon();
757 Icon largeIcon = n.getLargeIcon();
Dan Sandler99a37f12015-06-09 14:34:38 -0400758 if (smallIcon != null && smallIcon.getType() == Icon.TYPE_RESOURCE) {
Daniel Sandlerf5a78382015-05-15 23:59:36 -0400759 n.extras.putInt(Notification.EXTRA_SMALL_ICON, smallIcon.getResId());
760 n.icon = smallIcon.getResId();
761 }
762 if (largeIcon != null) {
763 Drawable d = largeIcon.loadDrawable(getContext());
764 if (d != null && d instanceof BitmapDrawable) {
765 final Bitmap largeIconBits = ((BitmapDrawable) d).getBitmap();
766 n.extras.putParcelable(Notification.EXTRA_LARGE_ICON, largeIconBits);
767 n.largeIcon = largeIconBits;
768 }
769 }
770 }
771
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400772 /**
773 * Populates remote views for pre-N targeting apps.
774 */
775 private void maybePopulateRemoteViews(Notification notification) {
776 if (getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
777 Builder builder = Builder.recoverBuilder(getContext(), notification);
Adrian Roos5081c0d2016-02-26 16:04:19 -0800778
779 // Some styles wrap Notification's contentView, bigContentView and headsUpContentView.
780 // First inflate them all, only then set them to avoid recursive wrapping.
781 RemoteViews content = builder.createContentView();
782 RemoteViews big = builder.createBigContentView();
783 RemoteViews headsUp = builder.createHeadsUpContentView();
784
785 notification.contentView = content;
786 notification.bigContentView = big;
787 notification.headsUpContentView = headsUp;
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400788 }
789 }
790
Chris Wren51017d02015-12-15 15:34:46 -0500791 /** @hide */
792 protected class NotificationListenerWrapper extends INotificationListener.Stub {
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700793 @Override
Griff Hazen84a00ea2014-09-02 17:10:47 -0700794 public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
Christoph Studer05ad4822014-05-16 14:16:03 +0200795 NotificationRankingUpdate update) {
Griff Hazen84a00ea2014-09-02 17:10:47 -0700796 StatusBarNotification sbn;
797 try {
798 sbn = sbnHolder.get();
799 } catch (RemoteException e) {
800 Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification", e);
801 return;
802 }
Christoph Studer4600f9b2014-07-22 22:44:43 +0200803
Chris Wren24fb8942015-06-18 14:33:56 -0400804 try {
Chris Wren24fb8942015-06-18 14:33:56 -0400805 // convert icon metadata to legacy format for older clients
806 createLegacyIconExtras(sbn.getNotification());
Julia Reynoldsd9228f12015-10-20 10:37:27 -0400807 maybePopulateRemoteViews(sbn.getNotification());
Chris Wren24fb8942015-06-18 14:33:56 -0400808 } catch (IllegalArgumentException e) {
Andreas Gampe1ed71f32015-12-11 15:49:07 -0800809 // warn and drop corrupt notification
Chris Wren24fb8942015-06-18 14:33:56 -0400810 Log.w(TAG, "onNotificationPosted: can't rebuild notification from " +
811 sbn.getPackageName());
Andreas Gampe1ed71f32015-12-11 15:49:07 -0800812 sbn = null;
Chris Wren24fb8942015-06-18 14:33:56 -0400813 }
Daniel Sandlerf5a78382015-05-15 23:59:36 -0400814
Christoph Studer05ad4822014-05-16 14:16:03 +0200815 // protect subclass from concurrent modifications of (@link mNotificationKeys}.
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800816 synchronized (mLock) {
817 applyUpdateLocked(update);
818 if (sbn != null) {
819 SomeArgs args = SomeArgs.obtain();
820 args.arg1 = sbn;
821 args.arg2 = mRankingMap;
822 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED,
823 args).sendToTarget();
824 } else {
825 // still pass along the ranking map, it may contain other information
826 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE,
827 mRankingMap).sendToTarget();
Chris Wrenf9536642014-04-17 10:01:54 -0400828 }
John Spurlockc133ab82013-06-10 15:16:22 -0400829 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800830
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700831 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800832
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700833 @Override
Griff Hazen84a00ea2014-09-02 17:10:47 -0700834 public void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder,
Christoph Studer05ad4822014-05-16 14:16:03 +0200835 NotificationRankingUpdate update) {
Griff Hazen84a00ea2014-09-02 17:10:47 -0700836 StatusBarNotification sbn;
837 try {
838 sbn = sbnHolder.get();
839 } catch (RemoteException e) {
840 Log.w(TAG, "onNotificationRemoved: Error receiving StatusBarNotification", e);
841 return;
842 }
Christoph Studer05ad4822014-05-16 14:16:03 +0200843 // protect subclass from concurrent modifications of (@link mNotificationKeys}.
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800844 synchronized (mLock) {
845 applyUpdateLocked(update);
846 SomeArgs args = SomeArgs.obtain();
847 args.arg1 = sbn;
848 args.arg2 = mRankingMap;
849 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_REMOVED,
850 args).sendToTarget();
John Spurlockc133ab82013-06-10 15:16:22 -0400851 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800852
Daniel Sandler5feceeb2013-03-22 18:29:23 -0700853 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800854
John Spurlocka4294292014-03-24 18:02:32 -0400855 @Override
Christoph Studer05ad4822014-05-16 14:16:03 +0200856 public void onListenerConnected(NotificationRankingUpdate update) {
857 // protect subclass from concurrent modifications of (@link mNotificationKeys}.
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800858 synchronized (mLock) {
859 applyUpdateLocked(update);
John Spurlocka4294292014-03-24 18:02:32 -0400860 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800861 mHandler.obtainMessage(MyHandler.MSG_ON_LISTENER_CONNECTED).sendToTarget();
John Spurlocka4294292014-03-24 18:02:32 -0400862 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800863
Chris Wrenf9536642014-04-17 10:01:54 -0400864 @Override
Christoph Studer05ad4822014-05-16 14:16:03 +0200865 public void onNotificationRankingUpdate(NotificationRankingUpdate update)
Chris Wrenf9536642014-04-17 10:01:54 -0400866 throws RemoteException {
Christoph Studer05ad4822014-05-16 14:16:03 +0200867 // protect subclass from concurrent modifications of (@link mNotificationKeys}.
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800868 synchronized (mLock) {
869 applyUpdateLocked(update);
870 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE,
871 mRankingMap).sendToTarget();
Chris Wrenf9536642014-04-17 10:01:54 -0400872 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800873
Chris Wrenf9536642014-04-17 10:01:54 -0400874 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800875
John Spurlock1fa865f2014-07-21 14:56:39 -0400876 @Override
John Spurlockd8afe3c2014-08-01 14:04:07 -0400877 public void onListenerHintsChanged(int hints) throws RemoteException {
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800878 mHandler.obtainMessage(MyHandler.MSG_ON_LISTENER_HINTS_CHANGED,
879 hints, 0).sendToTarget();
John Spurlock1fa865f2014-07-21 14:56:39 -0400880 }
Christoph Studer85a384b2014-08-27 20:16:15 +0200881
882 @Override
883 public void onInterruptionFilterChanged(int interruptionFilter) throws RemoteException {
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800884 mHandler.obtainMessage(MyHandler.MSG_ON_INTERRUPTION_FILTER_CHANGED,
885 interruptionFilter, 0).sendToTarget();
Christoph Studer85a384b2014-08-27 20:16:15 +0200886 }
Chris Wren51017d02015-12-15 15:34:46 -0500887
888 @Override
889 public void onNotificationEnqueued(IStatusBarNotificationHolder notificationHolder,
890 int importance, boolean user) throws RemoteException {
891 // no-op in the listener
892 }
893
894 @Override
895 public void onNotificationVisibilityChanged(String key, long time, boolean visible)
896 throws RemoteException {
897 // no-op in the listener
898 }
899
900 @Override
901 public void onNotificationClick(String key, long time) throws RemoteException {
902 // no-op in the listener
903 }
904
905 @Override
906 public void onNotificationActionClick(String key, long time, int actionIndex)
907 throws RemoteException {
908 // no-op in the listener
909 }
910
911 @Override
912 public void onNotificationRemovedReason(String key, long time, int reason)
913 throws RemoteException {
914 // no-op in the listener
915 }
Chris Wrenf9536642014-04-17 10:01:54 -0400916 }
917
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800918 private void applyUpdateLocked(NotificationRankingUpdate update) {
Christoph Studerd0694b62014-06-04 16:36:01 +0200919 mRankingMap = new RankingMap(update);
920 }
921
Svet Ganovb8f53ee2016-02-18 08:38:56 -0800922 /** @hide */
923 protected Context getContext() {
Christoph Studer4600f9b2014-07-22 22:44:43 +0200924 if (mSystemContext != null) {
925 return mSystemContext;
926 }
927 return this;
928 }
929
Christoph Studerd0694b62014-06-04 16:36:01 +0200930 /**
Christoph Studer1d599da2014-06-12 15:25:59 +0200931 * Stores ranking related information on a currently active notification.
Christoph Studerd0694b62014-06-04 16:36:01 +0200932 *
933 * <p>
Christoph Studer1d599da2014-06-12 15:25:59 +0200934 * Ranking objects aren't automatically updated as notification events
935 * occur. Instead, ranking information has to be retrieved again via the
936 * current {@link RankingMap}.
Christoph Studerd0694b62014-06-04 16:36:01 +0200937 */
938 public static class Ranking {
Julia Reynolds0edb50c2016-02-26 14:08:25 -0500939
940 /** @hide */
941 @IntDef({VISIBILITY_NO_OVERRIDE, IMPORTANCE_UNSPECIFIED, IMPORTANCE_NONE,
942 IMPORTANCE_MIN, IMPORTANCE_LOW, IMPORTANCE_DEFAULT, IMPORTANCE_HIGH,
943 IMPORTANCE_MAX})
944 @Retention(RetentionPolicy.SOURCE)
945 public @interface Importance {}
946
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400947 /** Value signifying that the user has not expressed a per-app visibility override value.
948 * @hide */
949 public static final int VISIBILITY_NO_OVERRIDE = -1000;
950
Chris Wren9fa689f2015-11-20 16:44:53 -0500951 /**
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500952 * Value signifying that the user has not expressed an importance.
Chris Wren9fa689f2015-11-20 16:44:53 -0500953 *
Julia Reynolds5d25ee72015-11-20 15:38:20 -0500954 * This value is for persisting preferences, and should never be associated with
Chris Wren9fa689f2015-11-20 16:44:53 -0500955 * an actual notification.
956 */
957 public static final int IMPORTANCE_UNSPECIFIED = -1000;
958
959 /**
960 * A notification with no importance: shows nowhere, is blocked.
961 */
Julia Reynoldsead00aa2015-12-07 08:23:48 -0500962 public static final int IMPORTANCE_NONE = 0;
Chris Wren9fa689f2015-11-20 16:44:53 -0500963
964 /**
Julia Reynoldsf0f629f2016-02-25 09:34:04 -0500965 * Min notification importance: only shows in the shade, below the fold.
Chris Wren9fa689f2015-11-20 16:44:53 -0500966 */
Julia Reynoldsf0f629f2016-02-25 09:34:04 -0500967 public static final int IMPORTANCE_MIN = 1;
Chris Wren9fa689f2015-11-20 16:44:53 -0500968
969 /**
Julia Reynoldsf0f629f2016-02-25 09:34:04 -0500970 * Low notification importance: shows everywhere, but is not intrusive.
Chris Wren9fa689f2015-11-20 16:44:53 -0500971 */
Julia Reynoldsf0f629f2016-02-25 09:34:04 -0500972 public static final int IMPORTANCE_LOW = 2;
Chris Wren9fa689f2015-11-20 16:44:53 -0500973
974 /**
Julia Reynoldsf0f629f2016-02-25 09:34:04 -0500975 * Default notification importance: shows everywhere, allowed to makes noise,
Chris Wren9fa689f2015-11-20 16:44:53 -0500976 * but does not visually intrude.
977 */
Julia Reynoldsf0f629f2016-02-25 09:34:04 -0500978 public static final int IMPORTANCE_DEFAULT = 3;
Chris Wren9fa689f2015-11-20 16:44:53 -0500979
980 /**
Julia Reynoldsf0f629f2016-02-25 09:34:04 -0500981 * Higher notification importance: shows everywhere, allowed to makes noise and peek.
Chris Wren9fa689f2015-11-20 16:44:53 -0500982 */
Julia Reynoldsf0f629f2016-02-25 09:34:04 -0500983 public static final int IMPORTANCE_HIGH = 4;
984
985 /**
986 * Highest notification importance: shows everywhere, allowed to makes noise, peek, and
987 * use full screen intents.
988 */
989 public static final int IMPORTANCE_MAX = 5;
Chris Wren9fa689f2015-11-20 16:44:53 -0500990
Christoph Studer1d599da2014-06-12 15:25:59 +0200991 private String mKey;
992 private int mRank = -1;
993 private boolean mIsAmbient;
Christoph Studerce7d6d22014-08-26 19:21:31 +0200994 private boolean mMatchesInterruptionFilter;
Chris Wren3ad4e3a2014-09-02 17:23:51 -0400995 private int mVisibilityOverride;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500996 private int mSuppressedVisualEffects;
Julia Reynolds0edb50c2016-02-26 14:08:25 -0500997 private @Importance int mImportance;
Chris Wrenbdf33762015-12-04 15:50:51 -0500998 private CharSequence mImportanceExplanation;
Christoph Studerd0694b62014-06-04 16:36:01 +0200999
Christoph Studer1d599da2014-06-12 15:25:59 +02001000 public Ranking() {}
Christoph Studerd0694b62014-06-04 16:36:01 +02001001
1002 /**
1003 * Returns the key of the notification this Ranking applies to.
1004 */
1005 public String getKey() {
1006 return mKey;
1007 }
1008
1009 /**
1010 * Returns the rank of the notification.
1011 *
1012 * @return the rank of the notification, that is the 0-based index in
1013 * the list of active notifications.
1014 */
1015 public int getRank() {
1016 return mRank;
1017 }
1018
1019 /**
1020 * Returns whether the notification is an ambient notification, that is
1021 * a notification that doesn't require the user's immediate attention.
1022 */
1023 public boolean isAmbient() {
1024 return mIsAmbient;
1025 }
1026
1027 /**
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001028 * Returns the user specificed visibility for the package that posted
1029 * this notification, or
1030 * {@link NotificationListenerService.Ranking#VISIBILITY_NO_OVERRIDE} if
1031 * no such preference has been expressed.
1032 * @hide
1033 */
1034 public int getVisibilityOverride() {
1035 return mVisibilityOverride;
1036 }
1037
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001038 /**
1039 * Returns the type(s) of visual effects that should be suppressed for this notification.
Julia Reynoldsd5607292016-02-05 15:25:58 -05001040 * See {@link #SUPPRESSED_EFFECT_SCREEN_OFF}, {@link #SUPPRESSED_EFFECT_SCREEN_ON}.
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001041 */
1042 public int getSuppressedVisualEffects() {
1043 return mSuppressedVisualEffects;
1044 }
1045
Christoph Studerce7d6d22014-08-26 19:21:31 +02001046 /**
1047 * Returns whether the notification matches the user's interruption
1048 * filter.
Chris Wren0fef44d2014-09-30 13:05:14 -04001049 *
1050 * @return {@code true} if the notification is allowed by the filter, or
1051 * {@code false} if it is blocked.
Christoph Studerce7d6d22014-08-26 19:21:31 +02001052 */
1053 public boolean matchesInterruptionFilter() {
1054 return mMatchesInterruptionFilter;
Christoph Studer1d599da2014-06-12 15:25:59 +02001055 }
1056
Chris Wren9fa689f2015-11-20 16:44:53 -05001057 /**
1058 * Returns the importance of the notification, which dictates its
1059 * modes of presentation, see: {@link #IMPORTANCE_DEFAULT}, etc.
1060 *
1061 * @return the rank of the notification
1062 */
Julia Reynolds0edb50c2016-02-26 14:08:25 -05001063 public @Importance int getImportance() {
Chris Wrenbdf33762015-12-04 15:50:51 -05001064 return mImportance;
Chris Wren9fa689f2015-11-20 16:44:53 -05001065 }
1066
1067 /**
Chris Wren10c63d82016-02-05 14:55:35 -05001068 * If the importance has been overriden by user preference, then this will be non-null,
Chris Wren9fa689f2015-11-20 16:44:53 -05001069 * and should be displayed to the user.
1070 *
1071 * @return the explanation for the importance, or null if it is the natural importance
1072 */
1073 public CharSequence getImportanceExplanation() {
Chris Wrenbdf33762015-12-04 15:50:51 -05001074 return mImportanceExplanation;
Chris Wren9fa689f2015-11-20 16:44:53 -05001075 }
1076
Julia Reynolds0421e6d2016-01-08 09:51:24 -05001077 private void populate(String key, int rank, boolean matchesInterruptionFilter,
1078 int visibilityOverride, int suppressedVisualEffects, int importance,
Chris Wrenbdf33762015-12-04 15:50:51 -05001079 CharSequence explanation) {
Christoph Studer1d599da2014-06-12 15:25:59 +02001080 mKey = key;
1081 mRank = rank;
Julia Reynoldsf0f629f2016-02-25 09:34:04 -05001082 mIsAmbient = importance < IMPORTANCE_LOW;
Christoph Studerce7d6d22014-08-26 19:21:31 +02001083 mMatchesInterruptionFilter = matchesInterruptionFilter;
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001084 mVisibilityOverride = visibilityOverride;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001085 mSuppressedVisualEffects = suppressedVisualEffects;
Chris Wrenbdf33762015-12-04 15:50:51 -05001086 mImportance = importance;
1087 mImportanceExplanation = explanation;
Christoph Studerd0694b62014-06-04 16:36:01 +02001088 }
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001089
1090 /**
1091 * {@hide}
1092 */
1093 public static String importanceToString(int importance) {
1094 switch (importance) {
1095 case IMPORTANCE_UNSPECIFIED:
1096 return "UNSPECIFIED";
1097 case IMPORTANCE_NONE:
1098 return "NONE";
Julia Reynoldsf0f629f2016-02-25 09:34:04 -05001099 case IMPORTANCE_MIN:
1100 return "MIN";
Julia Reynolds5d25ee72015-11-20 15:38:20 -05001101 case IMPORTANCE_LOW:
1102 return "LOW";
1103 case IMPORTANCE_DEFAULT:
1104 return "DEFAULT";
1105 case IMPORTANCE_HIGH:
1106 return "HIGH";
1107 case IMPORTANCE_MAX:
1108 return "MAX";
1109 default:
1110 return "UNKNOWN(" + String.valueOf(importance) + ")";
1111 }
1112 }
Christoph Studer05ad4822014-05-16 14:16:03 +02001113 }
1114
1115 /**
1116 * Provides access to ranking information on currently active
1117 * notifications.
1118 *
1119 * <p>
1120 * Note that this object represents a ranking snapshot that only applies to
1121 * notifications active at the time of retrieval.
1122 */
Christoph Studerd0694b62014-06-04 16:36:01 +02001123 public static class RankingMap implements Parcelable {
Christoph Studer05ad4822014-05-16 14:16:03 +02001124 private final NotificationRankingUpdate mRankingUpdate;
Christoph Studerdda48f12014-07-29 23:13:16 +02001125 private ArrayMap<String,Integer> mRanks;
1126 private ArraySet<Object> mIntercepted;
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001127 private ArrayMap<String, Integer> mVisibilityOverrides;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001128 private ArrayMap<String, Integer> mSuppressedVisualEffects;
Chris Wrenbdf33762015-12-04 15:50:51 -05001129 private ArrayMap<String, Integer> mImportance;
1130 private ArrayMap<String, String> mImportanceExplanation;
Christoph Studer05ad4822014-05-16 14:16:03 +02001131
Christoph Studerd0694b62014-06-04 16:36:01 +02001132 private RankingMap(NotificationRankingUpdate rankingUpdate) {
Christoph Studer05ad4822014-05-16 14:16:03 +02001133 mRankingUpdate = rankingUpdate;
1134 }
1135
1136 /**
1137 * Request the list of notification keys in their current ranking
1138 * order.
1139 *
1140 * @return An array of active notification keys, in their ranking order.
1141 */
1142 public String[] getOrderedKeys() {
1143 return mRankingUpdate.getOrderedKeys();
1144 }
1145
1146 /**
Christoph Studer1d599da2014-06-12 15:25:59 +02001147 * Populates outRanking with ranking information for the notification
1148 * with the given key.
Christoph Studer05ad4822014-05-16 14:16:03 +02001149 *
Christoph Studer1d599da2014-06-12 15:25:59 +02001150 * @return true if a valid key has been passed and outRanking has
1151 * been populated; false otherwise
Christoph Studer05ad4822014-05-16 14:16:03 +02001152 */
Christoph Studer1d599da2014-06-12 15:25:59 +02001153 public boolean getRanking(String key, Ranking outRanking) {
1154 int rank = getRank(key);
Julia Reynolds0421e6d2016-01-08 09:51:24 -05001155 outRanking.populate(key, rank, !isIntercepted(key),
Chris Wrenbdf33762015-12-04 15:50:51 -05001156 getVisibilityOverride(key), getSuppressedVisualEffects(key),
1157 getImportance(key), getImportanceExplanation(key));
Christoph Studer1d599da2014-06-12 15:25:59 +02001158 return rank >= 0;
Christoph Studerd0694b62014-06-04 16:36:01 +02001159 }
1160
Christoph Studer1d599da2014-06-12 15:25:59 +02001161 private int getRank(String key) {
Christoph Studerdda48f12014-07-29 23:13:16 +02001162 synchronized (this) {
1163 if (mRanks == null) {
1164 buildRanksLocked();
Christoph Studer05ad4822014-05-16 14:16:03 +02001165 }
1166 }
Christoph Studerdda48f12014-07-29 23:13:16 +02001167 Integer rank = mRanks.get(key);
1168 return rank != null ? rank : -1;
Christoph Studer1d599da2014-06-12 15:25:59 +02001169 }
1170
Christoph Studer1d599da2014-06-12 15:25:59 +02001171 private boolean isIntercepted(String key) {
Christoph Studerdda48f12014-07-29 23:13:16 +02001172 synchronized (this) {
1173 if (mIntercepted == null) {
1174 buildInterceptedSetLocked();
Christoph Studer1d599da2014-06-12 15:25:59 +02001175 }
1176 }
Christoph Studerdda48f12014-07-29 23:13:16 +02001177 return mIntercepted.contains(key);
1178 }
1179
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001180 private int getVisibilityOverride(String key) {
1181 synchronized (this) {
1182 if (mVisibilityOverrides == null) {
1183 buildVisibilityOverridesLocked();
1184 }
1185 }
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001186 Integer override = mVisibilityOverrides.get(key);
1187 if (override == null) {
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001188 return Ranking.VISIBILITY_NO_OVERRIDE;
1189 }
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001190 return override.intValue();
1191 }
1192
1193 private int getSuppressedVisualEffects(String key) {
1194 synchronized (this) {
1195 if (mSuppressedVisualEffects == null) {
1196 buildSuppressedVisualEffectsLocked();
1197 }
1198 }
1199 Integer suppressed = mSuppressedVisualEffects.get(key);
1200 if (suppressed == null) {
1201 return 0;
1202 }
1203 return suppressed.intValue();
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001204 }
1205
Chris Wrenbdf33762015-12-04 15:50:51 -05001206 private int getImportance(String key) {
1207 synchronized (this) {
1208 if (mImportance == null) {
1209 buildImportanceLocked();
1210 }
1211 }
1212 Integer importance = mImportance.get(key);
1213 if (importance == null) {
1214 return Ranking.IMPORTANCE_DEFAULT;
1215 }
1216 return importance.intValue();
1217 }
1218
1219 private String getImportanceExplanation(String key) {
1220 synchronized (this) {
1221 if (mImportanceExplanation == null) {
1222 buildImportanceExplanationLocked();
1223 }
1224 }
1225 return mImportanceExplanation.get(key);
1226 }
1227
Christoph Studerdda48f12014-07-29 23:13:16 +02001228 // Locked by 'this'
1229 private void buildRanksLocked() {
1230 String[] orderedKeys = mRankingUpdate.getOrderedKeys();
1231 mRanks = new ArrayMap<>(orderedKeys.length);
1232 for (int i = 0; i < orderedKeys.length; i++) {
1233 String key = orderedKeys[i];
1234 mRanks.put(key, i);
1235 }
1236 }
1237
1238 // Locked by 'this'
1239 private void buildInterceptedSetLocked() {
1240 String[] dndInterceptedKeys = mRankingUpdate.getInterceptedKeys();
1241 mIntercepted = new ArraySet<>(dndInterceptedKeys.length);
1242 Collections.addAll(mIntercepted, dndInterceptedKeys);
Christoph Studer05ad4822014-05-16 14:16:03 +02001243 }
1244
Chris Wren3ad4e3a2014-09-02 17:23:51 -04001245 // Locked by 'this'
1246 private void buildVisibilityOverridesLocked() {
1247 Bundle visibilityBundle = mRankingUpdate.getVisibilityOverrides();
1248 mVisibilityOverrides = new ArrayMap<>(visibilityBundle.size());
1249 for (String key: visibilityBundle.keySet()) {
1250 mVisibilityOverrides.put(key, visibilityBundle.getInt(key));
1251 }
1252 }
1253
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001254 // Locked by 'this'
1255 private void buildSuppressedVisualEffectsLocked() {
1256 Bundle suppressedBundle = mRankingUpdate.getSuppressedVisualEffects();
1257 mSuppressedVisualEffects = new ArrayMap<>(suppressedBundle.size());
1258 for (String key: suppressedBundle.keySet()) {
1259 mSuppressedVisualEffects.put(key, suppressedBundle.getInt(key));
1260 }
1261 }
Chris Wrenbdf33762015-12-04 15:50:51 -05001262 // Locked by 'this'
1263 private void buildImportanceLocked() {
1264 String[] orderedKeys = mRankingUpdate.getOrderedKeys();
1265 int[] importance = mRankingUpdate.getImportance();
1266 mImportance = new ArrayMap<>(orderedKeys.length);
1267 for (int i = 0; i < orderedKeys.length; i++) {
1268 String key = orderedKeys[i];
1269 mImportance.put(key, importance[i]);
1270 }
1271 }
1272
1273 // Locked by 'this'
1274 private void buildImportanceExplanationLocked() {
1275 Bundle explanationBundle = mRankingUpdate.getImportanceExplanation();
1276 mImportanceExplanation = new ArrayMap<>(explanationBundle.size());
1277 for (String key: explanationBundle.keySet()) {
1278 mImportanceExplanation.put(key, explanationBundle.getString(key));
1279 }
1280 }
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001281
Christoph Studer05ad4822014-05-16 14:16:03 +02001282 // ----------- Parcelable
1283
1284 @Override
1285 public int describeContents() {
1286 return 0;
1287 }
1288
1289 @Override
1290 public void writeToParcel(Parcel dest, int flags) {
1291 dest.writeParcelable(mRankingUpdate, flags);
1292 }
1293
Christoph Studerd0694b62014-06-04 16:36:01 +02001294 public static final Creator<RankingMap> CREATOR = new Creator<RankingMap>() {
Christoph Studer05ad4822014-05-16 14:16:03 +02001295 @Override
Christoph Studerd0694b62014-06-04 16:36:01 +02001296 public RankingMap createFromParcel(Parcel source) {
Christoph Studer05ad4822014-05-16 14:16:03 +02001297 NotificationRankingUpdate rankingUpdate = source.readParcelable(null);
Christoph Studerd0694b62014-06-04 16:36:01 +02001298 return new RankingMap(rankingUpdate);
Christoph Studer05ad4822014-05-16 14:16:03 +02001299 }
1300
1301 @Override
Christoph Studerd0694b62014-06-04 16:36:01 +02001302 public RankingMap[] newArray(int size) {
1303 return new RankingMap[size];
Christoph Studer05ad4822014-05-16 14:16:03 +02001304 }
1305 };
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001306 }
Svet Ganovb8f53ee2016-02-18 08:38:56 -08001307
1308 private final class MyHandler extends Handler {
1309 public static final int MSG_ON_NOTIFICATION_POSTED = 1;
1310 public static final int MSG_ON_NOTIFICATION_REMOVED = 2;
1311 public static final int MSG_ON_LISTENER_CONNECTED = 3;
1312 public static final int MSG_ON_NOTIFICATION_RANKING_UPDATE = 4;
1313 public static final int MSG_ON_LISTENER_HINTS_CHANGED = 5;
1314 public static final int MSG_ON_INTERRUPTION_FILTER_CHANGED = 6;
1315
1316 public MyHandler(Looper looper) {
1317 super(looper, null, false);
1318 }
1319
1320 @Override
1321 public void handleMessage(Message msg) {
1322 switch (msg.what) {
1323 case MSG_ON_NOTIFICATION_POSTED: {
1324 SomeArgs args = (SomeArgs) msg.obj;
1325 StatusBarNotification sbn = (StatusBarNotification) args.arg1;
1326 RankingMap rankingMap = (RankingMap) args.arg2;
1327 args.recycle();
1328 onNotificationPosted(sbn, rankingMap);
1329 } break;
1330
1331 case MSG_ON_NOTIFICATION_REMOVED: {
1332 SomeArgs args = (SomeArgs) msg.obj;
1333 StatusBarNotification sbn = (StatusBarNotification) args.arg1;
1334 RankingMap rankingMap = (RankingMap) args.arg2;
1335 args.recycle();
1336 onNotificationRemoved(sbn, rankingMap);
1337 } break;
1338
1339 case MSG_ON_LISTENER_CONNECTED: {
1340 onListenerConnected();
1341 } break;
1342
1343 case MSG_ON_NOTIFICATION_RANKING_UPDATE: {
1344 RankingMap rankingMap = (RankingMap) msg.obj;
1345 onNotificationRankingUpdate(rankingMap);
1346 } break;
1347
1348 case MSG_ON_LISTENER_HINTS_CHANGED: {
1349 final int hints = msg.arg1;
1350 onListenerHintsChanged(hints);
1351 } break;
1352
1353 case MSG_ON_INTERRUPTION_FILTER_CHANGED: {
1354 final int interruptionFilter = msg.arg1;
1355 onInterruptionFilterChanged(interruptionFilter);
1356 } break;
1357 }
1358 }
1359 }
Daniel Sandler5feceeb2013-03-22 18:29:23 -07001360}