diff options
| author | Daniel Sandler <dsandler@android.com> | 2013-03-22 18:29:23 -0700 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-04-05 19:54:44 +0000 |
| commit | 5feceebb892d4cb5777cea3c6174b206705d456b (patch) | |
| tree | 58feee1605f35b07e70887f4ec017d80b5687436 /core/java/android/service | |
| parent | bab9687e6473072d6ff4f7ea5a7b21bcfbf95744 (diff) | |
| download | frameworks_base-5feceebb892d4cb5777cea3c6174b206705d456b.zip frameworks_base-5feceebb892d4cb5777cea3c6174b206705d456b.tar.gz frameworks_base-5feceebb892d4cb5777cea3c6174b206705d456b.tar.bz2 | |
New NotificationListenerService.
This is the best and only way for apps to listen for
notifications: create a NotificationListenerService, wait
for the NoMan to bind to you (as a result of the user
checking a box somewhere in Settings and agreeing to a
scary dialog box), and you'll start receiving notification
posted and dismissed callbacks. Your service, while enabled,
will also be able to clear one or all notifications.
Use this power wisely.
This change moves StatusBarNotification out of
com.android.internal into android.service.notification.
[Internal customers, including System UI and early users of
the system-only listener binder API, will need to be
updated.]
Bug: 8199624
Change-Id: I1be46f823d4b3ddc901109ec1e085cd6deb740c2
Diffstat (limited to 'core/java/android/service')
4 files changed, 374 insertions, 0 deletions
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl new file mode 100644 index 0000000..425fdc1 --- /dev/null +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.notification; + +import android.service.notification.StatusBarNotification; + +/** @hide */ +oneway interface INotificationListener +{ + void onNotificationPosted(in StatusBarNotification notification); + void onNotificationRemoved(in StatusBarNotification notification); +}
\ No newline at end of file diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java new file mode 100644 index 0000000..86bab2a --- /dev/null +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.notification; + +import android.annotation.SdkConstant; +import android.app.INotificationManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.os.ServiceManager; +import android.util.Log; + +public abstract class NotificationListenerService extends Service { + // TAG = "NotificationListenerService[MySubclass]" + private final String TAG = NotificationListenerService.class.getSimpleName() + + "[" + getClass().getSimpleName() + "]"; + + private INotificationListenerWrapper mWrapper = null; + + private INotificationManager mNoMan; + + /** + * The {@link Intent} that must be declared as handled by the service. + */ + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE + = "android.service.notification.NotificationListenerService"; + + /** + * Implement this method to learn about new notifications as they are posted by apps. + * + * @param sbn A data structure encapsulating the original {@link android.app.Notification} + * object as well as its identifying information (tag and id) and source + * (package name). + */ + public abstract void onNotificationPosted(StatusBarNotification sbn); + + /** + * Implement this method to learn when notifications are removed. + * <P> + * This might occur because the user has dismissed the notification using system UI (or another + * notification listener) or because the app has withdrawn the notification. + * + * @param sbn A data structure encapsulating the original {@link android.app.Notification} + * object as well as its identifying information (tag and id) and source + * (package name). + */ + public abstract void onNotificationRemoved(StatusBarNotification sbn); + + private final INotificationManager getNotificationInterface() { + if (mNoMan == null) { + mNoMan = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + } + return mNoMan; + } + + /** + * Inform the notification manager about dismissal of a single notification. + * <p> + * Use this if your listener has a user interface that allows the user to dismiss individual + * notifications, similar to the behavior of Android's status bar and notification panel. + * It should be called after the user dismisses a single notification using your UI; + * upon being informed, the notification manager will actually remove the notification + * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback. + * <P> + * <b>Note:</b> If your listener allows the user to fire a notification's + * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call + * this method at that time <i>if</i> the Notification in question has the + * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set. + * + * @param pkg Package of the notifying app. + * @param tag Tag of the notification as specified by the notifying app in + * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}. + * @param id ID of the notification as specified by the notifying app in + * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}. + */ + public final void clearNotification(String pkg, String tag, int id) { + try { + getNotificationInterface().clearNotificationFromListener(mWrapper, pkg, tag, id); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + } + + /** + * Inform the notification manager about dismissal of all notifications. + * <p> + * Use this if your listener has a user interface that allows the user to dismiss all + * notifications, similar to the behavior of Android's status bar and notification panel. + * It should be called after the user invokes the "dismiss all" function of your UI; + * upon being informed, the notification manager will actually remove all active notifications + * and you will get multiple {@link #onNotificationRemoved(StatusBarNotification)} callbacks. + * + * {@see #clearNotification(String, String, int)} + */ + public final void clearAllNotifications() { + try { + getNotificationInterface().clearAllNotificationsFromListener(mWrapper); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + } + + @Override + public IBinder onBind(Intent intent) { + if (mWrapper == null) { + mWrapper = new INotificationListenerWrapper(); + } + return mWrapper; + } + + private class INotificationListenerWrapper extends INotificationListener.Stub { + @Override + public void onNotificationPosted(StatusBarNotification sbn) { + NotificationListenerService.this.onNotificationPosted(sbn); + } + @Override + public void onNotificationRemoved(StatusBarNotification sbn) { + NotificationListenerService.this.onNotificationRemoved(sbn); + } + } +} diff --git a/core/java/android/service/notification/StatusBarNotification.aidl b/core/java/android/service/notification/StatusBarNotification.aidl new file mode 100644 index 0000000..ba81972 --- /dev/null +++ b/core/java/android/service/notification/StatusBarNotification.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2010, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.notification; + +parcelable StatusBarNotification; + diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java new file mode 100644 index 0000000..ef5f8c4 --- /dev/null +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.notification; + +import android.app.Notification; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; + +/** + * Class encapsulating a Notification. Sent by the NotificationManagerService to clients including + * the status bar and any {@link android.service.notification.NotificationListenerService}s. + */ +public class StatusBarNotification implements Parcelable { + /** The package of the app that posted the notification. */ + public final String pkg; + /** The id supplied to {@link android.app.NotificationManager#notify}. */ + public final int id; + /** The tag supplied to {@link android.app.NotificationManager#notify}, or null if no tag + * was specified. */ + public final String tag; + + /** The notifying app's calling uid. @hide */ + public final int uid; + /** The notifying app's base package. @hide */ + public final String basePkg; + /** @hide */ + public final int initialPid; + // TODO: make this field private and move callers to an accessor that + // ensures sourceUser is applied. + + /** The {@link android.app.Notification} supplied to + * {@link android.app.NotificationManager#notify}. */ + public final Notification notification; + /** The {@link android.os.UserHandle} for whom this notification is intended. */ + public final UserHandle user; + /** The time (in {@link System#currentTimeMillis} time) the notification was posted, + * which may be different than {@link android.app.Notification#when}. + */ + public final long postTime; + + /** @hide */ + public final int score; + + /** This is temporarily needed for the JB MR1 PDK. + * @hide */ + @Deprecated + public StatusBarNotification(String pkg, int id, String tag, int uid, int initialPid, int score, + Notification notification) { + this(pkg, id, tag, uid, initialPid, score, notification, UserHandle.OWNER); + } + + /** @hide */ + public StatusBarNotification(String pkg, int id, String tag, int uid, int initialPid, int score, + Notification notification, UserHandle user) { + this(pkg, null, id, tag, uid, initialPid, score, notification, user); + } + + /** @hide */ + public StatusBarNotification(String pkg, String basePkg, int id, String tag, int uid, + int initialPid, int score, Notification notification, UserHandle user) { + this(pkg, basePkg, id, tag, uid, initialPid, score, notification, user, + System.currentTimeMillis()); + } + + public StatusBarNotification(String pkg, String basePkg, int id, String tag, int uid, + int initialPid, int score, Notification notification, UserHandle user, + long postTime) { + if (pkg == null) throw new NullPointerException(); + if (notification == null) throw new NullPointerException(); + + this.pkg = pkg; + this.basePkg = pkg; + this.id = id; + this.tag = tag; + this.uid = uid; + this.initialPid = initialPid; + this.score = score; + this.notification = notification; + this.user = user; + this.notification.setUser(user); + + this.postTime = postTime; + } + + public StatusBarNotification(Parcel in) { + this.pkg = in.readString(); + this.basePkg = in.readString(); + this.id = in.readInt(); + if (in.readInt() != 0) { + this.tag = in.readString(); + } else { + this.tag = null; + } + this.uid = in.readInt(); + this.initialPid = in.readInt(); + this.score = in.readInt(); + this.notification = new Notification(in); + this.user = UserHandle.readFromParcel(in); + this.notification.setUser(this.user); + this.postTime = in.readLong(); + } + + public void writeToParcel(Parcel out, int flags) { + out.writeString(this.pkg); + out.writeString(this.basePkg); + out.writeInt(this.id); + if (this.tag != null) { + out.writeInt(1); + out.writeString(this.tag); + } else { + out.writeInt(0); + } + out.writeInt(this.uid); + out.writeInt(this.initialPid); + out.writeInt(this.score); + this.notification.writeToParcel(out, flags); + user.writeToParcel(out, flags); + + out.writeLong(this.postTime); + } + + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<StatusBarNotification> CREATOR + = new Parcelable.Creator<StatusBarNotification>() + { + public StatusBarNotification createFromParcel(Parcel parcel) + { + return new StatusBarNotification(parcel); + } + + public StatusBarNotification[] newArray(int size) + { + return new StatusBarNotification[size]; + } + }; + + @Override + public StatusBarNotification clone() { + return new StatusBarNotification(this.pkg, this.basePkg, + this.id, this.tag, this.uid, this.initialPid, + this.score, this.notification.clone(), this.user, this.postTime); + } + + @Override + public String toString() { + return String.format( + "StatusBarNotification(pkg=%s user=%s id=%d tag=%s score=%d: %s)", + this.pkg, this.user, this.id, this.tag, + this.score, this.notification); + } + + /** Convenience method to check the notification's flags for + * {@link Notification#FLAG_ONGOING_EVENT}. + */ + public boolean isOngoing() { + return (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0; + } + + /** Convenience method to check the notification's flags for + * either {@link Notification#FLAG_ONGOING_EVENT} or + * {@link Notification#FLAG_NO_CLEAR}. + */ + public boolean isClearable() { + return ((notification.flags & Notification.FLAG_ONGOING_EVENT) == 0) + && ((notification.flags & Notification.FLAG_NO_CLEAR) == 0); + } + + /** Returns a userHandle for the instance of the app that posted this notification. */ + public int getUserId() { + return this.user.getIdentifier(); + } +} |
