NotificationAssistantService.java revision c441856bb6c5712efab9d5ef422c95210884a1d8
1/*
2 * Copyright (C) 2015 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
19import android.annotation.SdkConstant;
20import android.annotation.SystemApi;
21import android.app.INotificationManager;
22import android.app.Notification;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.net.Uri;
27import android.os.IBinder;
28import android.os.Parcel;
29import android.os.Parcelable;
30import android.os.RemoteException;
31import android.os.ServiceManager;
32import android.util.Log;
33
34/**
35 * A service that helps the user manage notifications by modifying the
36 * relative importance of notifications.
37 * <p>To extend this class, you must declare the service in your manifest file with
38 * the {@link android.Manifest.permission#BIND_NOTIFICATION_ASSISTANT_SERVICE} permission
39 * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
40 * <pre>
41 * &lt;service android:name=".NotificationAssistant"
42 *          android:label="&#64;string/service_name"
43 *          android:permission="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE">
44 *     &lt;intent-filter>
45 *         &lt;action android:name="android.service.notification.NotificationAssistantService" />
46 *     &lt;/intent-filter>
47 * &lt;/service></pre>
48 * @hide
49 */
50@SystemApi
51public abstract class NotificationAssistantService extends NotificationListenerService {
52    private static final String TAG = "NotificationAssistant";
53
54    /**
55     * The {@link Intent} that must be declared as handled by the service.
56     */
57    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
58    public static final String SERVICE_INTERFACE
59            = "android.service.notification.NotificationAssistantService";
60
61    /** Notification was canceled by the status bar reporting a click. */
62    public static final int REASON_DELEGATE_CLICK = 1;
63
64    /** Notification was canceled by the status bar reporting a user dismissal. */
65    public static final int REASON_DELEGATE_CANCEL = 2;
66
67    /** Notification was canceled by the status bar reporting a user dismiss all. */
68    public static final int REASON_DELEGATE_CANCEL_ALL = 3;
69
70    /** Notification was canceled by the status bar reporting an inflation error. */
71    public static final int REASON_DELEGATE_ERROR = 4;
72
73    /** Notification was canceled by the package manager modifying the package. */
74    public static final int REASON_PACKAGE_CHANGED = 5;
75
76    /** Notification was canceled by the owning user context being stopped. */
77    public static final int REASON_USER_STOPPED = 6;
78
79    /** Notification was canceled by the user banning the package. */
80    public static final int REASON_PACKAGE_BANNED = 7;
81
82    /** Notification was canceled by the app canceling this specific notification. */
83    public static final int REASON_APP_CANCEL = 8;
84
85    /** Notification was canceled by the app cancelling all its notifications. */
86    public static final int REASON_APP_CANCEL_ALL = 9;
87
88    /** Notification was canceled by a listener reporting a user dismissal. */
89    public static final int REASON_LISTENER_CANCEL = 10;
90
91    /** Notification was canceled by a listener reporting a user dismiss all. */
92    public static final int REASON_LISTENER_CANCEL_ALL = 11;
93
94    /** Notification was canceled because it was a member of a canceled group. */
95    public static final int REASON_GROUP_SUMMARY_CANCELED = 12;
96
97    /** Notification was canceled because it was an invisible member of a group. */
98    public static final int REASON_GROUP_OPTIMIZATION = 13;
99
100    /** Notification was canceled by the user banning the topic. */
101    public static final int REASON_TOPIC_BANNED = 14;
102
103    /** Notification was canceled by the device administrator suspending the package. */
104    public static final int REASON_PACKAGE_SUSPENDED = 15;
105
106    /** Notification was canceled by the owning managed profile being turned off. */
107    public static final int REASON_PROFILE_TURNED_OFF = 16;
108
109    public class Adjustment {
110        int mImportance;
111        CharSequence mExplanation;
112        Uri mReference;
113
114        /**
115         * Create a notification importance adjustment.
116         *
117         * @param importance The final importance of the notification.
118         * @param explanation A human-readable justification for the adjustment.
119         * @param reference A reference to an external object that augments the
120         *                  explanation, such as a
121         *                  {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
122         *                  or null.
123         */
124        public Adjustment(int importance, CharSequence explanation, Uri reference) {
125            mImportance = importance;
126            mExplanation = explanation;
127            mReference = reference;
128        }
129    }
130
131    @Override
132    public IBinder onBind(Intent intent) {
133        if (mWrapper == null) {
134            mWrapper = new NotificationAssistantWrapper();
135        }
136        return mWrapper;
137    }
138
139    /**
140     * A notification was posted by an app. Called before alert.
141     *
142     * @param sbn the new notification
143     * @param importance the initial importance of the notification.
144     * @param user true if the initial importance reflects an explicit user preference.
145     * @return an adjustment or null to take no action, within 100ms.
146     */
147    abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
148          int importance, boolean user);
149
150    /**
151     * The visibility of a notification has changed.
152     *
153     * @param key the notification key
154     * @param time milliseconds since midnight, January 1, 1970 UTC.
155     * @param visible true if the notification became visible, false if hidden.
156     */
157    public void onNotificationVisibilityChanged(String key, long time, boolean visible)
158    {
159        // Do nothing, Override this to collect visibility statistics.
160    }
161
162    /**
163     * The user clicked on a notification.
164     *
165     * @param key the notification key
166     * @param time milliseconds since midnight, January 1, 1970 UTC.
167     */
168    public void onNotificationClick(String key, long time)
169    {
170        // Do nothing, Override this to collect click statistics
171    }
172
173    /**
174     * The user clicked on a notification action.
175     *
176     * @param key the notification key
177     * @param time milliseconds since midnight, January 1, 1970 UTC.
178     * @param actionIndex the index of the action button that was pressed.
179     */
180    public void onNotificationActionClick(String key, long time, int actionIndex)
181    {
182        // Do nothing, Override this to collect action button click statistics
183    }
184
185    /**
186     * A notification was removed.
187
188     * @param key the notification key
189     * @param time milliseconds since midnight, January 1, 1970 UTC.
190     * @param reason see {@link #REASON_LISTENER_CANCEL}, etc.
191     */
192    public void onNotificationRemoved(String key, long time, int reason) {
193        // Do nothing, Override this to collect dismissal statistics
194    }
195
196    /**
197     * Change the importance of an existing notification.  N.B. this won’t cause
198     * an existing notification to alert, but might allow a future update to
199     * this notification to alert.
200     *
201     * @param key the notification key
202     * @param adjustment the new importance with an explanation
203     */
204    public final void adjustImportance(String key, Adjustment adjustment)
205    {
206        if (!isBound()) return;
207        try {
208            getNotificationInterface().setImportanceFromAssistant(mWrapper, key,
209                    adjustment.mImportance, adjustment.mExplanation);
210        } catch (android.os.RemoteException ex) {
211            Log.v(TAG, "Unable to contact notification manager", ex);
212        }
213    }
214
215    private class NotificationAssistantWrapper extends NotificationListenerWrapper {
216        @Override
217        public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder,
218                                           int importance, boolean user) throws RemoteException {
219            StatusBarNotification sbn;
220            try {
221                sbn = sbnHolder.get();
222            } catch (RemoteException e) {
223                Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e);
224                return;
225            }
226
227            try {
228                Adjustment adjustment =
229                    NotificationAssistantService.this.onNotificationEnqueued(sbn, importance, user);
230                if (adjustment != null) {
231                    adjustImportance(sbn.getKey(), adjustment);
232                }
233            } catch (Throwable t) {
234                Log.w(TAG, "Error running onNotificationEnqueued", t);
235            }
236        }
237
238        @Override
239        public void onNotificationVisibilityChanged(String key, long time, boolean visible)
240                throws RemoteException {
241            try {
242                NotificationAssistantService.this.onNotificationVisibilityChanged(key, time,
243                        visible);
244            } catch (Throwable t) {
245                Log.w(TAG, "Error running onNotificationVisibilityChanged", t);
246            }
247        }
248
249        @Override
250        public void onNotificationClick(String key, long time) throws RemoteException {
251            try {
252                NotificationAssistantService.this.onNotificationClick(key, time);
253            } catch (Throwable t) {
254                Log.w(TAG, "Error running onNotificationClick", t);
255            }
256        }
257
258        @Override
259        public void onNotificationActionClick(String key, long time, int actionIndex)
260                throws RemoteException {
261            try {
262                NotificationAssistantService.this.onNotificationActionClick(key, time, actionIndex);
263            } catch (Throwable t) {
264                Log.w(TAG, "Error running onNotificationActionClick", t);
265            }
266        }
267
268        @Override
269        public void onNotificationRemovedReason(String key, long time, int reason)
270                throws RemoteException {
271            try {
272                NotificationAssistantService.this.onNotificationRemoved(key, time, reason);
273            } catch (Throwable t) {
274                Log.w(TAG, "Error running onNotificationRemoved", t);
275            }
276        }
277    }
278}
279