Merge "Allow listeners more snoozing options."

This commit is contained in:
Julia Reynolds
2016-11-28 16:40:26 +00:00
committed by Android (Google) Code Review
9 changed files with 152 additions and 19 deletions

View File

@@ -35094,6 +35094,8 @@ package android.service.notification {
method public final void requestUnbind();
method public final void setNotificationsShown(java.lang.String[]);
method public final void snoozeNotification(java.lang.String, long);
method public final void snoozeNotification(java.lang.String);
method public final void unsnoozeNotification(java.lang.String);
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2

View File

@@ -37893,7 +37893,9 @@ package android.service.notification {
method public final void setNotificationsShown(java.lang.String[]);
method public final void setOnNotificationPostedTrim(int);
method public final void snoozeNotification(java.lang.String, long);
method public final void snoozeNotification(java.lang.String);
method public void unregisterAsSystemService() throws android.os.RemoteException;
method public final void unsnoozeNotification(java.lang.String);
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2

View File

@@ -5395,6 +5395,7 @@ package android.app {
method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
method public java.util.Map<java.lang.String, android.app.AutomaticZenRule> getAutomaticZenRules();
method public final int getCurrentInterruptionFilter();
method public android.content.ComponentName getEffectsSuppressor();
method public int getImportance();
method public android.app.NotificationChannel getNotificationChannel(java.lang.String);
method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
@@ -35187,6 +35188,8 @@ package android.service.notification {
method public final void requestUnbind();
method public final void setNotificationsShown(java.lang.String[]);
method public final void snoozeNotification(java.lang.String, long);
method public final void snoozeNotification(java.lang.String);
method public final void unsnoozeNotification(java.lang.String);
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2

View File

@@ -77,7 +77,10 @@ interface INotificationManager
void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
void cancelNotificationsFromListener(in INotificationListener token, in String[] keys);
void snoozeNotificationFromListener(in INotificationListener token, String key, long until);
void snoozeNotificationUntilFromListener(in INotificationListener token, String key, long until);
void snoozeNotificationFromListener(in INotificationListener token, String key);
void unsnoozeNotificationFromListener(in INotificationListener token, String key);
void requestBindListener(in ComponentName component);
void requestUnbindListener(in INotificationListener token);

View File

@@ -19,6 +19,7 @@ package android.app;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.TestApi;
import android.app.Notification.Builder;
import android.content.ComponentName;
import android.content.Context;
@@ -428,6 +429,7 @@ public class NotificationManager
/**
* @hide
*/
@TestApi
public ComponentName getEffectsSuppressor() {
INotificationManager service = getService();
try {

View File

@@ -534,7 +534,46 @@ public abstract class NotificationListenerService extends Service {
public final void snoozeNotification(String key, long snoozeUntil) {
if (!isBound()) return;
try {
getNotificationInterface().snoozeNotificationFromListener(mWrapper, key, snoozeUntil);
getNotificationInterface().snoozeNotificationUntilFromListener(
mWrapper, key, snoozeUntil);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
}
/**
* Inform the notification manager about snoozing a specific notification.
* <p>
* Use this to snooze a notification for an indeterminate time. Upon being informed, the
* notification manager will actually remove the notification and you will get an
* {@link #onNotificationRemoved(StatusBarNotification)} callback. When the
* snoozing period expires, you will get a
* {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
* notification. Use {@link #unsnoozeNotification(String)} to restore the notification.
* @param key The key of the notification to snooze
*/
public final void snoozeNotification(String key) {
if (!isBound()) return;
try {
getNotificationInterface().snoozeNotificationFromListener(mWrapper, key);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
}
/**
* Inform the notification manager about un-snoozing a specific notification.
* <p>
* This should only be used for notifications snoozed by this listener using
* {@link #snoozeNotification(String)}. Once un-snoozed, you will get a
* {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
* notification.
* @param key The key of the notification to snooze
*/
public final void unsnoozeNotification(String key) {
if (!isBound()) return;
try {
getNotificationInterface().unsnoozeNotificationFromListener(mWrapper, key);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}

View File

@@ -1837,7 +1837,7 @@ public class NotificationManagerService extends SystemService {
* @param token The binder for the listener, to check that the caller is allowed
*/
@Override
public void snoozeNotificationFromListener(INotificationListener token, String key,
public void snoozeNotificationUntilFromListener(INotificationListener token, String key,
long snoozeUntil) {
long identity = Binder.clearCallingIdentity();
try {
@@ -1848,6 +1848,38 @@ public class NotificationManagerService extends SystemService {
}
}
/**
* Allow an INotificationListener to snooze a single notification.
*
* @param token The binder for the listener, to check that the caller is allowed
*/
@Override
public void snoozeNotificationFromListener(INotificationListener token, String key) {
long identity = Binder.clearCallingIdentity();
try {
final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
snoozeNotificationInt(key, info);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
* Allow an INotificationListener to un-snooze a single notification.
*
* @param token The binder for the listener, to check that the caller is allowed
*/
@Override
public void unsnoozeNotificationFromListener(INotificationListener token, String key) {
long identity = Binder.clearCallingIdentity();
try {
final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
unsnoozeNotificationInt(key, info);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
/**
* Allow an INotificationListener to simulate clearing (dismissing) a single notification.
*
@@ -2206,7 +2238,6 @@ public class NotificationManagerService extends SystemService {
@Override
public ComponentName getEffectsSuppressor() {
enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
}
@@ -3686,6 +3717,34 @@ public class NotificationManagerService extends SystemService {
}
}
void snoozeNotificationInt(String key, ManagedServiceInfo listener) {
String listenerName = listener == null ? null : listener.component.toShortString();
// TODO: write to event log
if (DBG) {
Slog.d(TAG, String.format("snooze event(%s, %s)", key, listenerName));
}
synchronized (mNotificationList) {
final NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
mNotificationList.remove(r);
cancelNotificationLocked(r, false, REASON_SNOOZED);
updateLightsLocked();
mSnoozeHelper.snooze(r, r.getUser().getIdentifier());
savePolicyFile();
}
}
}
void unsnoozeNotificationInt(String key, ManagedServiceInfo listener) {
String listenerName = listener == null ? null : listener.component.toShortString();
// TODO: write to event log
if (DBG) {
Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
}
mSnoozeHelper.repost(key, Binder.getCallingUid());
savePolicyFile();
}
void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
ManagedServiceInfo listener, boolean includeCurrentProfiles) {
String listenerName = listener == null ? null : listener.component.toShortString();

View File

@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.Log;
@@ -62,6 +63,8 @@ public class SnoozeHelper {
// User id : package name : notification key : record.
private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, NotificationRecord>>>
mSnoozedNotifications = new ArrayMap<>();
// notification key : package.
private ArrayMap<String, String> mPackages = new ArrayMap<>();
private Callback mCallback;
public SnoozeHelper(Context context, Callback callback,
@@ -82,10 +85,20 @@ public class SnoozeHelper {
}
/**
* Records a notification that should be snoozed until the given time and schedules an alarm
* to repost at that time.
* Snoozes a notification and schedules an alarm to repost at that time.
*/
protected void snooze(NotificationRecord record, int userId, long until) {
snooze(record, userId);
scheduleRepost(record.sbn.getPackageName(), record.getKey(), userId, until);
}
/**
* Records a snoozed notification.
*/
protected void snooze(NotificationRecord record, int userId) {
if (DEBUG) {
Slog.d(TAG, "Snoozing " + record.getKey());
}
ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
mSnoozedNotifications.get(userId);
if (records == null) {
@@ -98,13 +111,9 @@ public class SnoozeHelper {
pkgRecords.put(record.getKey(), record);
records.put(record.sbn.getPackageName(), pkgRecords);
mSnoozedNotifications.put(userId, records);
if (DEBUG) {
Slog.d(TAG, "Snoozing " + record.getKey() + " until " + new Date(until));
}
scheduleRepost(record.sbn.getPackageName(), record.getKey(), userId, until);
mPackages.put(record.getKey(), record.sbn.getPackageName());
}
protected boolean cancel(int userId, String pkg, String tag, int id) {
if (mSnoozedNotifications.containsKey(userId)) {
ArrayMap<String, NotificationRecord> recordsForPkg =
@@ -121,6 +130,7 @@ public class SnoozeHelper {
if (key != null) {
recordsForPkg.remove(key);
cancelAlarm(userId, pkg, key);
mPackages.remove(key);
return true;
}
}
@@ -145,6 +155,7 @@ public class SnoozeHelper {
int P = records.size();
for (int k = 0; k < P; k++) {
cancelAlarm(userId, snoozedPkgs.keyAt(j), records.keyAt(k));
mPackages.remove(records.keyAt(k));
}
}
}
@@ -162,6 +173,7 @@ public class SnoozeHelper {
int N = records.size();
for (int i = 0; i < N; i++) {
cancelAlarm(userId, pkg, records.keyAt(i));
mPackages.remove(records.keyAt(i));
}
return true;
}
@@ -190,8 +202,8 @@ public class SnoozeHelper {
pkgRecords.put(record.getKey(), record);
}
@VisibleForTesting
void repost(String pkg, String key, int userId) {
protected void repost(String key, int userId) {
final String pkg = mPackages.remove(key);
ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
mSnoozedNotifications.get(userId);
if (records == null) {
@@ -202,6 +214,7 @@ public class SnoozeHelper {
return;
}
final NotificationRecord record = pkgRecords.remove(key);
if (record != null) {
mCallback.repost(userId, record);
}
@@ -213,7 +226,6 @@ public class SnoozeHelper {
new Intent(REPOST_ACTION)
.setData(new Uri.Builder().scheme(REPOST_SCHEME).appendPath(key).build())
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
.putExtra(EXTRA_PKG, pkg)
.putExtra(EXTRA_KEY, key)
.putExtra(EXTRA_USER_ID, userId),
PendingIntent.FLAG_UPDATE_CURRENT);
@@ -273,8 +285,8 @@ public class SnoozeHelper {
Slog.d(TAG, "Reposting notification");
}
if (REPOST_ACTION.equals(intent.getAction())) {
repost(intent.getStringExtra(EXTRA_PKG), intent.getStringExtra(EXTRA_KEY),
intent.getIntExtra(EXTRA_USER_ID, 0));
repost(intent.getStringExtra(EXTRA_KEY), intent.getIntExtra(EXTRA_USER_ID,
UserHandle.USER_SYSTEM));
}
}
};

View File

@@ -37,6 +37,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -65,7 +66,7 @@ public class SnoozeHelperTest {
}
@Test
public void testSnooze() throws Exception {
public void testSnoozeForTime() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
mSnoozeHelper.snooze(r , UserHandle.USER_SYSTEM, 1000);
verify(mAm, times(1)).setExactAndAllowWhileIdle(
@@ -74,6 +75,16 @@ public class SnoozeHelperTest {
UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
}
@Test
public void testSnooze() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
mSnoozeHelper.snooze(r , UserHandle.USER_SYSTEM);
verify(mAm, never()).setExactAndAllowWhileIdle(
anyInt(), anyLong(), any(PendingIntent.class));
assertTrue(mSnoozeHelper.isSnoozed(
UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
}
@Test
public void testCancelByApp() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
@@ -152,7 +163,7 @@ public class SnoozeHelperTest {
mSnoozeHelper.snooze(r , UserHandle.USER_SYSTEM, 1000);
NotificationRecord r2 = getNotificationRecord("pkg", 2, "one", UserHandle.ALL);
mSnoozeHelper.snooze(r2 , UserHandle.USER_ALL, 1000);
mSnoozeHelper.repost(r.sbn.getPackageName(), r.getKey(), UserHandle.USER_SYSTEM);
mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM);
verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
}
@@ -165,7 +176,7 @@ public class SnoozeHelperTest {
mSnoozeHelper.update(UserHandle.USER_SYSTEM, r);
verify(mCallback, never()).repost(anyInt(), any(NotificationRecord.class));
mSnoozeHelper.repost(r.sbn.getPackageName(), r.getKey(), UserHandle.USER_SYSTEM);
mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM);
verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
}