Merge "Allow the ranker to autobundle notifications." into nyc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
3898db9e15
@@ -34741,6 +34741,7 @@ package android.service.notification {
|
||||
method public int getImportance();
|
||||
method public java.lang.CharSequence getImportanceExplanation();
|
||||
method public java.lang.String getKey();
|
||||
method public java.lang.String getOverrideGroupKey();
|
||||
method public int getRank();
|
||||
method public int getSuppressedVisualEffects();
|
||||
method public boolean isAmbient();
|
||||
@@ -34771,13 +34772,16 @@ package android.service.notification {
|
||||
method public int getId();
|
||||
method public java.lang.String getKey();
|
||||
method public android.app.Notification getNotification();
|
||||
method public java.lang.String getOverrideGroupKey();
|
||||
method public java.lang.String getPackageName();
|
||||
method public long getPostTime();
|
||||
method public java.lang.String getTag();
|
||||
method public android.os.UserHandle getUser();
|
||||
method public deprecated int getUserId();
|
||||
method public boolean isClearable();
|
||||
method public boolean isGroup();
|
||||
method public boolean isOngoing();
|
||||
method public void setOverrideGroupKey(java.lang.String);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.service.notification.StatusBarNotification> CREATOR;
|
||||
}
|
||||
|
||||
@@ -5076,6 +5076,7 @@ package android.app {
|
||||
field public static final java.lang.String EXTRA_THREAD_TITLE = "android.threadTitle";
|
||||
field public static final java.lang.String EXTRA_TITLE = "android.title";
|
||||
field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
|
||||
field public static final int FLAG_AUTOGROUP_SUMMARY = 1024; // 0x400
|
||||
field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
|
||||
field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
|
||||
field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
|
||||
@@ -37119,6 +37120,22 @@ package android.service.media {
|
||||
|
||||
package android.service.notification {
|
||||
|
||||
public final class Adjustment implements android.os.Parcelable {
|
||||
ctor public Adjustment(java.lang.String, java.lang.String, int, android.os.Bundle, java.lang.CharSequence, android.net.Uri);
|
||||
ctor protected Adjustment(android.os.Parcel);
|
||||
method public int describeContents();
|
||||
method public java.lang.CharSequence getExplanation();
|
||||
method public int getImportance();
|
||||
method public java.lang.String getKey();
|
||||
method public java.lang.String getPackage();
|
||||
method public android.net.Uri getReference();
|
||||
method public android.os.Bundle getSignals();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
|
||||
field public static final java.lang.String GROUP_KEY_OVERRIDE_KEY = "group_key_override";
|
||||
field public static final java.lang.String NEEDS_AUTOGROUPING_KEY = "autogroup_needed";
|
||||
}
|
||||
|
||||
public class Condition implements android.os.Parcelable {
|
||||
ctor public Condition(android.net.Uri, java.lang.String, int);
|
||||
ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
|
||||
@@ -37212,6 +37229,7 @@ package android.service.notification {
|
||||
method public int getImportance();
|
||||
method public java.lang.CharSequence getImportanceExplanation();
|
||||
method public java.lang.String getKey();
|
||||
method public java.lang.String getOverrideGroupKey();
|
||||
method public int getRank();
|
||||
method public int getSuppressedVisualEffects();
|
||||
method public boolean isAmbient();
|
||||
@@ -37235,11 +37253,12 @@ package android.service.notification {
|
||||
|
||||
public abstract class NotificationRankerService extends android.service.notification.NotificationListenerService {
|
||||
ctor public NotificationRankerService();
|
||||
method public final void adjustImportance(java.lang.String, android.service.notification.NotificationRankerService.Adjustment);
|
||||
method public final void adjustNotification(android.service.notification.Adjustment);
|
||||
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
|
||||
method public final android.os.IBinder onBind(android.content.Intent);
|
||||
method public void onNotificationActionClick(java.lang.String, long, int);
|
||||
method public void onNotificationClick(java.lang.String, long);
|
||||
method public abstract android.service.notification.NotificationRankerService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
|
||||
method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
|
||||
method public void onNotificationRemoved(java.lang.String, long, int);
|
||||
method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
|
||||
field public static final int REASON_APP_CANCEL = 8; // 0x8
|
||||
@@ -37256,14 +37275,11 @@ package android.service.notification {
|
||||
field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
|
||||
field public static final int REASON_PACKAGE_SUSPENDED = 14; // 0xe
|
||||
field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf
|
||||
field public static final int REASON_UNAUTOBUNDLED = 16; // 0x10
|
||||
field public static final int REASON_USER_STOPPED = 6; // 0x6
|
||||
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationRankerService";
|
||||
}
|
||||
|
||||
public class NotificationRankerService.Adjustment {
|
||||
ctor public NotificationRankerService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
|
||||
}
|
||||
|
||||
public class StatusBarNotification implements android.os.Parcelable {
|
||||
ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long);
|
||||
ctor public StatusBarNotification(android.os.Parcel);
|
||||
@@ -37273,13 +37289,16 @@ package android.service.notification {
|
||||
method public int getId();
|
||||
method public java.lang.String getKey();
|
||||
method public android.app.Notification getNotification();
|
||||
method public java.lang.String getOverrideGroupKey();
|
||||
method public java.lang.String getPackageName();
|
||||
method public long getPostTime();
|
||||
method public java.lang.String getTag();
|
||||
method public android.os.UserHandle getUser();
|
||||
method public deprecated int getUserId();
|
||||
method public boolean isClearable();
|
||||
method public boolean isGroup();
|
||||
method public boolean isOngoing();
|
||||
method public void setOverrideGroupKey(java.lang.String);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.service.notification.StatusBarNotification> CREATOR;
|
||||
}
|
||||
|
||||
@@ -34814,6 +34814,7 @@ package android.service.notification {
|
||||
method public int getImportance();
|
||||
method public java.lang.CharSequence getImportanceExplanation();
|
||||
method public java.lang.String getKey();
|
||||
method public java.lang.String getOverrideGroupKey();
|
||||
method public int getRank();
|
||||
method public int getSuppressedVisualEffects();
|
||||
method public boolean isAmbient();
|
||||
@@ -34844,13 +34845,16 @@ package android.service.notification {
|
||||
method public int getId();
|
||||
method public java.lang.String getKey();
|
||||
method public android.app.Notification getNotification();
|
||||
method public java.lang.String getOverrideGroupKey();
|
||||
method public java.lang.String getPackageName();
|
||||
method public long getPostTime();
|
||||
method public java.lang.String getTag();
|
||||
method public android.os.UserHandle getUser();
|
||||
method public deprecated int getUserId();
|
||||
method public boolean isClearable();
|
||||
method public boolean isGroup();
|
||||
method public boolean isOngoing();
|
||||
method public void setOverrideGroupKey(java.lang.String);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.service.notification.StatusBarNotification> CREATOR;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.content.Intent;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.service.notification.Adjustment;
|
||||
import android.service.notification.Condition;
|
||||
import android.service.notification.IConditionListener;
|
||||
import android.service.notification.IConditionProvider;
|
||||
@@ -80,7 +81,8 @@ interface INotificationManager
|
||||
void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
|
||||
void setInterruptionFilter(String pkg, int interruptionFilter);
|
||||
|
||||
void setImportanceFromRankerService(in INotificationListener token, String key, int importance, CharSequence explanation);
|
||||
void applyAdjustmentFromRankerService(in INotificationListener token, in Adjustment adjustment);
|
||||
void applyAdjustmentsFromRankerService(in INotificationListener token, in List<Adjustment> adjustments);
|
||||
|
||||
ComponentName getEffectsSuppressor();
|
||||
boolean matchesCallFilter(in Bundle extras);
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.annotation.DrawableRes;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.SdkConstant;
|
||||
import android.annotation.SdkConstant.SdkConstantType;
|
||||
import android.annotation.SystemApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
@@ -496,6 +497,15 @@ public class Notification implements Parcelable
|
||||
*/
|
||||
public static final int FLAG_GROUP_SUMMARY = 0x00000200;
|
||||
|
||||
/**
|
||||
* Bit to be bitswise-ored into the {@link #flags} field that should be
|
||||
* set if this notification is the group summary for an auto-group of notifications.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public static final int FLAG_AUTOGROUP_SUMMARY = 0x00000400;
|
||||
|
||||
public int flags;
|
||||
|
||||
/** @hide */
|
||||
@@ -1945,13 +1955,9 @@ public class Notification implements Parcelable
|
||||
* @hide
|
||||
*/
|
||||
public static void addFieldsFromContext(Context context, Notification notification) {
|
||||
if (notification.extras.getParcelable(EXTRA_BUILDER_APPLICATION_INFO) == null) {
|
||||
notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO,
|
||||
context.getApplicationInfo());
|
||||
}
|
||||
if (!notification.extras.containsKey(EXTRA_ORIGINATING_USERID)) {
|
||||
notification.extras.putInt(EXTRA_ORIGINATING_USERID, context.getUserId());
|
||||
}
|
||||
notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO,
|
||||
context.getApplicationInfo());
|
||||
notification.extras.putInt(EXTRA_ORIGINATING_USERID, context.getUserId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -3020,12 +3026,13 @@ public class Notification implements Parcelable
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void setFlag(int mask, boolean value) {
|
||||
public Builder setFlag(int mask, boolean value) {
|
||||
if (value) {
|
||||
mN.flags |= mask;
|
||||
} else {
|
||||
mN.flags &= ~mask;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
19
core/java/android/service/notification/Adjustment.aidl
Normal file
19
core/java/android/service/notification/Adjustment.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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 Adjustment;
|
||||
150
core/java/android/service/notification/Adjustment.java
Normal file
150
core/java/android/service/notification/Adjustment.java
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.SystemApi;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Ranking updates from the Ranker.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class Adjustment implements Parcelable {
|
||||
private final String mPackage;
|
||||
private final String mKey;
|
||||
private final int mImportance;
|
||||
private final CharSequence mExplanation;
|
||||
private final Uri mReference;
|
||||
private final Bundle mSignals;
|
||||
|
||||
public static final String GROUP_KEY_OVERRIDE_KEY = "group_key_override";
|
||||
public static final String NEEDS_AUTOGROUPING_KEY = "autogroup_needed";
|
||||
|
||||
/**
|
||||
* Create a notification adjustment.
|
||||
*
|
||||
* @param pkg The package of the notification.
|
||||
* @param key The notification key.
|
||||
* @param importance The recommended importance of the notification.
|
||||
* @param signals A bundle of signals that should inform notification grouping and ordering.
|
||||
* @param explanation A human-readable justification for the adjustment.
|
||||
* @param reference A reference to an external object that augments the
|
||||
* explanation, such as a
|
||||
* {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
|
||||
* or null.
|
||||
*/
|
||||
public Adjustment(String pkg, String key, int importance, Bundle signals,
|
||||
CharSequence explanation, Uri reference) {
|
||||
mPackage = pkg;
|
||||
mKey = key;
|
||||
mImportance = importance;
|
||||
mSignals = signals;
|
||||
mExplanation = explanation;
|
||||
mReference = reference;
|
||||
}
|
||||
|
||||
protected Adjustment(Parcel in) {
|
||||
if (in.readInt() == 1) {
|
||||
mPackage = in.readString();
|
||||
} else {
|
||||
mPackage = null;
|
||||
}
|
||||
if (in.readInt() == 1) {
|
||||
mKey = in.readString();
|
||||
} else {
|
||||
mKey = null;
|
||||
}
|
||||
mImportance = in.readInt();
|
||||
if (in.readInt() == 1) {
|
||||
mExplanation = in.readCharSequence();
|
||||
} else {
|
||||
mExplanation = null;
|
||||
}
|
||||
mReference = in.readParcelable(Uri.class.getClassLoader());
|
||||
mSignals = in.readBundle();
|
||||
}
|
||||
|
||||
public static final Creator<Adjustment> CREATOR = new Creator<Adjustment>() {
|
||||
@Override
|
||||
public Adjustment createFromParcel(Parcel in) {
|
||||
return new Adjustment(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Adjustment[] newArray(int size) {
|
||||
return new Adjustment[size];
|
||||
}
|
||||
};
|
||||
|
||||
public String getPackage() {
|
||||
return mPackage;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return mKey;
|
||||
}
|
||||
|
||||
public int getImportance() {
|
||||
return mImportance;
|
||||
}
|
||||
|
||||
public CharSequence getExplanation() {
|
||||
return mExplanation;
|
||||
}
|
||||
|
||||
public Uri getReference() {
|
||||
return mReference;
|
||||
}
|
||||
|
||||
public Bundle getSignals() {
|
||||
return mSignals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
if (mPackage != null) {
|
||||
dest.writeInt(1);
|
||||
dest.writeString(mPackage);
|
||||
} else {
|
||||
dest.writeInt(0);
|
||||
}
|
||||
if (mKey != null) {
|
||||
dest.writeInt(1);
|
||||
dest.writeString(mKey);
|
||||
} else {
|
||||
dest.writeInt(0);
|
||||
}
|
||||
dest.writeInt(mImportance);
|
||||
if (mExplanation != null) {
|
||||
dest.writeInt(1);
|
||||
dest.writeCharSequence(mExplanation);
|
||||
} else {
|
||||
dest.writeInt(0);
|
||||
}
|
||||
dest.writeParcelable(mReference, flags);
|
||||
dest.writeBundle(mSignals);
|
||||
}
|
||||
}
|
||||
@@ -1052,6 +1052,8 @@ public abstract class NotificationListenerService extends Service {
|
||||
private int mSuppressedVisualEffects;
|
||||
private @Importance int mImportance;
|
||||
private CharSequence mImportanceExplanation;
|
||||
// System specified group key.
|
||||
private String mOverrideGroupKey;
|
||||
|
||||
public Ranking() {}
|
||||
|
||||
@@ -1130,9 +1132,17 @@ public abstract class NotificationListenerService extends Service {
|
||||
return mImportanceExplanation;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the system has overriden the group key, then this will be non-null, and this
|
||||
* key should be used to bundle notifications.
|
||||
*/
|
||||
public String getOverrideGroupKey() {
|
||||
return mOverrideGroupKey;
|
||||
}
|
||||
|
||||
private void populate(String key, int rank, boolean matchesInterruptionFilter,
|
||||
int visibilityOverride, int suppressedVisualEffects, int importance,
|
||||
CharSequence explanation) {
|
||||
CharSequence explanation, String overrideGroupKey) {
|
||||
mKey = key;
|
||||
mRank = rank;
|
||||
mIsAmbient = importance < IMPORTANCE_LOW;
|
||||
@@ -1141,6 +1151,7 @@ public abstract class NotificationListenerService extends Service {
|
||||
mSuppressedVisualEffects = suppressedVisualEffects;
|
||||
mImportance = importance;
|
||||
mImportanceExplanation = explanation;
|
||||
mOverrideGroupKey = overrideGroupKey;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1184,6 +1195,7 @@ public abstract class NotificationListenerService extends Service {
|
||||
private ArrayMap<String, Integer> mSuppressedVisualEffects;
|
||||
private ArrayMap<String, Integer> mImportance;
|
||||
private ArrayMap<String, String> mImportanceExplanation;
|
||||
private ArrayMap<String, String> mOverrideGroupKeys;
|
||||
|
||||
private RankingMap(NotificationRankingUpdate rankingUpdate) {
|
||||
mRankingUpdate = rankingUpdate;
|
||||
@@ -1210,7 +1222,7 @@ public abstract class NotificationListenerService extends Service {
|
||||
int rank = getRank(key);
|
||||
outRanking.populate(key, rank, !isIntercepted(key),
|
||||
getVisibilityOverride(key), getSuppressedVisualEffects(key),
|
||||
getImportance(key), getImportanceExplanation(key));
|
||||
getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key));
|
||||
return rank >= 0;
|
||||
}
|
||||
|
||||
@@ -1281,6 +1293,15 @@ public abstract class NotificationListenerService extends Service {
|
||||
return mImportanceExplanation.get(key);
|
||||
}
|
||||
|
||||
private String getOverrideGroupKey(String key) {
|
||||
synchronized (this) {
|
||||
if (mOverrideGroupKeys == null) {
|
||||
buildOverrideGroupKeys();
|
||||
}
|
||||
}
|
||||
return mOverrideGroupKeys.get(key);
|
||||
}
|
||||
|
||||
// Locked by 'this'
|
||||
private void buildRanksLocked() {
|
||||
String[] orderedKeys = mRankingUpdate.getOrderedKeys();
|
||||
@@ -1335,6 +1356,15 @@ public abstract class NotificationListenerService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
// Locked by 'this'
|
||||
private void buildOverrideGroupKeys() {
|
||||
Bundle overrideGroupKeys = mRankingUpdate.getOverrideGroupKeys();
|
||||
mOverrideGroupKeys = new ArrayMap<>(overrideGroupKeys.size());
|
||||
for (String key: overrideGroupKeys.keySet()) {
|
||||
mOverrideGroupKeys.put(key, overrideGroupKeys.getString(key));
|
||||
}
|
||||
}
|
||||
|
||||
// ----------- Parcelable
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,14 +22,19 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import com.android.internal.os.SomeArgs;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A service that helps the user manage notifications. This class is only used to
|
||||
* extend the framework service and may not be implemented by non-framework components.
|
||||
@@ -91,27 +96,8 @@ public abstract class NotificationRankerService extends NotificationListenerServ
|
||||
/** Notification was canceled by the owning managed profile being turned off. */
|
||||
public static final int REASON_PROFILE_TURNED_OFF = 15;
|
||||
|
||||
public class Adjustment {
|
||||
int mImportance;
|
||||
CharSequence mExplanation;
|
||||
Uri mReference;
|
||||
|
||||
/**
|
||||
* Create a notification importance adjustment.
|
||||
*
|
||||
* @param importance The final importance of the notification.
|
||||
* @param explanation A human-readable justification for the adjustment.
|
||||
* @param reference A reference to an external object that augments the
|
||||
* explanation, such as a
|
||||
* {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
|
||||
* or null.
|
||||
*/
|
||||
public Adjustment(int importance, CharSequence explanation, Uri reference) {
|
||||
mImportance = importance;
|
||||
mExplanation = explanation;
|
||||
mReference = reference;
|
||||
}
|
||||
}
|
||||
/** Autobundled summary notification was canceled because its group was unbundled */
|
||||
public static final int REASON_UNAUTOBUNDLED = 16;
|
||||
|
||||
private Handler mHandler;
|
||||
|
||||
@@ -200,18 +186,32 @@ public abstract class NotificationRankerService extends NotificationListenerServ
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the importance of an existing notification. N.B. this won’t cause
|
||||
* Updates a notification. N.B. this won’t cause
|
||||
* an existing notification to alert, but might allow a future update to
|
||||
* this notification to alert.
|
||||
*
|
||||
* @param key the notification key
|
||||
* @param adjustment the new importance with an explanation
|
||||
* @param adjustment the adjustment with an explanation
|
||||
*/
|
||||
public final void adjustImportance(String key, Adjustment adjustment) {
|
||||
public final void adjustNotification(Adjustment adjustment) {
|
||||
if (!isBound()) return;
|
||||
try {
|
||||
getNotificationInterface().setImportanceFromRankerService(mWrapper, key,
|
||||
adjustment.mImportance, adjustment.mExplanation);
|
||||
getNotificationInterface().applyAdjustmentFromRankerService(mWrapper, adjustment);
|
||||
} catch (android.os.RemoteException ex) {
|
||||
Log.v(TAG, "Unable to contact notification manager", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates existing notifications. Re-ranking won't occur until all adjustments are applied.
|
||||
* N.B. this won’t cause an existing notification to alert, but might allow a future update to
|
||||
* these notifications to alert.
|
||||
*
|
||||
* @param adjustments a list of adjustments with explanations
|
||||
*/
|
||||
public final void adjustNotifications(List<Adjustment> adjustments) {
|
||||
if (!isBound()) return;
|
||||
try {
|
||||
getNotificationInterface().applyAdjustmentsFromRankerService(mWrapper, adjustments);
|
||||
} catch (android.os.RemoteException ex) {
|
||||
Log.v(TAG, "Unable to contact notification manager", ex);
|
||||
}
|
||||
@@ -299,7 +299,7 @@ public abstract class NotificationRankerService extends NotificationListenerServ
|
||||
args.recycle();
|
||||
Adjustment adjustment = onNotificationEnqueued(sbn, importance, user);
|
||||
if (adjustment != null) {
|
||||
adjustImportance(sbn.getKey(), adjustment);
|
||||
adjustNotification(adjustment);
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
@@ -30,16 +30,18 @@ public class NotificationRankingUpdate implements Parcelable {
|
||||
private final Bundle mSuppressedVisualEffects;
|
||||
private final int[] mImportance;
|
||||
private final Bundle mImportanceExplanation;
|
||||
private final Bundle mOverrideGroupKeys;
|
||||
|
||||
public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
|
||||
Bundle visibilityOverrides, Bundle suppressedVisualEffects,
|
||||
int[] importance, Bundle explanation) {
|
||||
int[] importance, Bundle explanation, Bundle overrideGroupKeys) {
|
||||
mKeys = keys;
|
||||
mInterceptedKeys = interceptedKeys;
|
||||
mVisibilityOverrides = visibilityOverrides;
|
||||
mSuppressedVisualEffects = suppressedVisualEffects;
|
||||
mImportance = importance;
|
||||
mImportanceExplanation = explanation;
|
||||
mOverrideGroupKeys = overrideGroupKeys;
|
||||
}
|
||||
|
||||
public NotificationRankingUpdate(Parcel in) {
|
||||
@@ -50,6 +52,7 @@ public class NotificationRankingUpdate implements Parcelable {
|
||||
mImportance = new int[mKeys.length];
|
||||
in.readIntArray(mImportance);
|
||||
mImportanceExplanation = in.readBundle();
|
||||
mOverrideGroupKeys = in.readBundle();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,6 +68,7 @@ public class NotificationRankingUpdate implements Parcelable {
|
||||
out.writeBundle(mSuppressedVisualEffects);
|
||||
out.writeIntArray(mImportance);
|
||||
out.writeBundle(mImportanceExplanation);
|
||||
out.writeBundle(mOverrideGroupKeys);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
|
||||
@@ -101,4 +105,8 @@ public class NotificationRankingUpdate implements Parcelable {
|
||||
public Bundle getImportanceExplanation() {
|
||||
return mImportanceExplanation;
|
||||
}
|
||||
|
||||
public Bundle getOverrideGroupKeys() {
|
||||
return mOverrideGroupKeys;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ public class StatusBarNotification implements Parcelable {
|
||||
private final int id;
|
||||
private final String tag;
|
||||
private final String key;
|
||||
private final String groupKey;
|
||||
private String groupKey;
|
||||
private String overrideGroupKey;
|
||||
|
||||
private final int uid;
|
||||
private final String opPkg;
|
||||
@@ -51,6 +52,27 @@ public class StatusBarNotification implements Parcelable {
|
||||
System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public StatusBarNotification(String pkg, String opPkg, int id, String tag, int uid,
|
||||
int initialPid, Notification notification, UserHandle user, String overrideGroupKey,
|
||||
long postTime) {
|
||||
if (pkg == null) throw new NullPointerException();
|
||||
if (notification == null) throw new NullPointerException();
|
||||
|
||||
this.pkg = pkg;
|
||||
this.opPkg = opPkg;
|
||||
this.id = id;
|
||||
this.tag = tag;
|
||||
this.uid = uid;
|
||||
this.initialPid = initialPid;
|
||||
this.notification = notification;
|
||||
this.user = user;
|
||||
this.postTime = postTime;
|
||||
this.overrideGroupKey = overrideGroupKey;
|
||||
this.key = key();
|
||||
this.groupKey = groupKey();
|
||||
}
|
||||
|
||||
public StatusBarNotification(String pkg, String opPkg, int id, String tag, int uid,
|
||||
int initialPid, int score, Notification notification, UserHandle user,
|
||||
long postTime) {
|
||||
@@ -84,15 +106,27 @@ public class StatusBarNotification implements Parcelable {
|
||||
this.notification = new Notification(in);
|
||||
this.user = UserHandle.readFromParcel(in);
|
||||
this.postTime = in.readLong();
|
||||
if (in.readInt() != 0) {
|
||||
this.overrideGroupKey = in.readString();
|
||||
} else {
|
||||
this.overrideGroupKey = null;
|
||||
}
|
||||
this.key = key();
|
||||
this.groupKey = groupKey();
|
||||
}
|
||||
|
||||
private String key() {
|
||||
return user.getIdentifier() + "|" + pkg + "|" + id + "|" + tag + "|" + uid;
|
||||
String sbnKey = user.getIdentifier() + "|" + pkg + "|" + id + "|" + tag + "|" + uid;
|
||||
if (overrideGroupKey != null && getNotification().isGroupSummary()) {
|
||||
sbnKey = sbnKey + "|" + overrideGroupKey;
|
||||
}
|
||||
return sbnKey;
|
||||
}
|
||||
|
||||
private String groupKey() {
|
||||
if (overrideGroupKey != null) {
|
||||
return user.getIdentifier() + "|" + pkg + "|" + "g:" + overrideGroupKey;
|
||||
}
|
||||
final String group = getNotification().getGroup();
|
||||
final String sortKey = getNotification().getSortKey();
|
||||
if (group == null && sortKey == null) {
|
||||
@@ -105,6 +139,17 @@ public class StatusBarNotification implements Parcelable {
|
||||
: "g:" + group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this notification is part of a group.
|
||||
*/
|
||||
public boolean isGroup() {
|
||||
if (overrideGroupKey != null || getNotification().getGroup() != null
|
||||
|| getNotification().getSortKey() != null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeString(this.pkg);
|
||||
out.writeString(this.opPkg);
|
||||
@@ -121,6 +166,12 @@ public class StatusBarNotification implements Parcelable {
|
||||
user.writeToParcel(out, flags);
|
||||
|
||||
out.writeLong(this.postTime);
|
||||
if (this.overrideGroupKey != null) {
|
||||
out.writeInt(1);
|
||||
out.writeString(this.overrideGroupKey);
|
||||
} else {
|
||||
out.writeInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
public int describeContents() {
|
||||
@@ -149,22 +200,22 @@ public class StatusBarNotification implements Parcelable {
|
||||
this.notification.cloneInto(no, false); // light copy
|
||||
return new StatusBarNotification(this.pkg, this.opPkg,
|
||||
this.id, this.tag, this.uid, this.initialPid,
|
||||
0, no, this.user, this.postTime);
|
||||
no, this.user, this.overrideGroupKey, this.postTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatusBarNotification clone() {
|
||||
return new StatusBarNotification(this.pkg, this.opPkg,
|
||||
this.id, this.tag, this.uid, this.initialPid,
|
||||
0, this.notification.clone(), this.user, this.postTime);
|
||||
this.notification.clone(), this.user, this.overrideGroupKey, this.postTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"StatusBarNotification(pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
|
||||
"StatusBarNotification(pkg=%s user=%s id=%d tag=%s key=%s: %s)",
|
||||
this.pkg, this.user, this.id, this.tag,
|
||||
0, this.key, this.notification);
|
||||
this.key, this.notification);
|
||||
}
|
||||
|
||||
/** Convenience method to check the notification's flags for
|
||||
@@ -257,6 +308,21 @@ public class StatusBarNotification implements Parcelable {
|
||||
return groupKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the override group key.
|
||||
*/
|
||||
public void setOverrideGroupKey(String overrideGroupKey) {
|
||||
this.overrideGroupKey = overrideGroupKey;
|
||||
groupKey = groupKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the override group key.
|
||||
*/
|
||||
public String getOverrideGroupKey() {
|
||||
return overrideGroupKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
||||
@@ -17,4 +17,5 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="app_name">Android Services Library</string>
|
||||
<string name="notification_ranker">Android Notification Ranking Service</string>
|
||||
<string name="notification_ranker_autobundle_explanation">Auto-grouping updated by Ranking Service</string>
|
||||
</resources>
|
||||
|
||||
@@ -16,16 +16,36 @@
|
||||
|
||||
package android.ext.services.notification;
|
||||
|
||||
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.service.notification.Adjustment;
|
||||
import android.service.notification.NotificationRankerService;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import android.ext.services.R;
|
||||
|
||||
/**
|
||||
* Class that provides an updatable ranker module for the notification manager..
|
||||
*/
|
||||
public final class Ranker extends NotificationRankerService {
|
||||
private static final String TAG = "RocketRanker";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);;
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
private static final int AUTOBUNDLE_AT_COUNT = 4;
|
||||
private static final String AUTOBUNDLE_KEY = "ranker_bundle";
|
||||
|
||||
// Map of package : notification keys. Only contains notifications that are not bundled
|
||||
// by the app (aka no group or sort key).
|
||||
Map<String, LinkedHashSet<String>> mUnbundledNotifications;
|
||||
|
||||
@Override
|
||||
public Adjustment onNotificationEnqueued(StatusBarNotification sbn, int importance,
|
||||
@@ -37,10 +57,146 @@ public final class Ranker extends NotificationRankerService {
|
||||
@Override
|
||||
public void onNotificationPosted(StatusBarNotification sbn) {
|
||||
if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey());
|
||||
try {
|
||||
List<String> notificationsToBundle = new ArrayList<>();
|
||||
if (!sbn.isGroup()) {
|
||||
// Not grouped by the app, add to the list of notifications for the app;
|
||||
// send bundling update if app exceeds the autobundling limit.
|
||||
synchronized (mUnbundledNotifications) {
|
||||
LinkedHashSet<String> notificationsForPackage
|
||||
= mUnbundledNotifications.get(sbn.getPackageName());
|
||||
if (notificationsForPackage == null) {
|
||||
notificationsForPackage = new LinkedHashSet<>();
|
||||
}
|
||||
if (notificationsForPackage.contains(sbn.getKey())) {
|
||||
return;
|
||||
}
|
||||
notificationsForPackage.add(sbn.getKey());
|
||||
mUnbundledNotifications.put(sbn.getPackageName(), notificationsForPackage);
|
||||
|
||||
if (notificationsForPackage.size() >= AUTOBUNDLE_AT_COUNT) {
|
||||
// Autobundle all but the most recently posted (not updated) notification.
|
||||
int count = 0;
|
||||
for (String key : notificationsForPackage) {
|
||||
if (count < notificationsForPackage.size() - 1) {
|
||||
notificationsToBundle.add(key);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (notificationsToBundle.size() > 0) {
|
||||
adjustAutobundlingSummary(sbn.getPackageName(), notificationsToBundle.get(0),
|
||||
true);
|
||||
adjustNotificationBundling(sbn.getPackageName(), notificationsToBundle, true);
|
||||
}
|
||||
} else {
|
||||
// Grouped, but not by us. Send updates to unautobundle, if we bundled it.
|
||||
maybeUnbundle(sbn, false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Failure processing new notification", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationRemoved(StatusBarNotification sbn) {
|
||||
try {
|
||||
maybeUnbundle(sbn, true);
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Error processing canceled notification", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Un-autobundles notifications that are now grouped by the app. Additionally cancels
|
||||
* autobundling if the status change of this notification resulted in the loose notification
|
||||
* count being under the limit.
|
||||
*/
|
||||
private void maybeUnbundle(StatusBarNotification sbn, boolean notificationGone) {
|
||||
List<String> notificationsToUnAutobundle = new ArrayList<>();
|
||||
boolean removeSummary = false;
|
||||
synchronized (mUnbundledNotifications) {
|
||||
LinkedHashSet<String> notificationsForPackage
|
||||
= mUnbundledNotifications.get(sbn.getPackageName());
|
||||
if (notificationsForPackage == null || notificationsForPackage.size() == 0) {
|
||||
return;
|
||||
}
|
||||
if (notificationsForPackage.remove(sbn.getKey())) {
|
||||
if (!notificationGone) {
|
||||
// Add the current notification to the unbundling list if it still exists.
|
||||
notificationsToUnAutobundle.add(sbn.getKey());
|
||||
}
|
||||
// If the status change of this notification has brought the number of loose
|
||||
// notifications back below the limit, remove the summary and un-autobundle.
|
||||
if (notificationsForPackage.size() == AUTOBUNDLE_AT_COUNT - 1) {
|
||||
removeSummary = true;
|
||||
for (String key : notificationsForPackage) {
|
||||
notificationsToUnAutobundle.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (notificationsToUnAutobundle.size() > 0) {
|
||||
if (removeSummary) {
|
||||
adjustAutobundlingSummary(sbn.getPackageName(), null, false);
|
||||
}
|
||||
adjustNotificationBundling(sbn.getPackageName(), notificationsToUnAutobundle, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListenerConnected() {
|
||||
if (DEBUG) Log.i(TAG, "CONNECTED");
|
||||
mUnbundledNotifications = new HashMap<>();
|
||||
for (StatusBarNotification sbn : getActiveNotifications()) {
|
||||
onNotificationPosted(sbn);
|
||||
}
|
||||
}
|
||||
|
||||
private void adjustAutobundlingSummary(String packageName, String key, boolean summaryNeeded) {
|
||||
Bundle signals = new Bundle();
|
||||
if (summaryNeeded) {
|
||||
signals.putBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, true);
|
||||
signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, AUTOBUNDLE_KEY);
|
||||
} else {
|
||||
signals.putBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false);
|
||||
}
|
||||
Adjustment adjustment = new Adjustment(packageName, key, IMPORTANCE_UNSPECIFIED, signals,
|
||||
getContext().getString(R.string.notification_ranker_autobundle_explanation), null);
|
||||
if (DEBUG) {
|
||||
Log.i(TAG, "Summary update for: " + packageName + " "
|
||||
+ (summaryNeeded ? "adding" : "removing"));
|
||||
}
|
||||
try {
|
||||
adjustNotification(adjustment);
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Adjustment failed", e);
|
||||
}
|
||||
|
||||
}
|
||||
private void adjustNotificationBundling(String packageName, List<String> keys, boolean bundle) {
|
||||
List<Adjustment> adjustments = new ArrayList<>();
|
||||
for (String key : keys) {
|
||||
adjustments.add(createBundlingAdjustment(packageName, key, bundle));
|
||||
if (DEBUG) Log.i(TAG, "Sending bundling adjustment for: " + key);
|
||||
}
|
||||
try {
|
||||
adjustNotifications(adjustments);
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Adjustments failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Adjustment createBundlingAdjustment(String packageName, String key, boolean bundle) {
|
||||
Bundle signals = new Bundle();
|
||||
if (bundle) {
|
||||
signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, AUTOBUNDLE_KEY);
|
||||
} else {
|
||||
signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
|
||||
}
|
||||
return new Adjustment(packageName, key, IMPORTANCE_UNSPECIFIED, signals,
|
||||
getContext().getString(R.string.notification_ranker_autobundle_explanation), null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -257,16 +258,21 @@ public class NotificationData {
|
||||
}
|
||||
|
||||
public void add(Entry entry, RankingMap ranking) {
|
||||
mEntries.put(entry.notification.getKey(), entry);
|
||||
updateRankingAndSort(ranking);
|
||||
synchronized (mEntries) {
|
||||
mEntries.put(entry.notification.getKey(), entry);
|
||||
}
|
||||
mGroupManager.onEntryAdded(entry);
|
||||
updateRankingAndSort(ranking);
|
||||
}
|
||||
|
||||
public Entry remove(String key, RankingMap ranking) {
|
||||
Entry removed = mEntries.remove(key);
|
||||
Entry removed = null;
|
||||
synchronized (mEntries) {
|
||||
removed = mEntries.remove(key);
|
||||
}
|
||||
if (removed == null) return null;
|
||||
updateRankingAndSort(ranking);
|
||||
mGroupManager.onEntryRemoved(removed);
|
||||
updateRankingAndSort(ranking);
|
||||
return removed;
|
||||
}
|
||||
|
||||
@@ -316,9 +322,30 @@ public class NotificationData {
|
||||
return Ranking.IMPORTANCE_UNSPECIFIED;
|
||||
}
|
||||
|
||||
public String getOverrideGroupKey(String key) {
|
||||
if (mRankingMap != null) {
|
||||
mRankingMap.getRanking(key, mTmpRanking);
|
||||
return mTmpRanking.getOverrideGroupKey();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void updateRankingAndSort(RankingMap ranking) {
|
||||
if (ranking != null) {
|
||||
mRankingMap = ranking;
|
||||
synchronized (mEntries) {
|
||||
final int N = mEntries.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
Entry entry = mEntries.valueAt(i);
|
||||
final StatusBarNotification oldSbn = entry.notification.clone();
|
||||
final String overrideGroupKey = getOverrideGroupKey(entry.key);
|
||||
if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
|
||||
entry.notification.setOverrideGroupKey(overrideGroupKey);
|
||||
mGroupManager.onEntryUpdated(entry, oldSbn);
|
||||
}
|
||||
//mGroupManager.onEntryBundlingUpdated(entry, getOverrideGroupKey(entry.key));
|
||||
}
|
||||
}
|
||||
}
|
||||
filterAndSort();
|
||||
}
|
||||
@@ -328,16 +355,18 @@ public class NotificationData {
|
||||
public void filterAndSort() {
|
||||
mSortedAndFiltered.clear();
|
||||
|
||||
final int N = mEntries.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
Entry entry = mEntries.valueAt(i);
|
||||
StatusBarNotification sbn = entry.notification;
|
||||
synchronized (mEntries) {
|
||||
final int N = mEntries.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
Entry entry = mEntries.valueAt(i);
|
||||
StatusBarNotification sbn = entry.notification;
|
||||
|
||||
if (shouldFilterOut(sbn)) {
|
||||
continue;
|
||||
if (shouldFilterOut(sbn)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mSortedAndFiltered.add(entry);
|
||||
}
|
||||
|
||||
mSortedAndFiltered.add(entry);
|
||||
}
|
||||
|
||||
Collections.sort(mSortedAndFiltered, mRankingComparator);
|
||||
@@ -398,16 +427,17 @@ public class NotificationData {
|
||||
NotificationData.Entry e = mSortedAndFiltered.get(active);
|
||||
dumpEntry(pw, indent, active, e);
|
||||
}
|
||||
|
||||
int M = mEntries.size();
|
||||
pw.print(indent);
|
||||
pw.println("inactive notifications: " + (M - active));
|
||||
int inactiveCount = 0;
|
||||
for (int i = 0; i < M; i++) {
|
||||
Entry entry = mEntries.valueAt(i);
|
||||
if (!mSortedAndFiltered.contains(entry)) {
|
||||
dumpEntry(pw, indent, inactiveCount, entry);
|
||||
inactiveCount++;
|
||||
synchronized (mEntries) {
|
||||
int M = mEntries.size();
|
||||
pw.print(indent);
|
||||
pw.println("inactive notifications: " + (M - active));
|
||||
int inactiveCount = 0;
|
||||
for (int i = 0; i < M; i++) {
|
||||
Entry entry = mEntries.valueAt(i);
|
||||
if (!mSortedAndFiltered.contains(entry)) {
|
||||
dumpEntry(pw, indent, inactiveCount, entry);
|
||||
inactiveCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A class to handle notifications and their corresponding groups.
|
||||
@@ -121,6 +122,15 @@ public class NotificationGroupManager implements HeadsUpManager.OnHeadsUpChanged
|
||||
}
|
||||
}
|
||||
|
||||
public void onEntryBundlingUpdated(final NotificationData.Entry updated,
|
||||
final String overrideGroupKey) {
|
||||
final StatusBarNotification oldSbn = updated.notification.clone();
|
||||
if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
|
||||
updated.notification.setOverrideGroupKey(overrideGroupKey);
|
||||
onEntryUpdated(updated, oldSbn);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSuppression(NotificationGroup group) {
|
||||
if (group == null) {
|
||||
return;
|
||||
@@ -129,7 +139,7 @@ public class NotificationGroupManager implements HeadsUpManager.OnHeadsUpChanged
|
||||
group.suppressed = group.summary != null && !group.expanded
|
||||
&& (group.children.size() == 1
|
||||
|| (group.children.size() == 0
|
||||
&& !group.summary.notification.getNotification().isGroupChild()
|
||||
&& group.summary.notification.getNotification().isGroupSummary()
|
||||
&& hasIsolatedChildren(group)));
|
||||
if (prevSuppressed != group.suppressed) {
|
||||
mListener.onGroupsChanged();
|
||||
@@ -173,7 +183,7 @@ public class NotificationGroupManager implements HeadsUpManager.OnHeadsUpChanged
|
||||
|
||||
public boolean isOnlyChildInSuppressedGroup(StatusBarNotification sbn) {
|
||||
return isGroupSuppressed(sbn.getGroupKey())
|
||||
&& sbn.getNotification().isGroupChild()
|
||||
&& !sbn.getNotification().isGroupSummary()
|
||||
&& getTotalNumberOfChildren(sbn) == 1;
|
||||
}
|
||||
|
||||
@@ -278,11 +288,12 @@ public class NotificationGroupManager implements HeadsUpManager.OnHeadsUpChanged
|
||||
}
|
||||
return sbn.getNotification().isGroupSummary();
|
||||
}
|
||||
|
||||
private boolean isGroupChild(StatusBarNotification sbn) {
|
||||
if (isIsolated(sbn)) {
|
||||
return false;
|
||||
}
|
||||
return sbn.getNotification().isGroupChild();
|
||||
return sbn.isGroup() && !sbn.getNotification().isGroupSummary();
|
||||
}
|
||||
|
||||
private String getGroupKey(StatusBarNotification sbn) {
|
||||
@@ -335,7 +346,7 @@ public class NotificationGroupManager implements HeadsUpManager.OnHeadsUpChanged
|
||||
|
||||
private boolean shouldIsolate(StatusBarNotification sbn) {
|
||||
NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
|
||||
return sbn.getNotification().isGroupChild()
|
||||
return (sbn.isGroup() && !sbn.getNotification().isGroupSummary())
|
||||
&& (sbn.getNotification().fullScreenIntent != null
|
||||
|| notificationGroup == null
|
||||
|| !notificationGroup.expanded
|
||||
|
||||
@@ -30,6 +30,7 @@ import static android.service.notification.NotificationRankerService.REASON_PACK
|
||||
import static android.service.notification.NotificationRankerService.REASON_PACKAGE_CHANGED;
|
||||
import static android.service.notification.NotificationRankerService.REASON_PACKAGE_SUSPENDED;
|
||||
import static android.service.notification.NotificationRankerService.REASON_PROFILE_TURNED_OFF;
|
||||
import static android.service.notification.NotificationRankerService.REASON_UNAUTOBUNDLED;
|
||||
import static android.service.notification.NotificationRankerService.REASON_USER_STOPPED;
|
||||
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
|
||||
import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
|
||||
@@ -39,8 +40,6 @@ import static android.service.notification.NotificationListenerService.TRIM_LIGH
|
||||
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
|
||||
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
|
||||
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
|
||||
import static org.xmlpull.v1.XmlPullParser.END_TAG;
|
||||
import static org.xmlpull.v1.XmlPullParser.START_TAG;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.Nullable;
|
||||
@@ -97,6 +96,7 @@ import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.os.Vibrator;
|
||||
import android.provider.Settings;
|
||||
import android.service.notification.Adjustment;
|
||||
import android.service.notification.Condition;
|
||||
import android.service.notification.IConditionProvider;
|
||||
import android.service.notification.INotificationListener;
|
||||
@@ -136,7 +136,6 @@ import com.android.server.notification.ManagedServices.UserProfiles;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
@@ -158,7 +157,6 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -263,6 +261,7 @@ public class NotificationManagerService extends SystemService {
|
||||
new ArrayList<NotificationRecord>();
|
||||
final ArrayMap<String, NotificationRecord> mNotificationsByKey =
|
||||
new ArrayMap<String, NotificationRecord>();
|
||||
final ArrayMap<String, String> mAutobundledSummaries = new ArrayMap<>();
|
||||
final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
|
||||
final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
|
||||
final PolicyAccess mPolicyAccess = new PolicyAccess();
|
||||
@@ -283,11 +282,6 @@ public class NotificationManagerService extends SystemService {
|
||||
private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
|
||||
private static final String ATTR_VERSION = "version";
|
||||
|
||||
// Obsolete: converted if present, but not resaved to disk.
|
||||
private static final String TAG_BLOCKED_PKGS = "blocked-packages";
|
||||
private static final String TAG_PACKAGE = "package";
|
||||
private static final String ATTR_NAME = "name";
|
||||
|
||||
private RankingHelper mRankingHelper;
|
||||
|
||||
private final UserProfiles mUserProfiles = new UserProfiles();
|
||||
@@ -1259,10 +1253,13 @@ public class NotificationManagerService extends SystemService {
|
||||
checkCallerIsSystemOrSameApp(pkg);
|
||||
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
|
||||
Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
|
||||
// Don't allow client applications to cancel foreground service notis.
|
||||
// Don't allow client applications to cancel foreground service notis or autobundled
|
||||
// summaries.
|
||||
cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
|
||||
Binder.getCallingUid() == Process.SYSTEM_UID
|
||||
? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId,
|
||||
(Binder.getCallingUid() == Process.SYSTEM_UID
|
||||
? 0 : Notification.FLAG_FOREGROUND_SERVICE)
|
||||
| (Binder.getCallingUid() == Process.SYSTEM_UID
|
||||
? 0 : Notification.FLAG_AUTOGROUP_SUMMARY), false, userId,
|
||||
REASON_APP_CANCEL, null);
|
||||
}
|
||||
|
||||
@@ -1404,7 +1401,9 @@ public class NotificationManagerService extends SystemService {
|
||||
final int N = mNotificationList.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final StatusBarNotification sbn = mNotificationList.get(i).sbn;
|
||||
if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
|
||||
if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
|
||||
&& (sbn.getNotification().flags
|
||||
& Notification.FLAG_AUTOGROUP_SUMMARY) != 0) {
|
||||
// We could pass back a cloneLight() but clients might get confused and
|
||||
// try to send this thing back to notify() again, which would not work
|
||||
// very well.
|
||||
@@ -1519,7 +1518,8 @@ public class NotificationManagerService extends SystemService {
|
||||
checkCallerIsSystemOrSameApp(component.getPackageName());
|
||||
long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
ManagedServices manager = mRankerServices.isComponentEnabledForCurrentProfiles(component)
|
||||
ManagedServices manager =
|
||||
mRankerServices.isComponentEnabledForCurrentProfiles(component)
|
||||
? mRankerServices
|
||||
: mListeners;
|
||||
manager.setComponentState(component, true);
|
||||
@@ -2035,25 +2035,123 @@ public class NotificationManagerService extends SystemService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImportanceFromRankerService(INotificationListener token, String key,
|
||||
int importance, CharSequence explanation) throws RemoteException {
|
||||
if (importance == IMPORTANCE_NONE) {
|
||||
throw new IllegalArgumentException("blocking not allowed: key=" + key);
|
||||
}
|
||||
public void applyAdjustmentFromRankerService(INotificationListener token,
|
||||
Adjustment adjustment) throws RemoteException {
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mNotificationList) {
|
||||
mRankerServices.checkServiceTokenLocked(token);
|
||||
NotificationRecord n = mNotificationsByKey.get(key);
|
||||
n.setImportance(importance, explanation);
|
||||
mRankingHandler.requestSort();
|
||||
applyAdjustmentLocked(adjustment);
|
||||
}
|
||||
maybeAddAutobundleSummary(adjustment);
|
||||
mRankingHandler.requestSort();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyAdjustmentsFromRankerService(INotificationListener token,
|
||||
List<Adjustment> adjustments) throws RemoteException {
|
||||
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mNotificationList) {
|
||||
mRankerServices.checkServiceTokenLocked(token);
|
||||
for (Adjustment adjustment : adjustments) {
|
||||
applyAdjustmentLocked(adjustment);
|
||||
}
|
||||
}
|
||||
for (Adjustment adjustment : adjustments) {
|
||||
maybeAddAutobundleSummary(adjustment);
|
||||
}
|
||||
mRankingHandler.requestSort();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void applyAdjustmentLocked(Adjustment adjustment) {
|
||||
maybeClearAutobundleSummaryLocked(adjustment);
|
||||
NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
|
||||
if (n == null) {
|
||||
return;
|
||||
}
|
||||
if (adjustment.getImportance() != IMPORTANCE_NONE) {
|
||||
n.setImportance(adjustment.getImportance(), adjustment.getExplanation());
|
||||
}
|
||||
if (adjustment.getSignals() != null) {
|
||||
Bundle.setDefusable(adjustment.getSignals(), true);
|
||||
n.sbn.setOverrideGroupKey(adjustment.getSignals().getString(
|
||||
Adjustment.GROUP_KEY_OVERRIDE_KEY, null));
|
||||
}
|
||||
}
|
||||
|
||||
// Clears the 'fake' auto-bunding summary.
|
||||
private void maybeClearAutobundleSummaryLocked(Adjustment adjustment) {
|
||||
if (adjustment.getSignals() != null
|
||||
&& adjustment.getSignals().containsKey(Adjustment.NEEDS_AUTOGROUPING_KEY)
|
||||
&& !adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
|
||||
if (mAutobundledSummaries.containsKey(adjustment.getPackage())) {
|
||||
// Clear summary.
|
||||
final NotificationRecord removed = mNotificationsByKey.get(
|
||||
mAutobundledSummaries.remove(adjustment.getPackage()));
|
||||
if (removed != null) {
|
||||
mNotificationList.remove(removed);
|
||||
cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
|
||||
private void maybeAddAutobundleSummary(Adjustment adjustment) {
|
||||
if (adjustment.getSignals() != null
|
||||
&& adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
|
||||
final String newAutoBundleKey =
|
||||
adjustment.getSignals().getString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
|
||||
int userId = -1;
|
||||
NotificationRecord summaryRecord = null;
|
||||
synchronized (mNotificationList) {
|
||||
if (!mAutobundledSummaries.containsKey(adjustment.getPackage())
|
||||
&& newAutoBundleKey != null) {
|
||||
// Add summary
|
||||
final StatusBarNotification adjustedSbn
|
||||
= mNotificationsByKey.get(adjustment.getKey()).sbn;
|
||||
|
||||
final ApplicationInfo appInfo =
|
||||
adjustedSbn.getNotification().extras.getParcelable(
|
||||
Notification.EXTRA_BUILDER_APPLICATION_INFO);
|
||||
final Bundle extras = new Bundle();
|
||||
extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
|
||||
final Notification summaryNotification =
|
||||
new Notification.Builder(getContext()).setSmallIcon(
|
||||
adjustedSbn.getNotification().getSmallIcon())
|
||||
.setGroupSummary(true)
|
||||
.setGroup(newAutoBundleKey)
|
||||
.setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
|
||||
.setFlag(Notification.FLAG_GROUP_SUMMARY, true)
|
||||
.build();
|
||||
summaryNotification.extras.putAll(extras);
|
||||
final StatusBarNotification summarySbn =
|
||||
new StatusBarNotification(adjustedSbn.getPackageName(),
|
||||
adjustedSbn.getOpPkg(),
|
||||
Integer.MAX_VALUE, Adjustment.GROUP_KEY_OVERRIDE_KEY,
|
||||
adjustedSbn.getUid(), adjustedSbn.getInitialPid(),
|
||||
summaryNotification, adjustedSbn.getUser(), newAutoBundleKey,
|
||||
System.currentTimeMillis());
|
||||
summaryRecord = new NotificationRecord(getContext(), summarySbn);
|
||||
mAutobundledSummaries.put(adjustment.getPackage(), summarySbn.getKey());
|
||||
userId = adjustedSbn.getUser().getIdentifier();
|
||||
}
|
||||
}
|
||||
if (summaryRecord != null) {
|
||||
mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String disableNotificationEffects(NotificationRecord record) {
|
||||
if (mDisableNotificationEffects) {
|
||||
return "booleanState";
|
||||
@@ -2253,6 +2351,17 @@ public class NotificationManagerService extends SystemService {
|
||||
callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
|
||||
final UserHandle user = new UserHandle(userId);
|
||||
|
||||
// Fix the notification as best we can.
|
||||
try {
|
||||
Notification.addFieldsFromContext(getContext().createApplicationContext(
|
||||
getContext().getPackageManager().getApplicationInfoAsUser(
|
||||
pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId),
|
||||
Context.CONTEXT_RESTRICTED), notification);
|
||||
} catch (NameNotFoundException e) {
|
||||
Slog.e(TAG, "Cannot create a context for sending app", e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Limit the number of notifications that any given package except the android
|
||||
// package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
|
||||
if (!isSystemNotification && !isNotificationFromListener) {
|
||||
@@ -2492,7 +2601,7 @@ public class NotificationManagerService extends SystemService {
|
||||
StatusBarNotification sbn = r.sbn;
|
||||
String group = sbn.getGroupKey();
|
||||
boolean isSummary = sbn.getNotification().isGroupSummary();
|
||||
boolean isChild = sbn.getNotification().isGroupChild();
|
||||
boolean isChild = !isSummary && sbn.isGroup();
|
||||
|
||||
NotificationRecord summary = mSummaryByGroupKey.get(group);
|
||||
if (isChild && summary != null) {
|
||||
@@ -2857,11 +2966,13 @@ public class NotificationManagerService extends SystemService {
|
||||
synchronized (mNotificationList) {
|
||||
final int N = mNotificationList.size();
|
||||
ArrayList<String> orderBefore = new ArrayList<String>(N);
|
||||
ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
|
||||
int[] visibilities = new int[N];
|
||||
int [] importances = new int[N];
|
||||
int[] importances = new int[N];
|
||||
for (int i = 0; i < N; i++) {
|
||||
final NotificationRecord r = mNotificationList.get(i);
|
||||
orderBefore.add(r.getKey());
|
||||
groupOverrideBefore.add(r.sbn.getGroupKey());
|
||||
visibilities[i] = r.getPackageVisibilityOverride();
|
||||
importances[i] = r.getImportance();
|
||||
mRankingHelper.extractSignals(r);
|
||||
@@ -2871,7 +2982,8 @@ public class NotificationManagerService extends SystemService {
|
||||
final NotificationRecord r = mNotificationList.get(i);
|
||||
if (!orderBefore.get(i).equals(r.getKey())
|
||||
|| visibilities[i] != r.getPackageVisibilityOverride()
|
||||
|| importances[i] != r.getImportance()) {
|
||||
|| importances[i] != r.getImportance()
|
||||
|| !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())) {
|
||||
scheduleSendRankingUpdate();
|
||||
return;
|
||||
}
|
||||
@@ -3070,6 +3182,7 @@ public class NotificationManagerService extends SystemService {
|
||||
mLights.remove(canceledKey);
|
||||
|
||||
// Record usage stats
|
||||
// TODO: add unbundling stats?
|
||||
switch (reason) {
|
||||
case REASON_DELEGATE_CANCEL:
|
||||
case REASON_DELEGATE_CANCEL_ALL:
|
||||
@@ -3089,6 +3202,9 @@ public class NotificationManagerService extends SystemService {
|
||||
if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
|
||||
mSummaryByGroupKey.remove(groupKey);
|
||||
}
|
||||
if (r.sbn.getKey().equals(mAutobundledSummaries.get(r.sbn.getPackageName()))) {
|
||||
mAutobundledSummaries.remove(r.sbn.getPackageName());
|
||||
}
|
||||
|
||||
// Save it for users of getHistoricalNotifications()
|
||||
mArchive.record(r.sbn);
|
||||
@@ -3287,7 +3403,7 @@ public class NotificationManagerService extends SystemService {
|
||||
for (int i = N - 1; i >= 0; i--) {
|
||||
NotificationRecord childR = mNotificationList.get(i);
|
||||
StatusBarNotification childSbn = childR.sbn;
|
||||
if (childR.getNotification().isGroupChild() &&
|
||||
if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
|
||||
childR.getGroupKey().equals(r.getGroupKey())) {
|
||||
EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
|
||||
childSbn.getTag(), userId, 0, 0, reason, listenerName);
|
||||
@@ -3438,6 +3554,7 @@ public class NotificationManagerService extends SystemService {
|
||||
ArrayList<String> keys = new ArrayList<String>(N);
|
||||
ArrayList<String> interceptedKeys = new ArrayList<String>(N);
|
||||
ArrayList<Integer> importance = new ArrayList<>(N);
|
||||
Bundle overrideGroupKeys = new Bundle();
|
||||
Bundle visibilityOverrides = new Bundle();
|
||||
Bundle suppressedVisualEffects = new Bundle();
|
||||
Bundle explanation = new Bundle();
|
||||
@@ -3461,6 +3578,7 @@ public class NotificationManagerService extends SystemService {
|
||||
!= NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
|
||||
visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
|
||||
}
|
||||
overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
|
||||
}
|
||||
final int M = keys.size();
|
||||
String[] keysAr = keys.toArray(new String[M]);
|
||||
@@ -3470,7 +3588,7 @@ public class NotificationManagerService extends SystemService {
|
||||
importanceAr[i] = importance.get(i);
|
||||
}
|
||||
return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
|
||||
suppressedVisualEffects, importanceAr, explanation);
|
||||
suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys);
|
||||
}
|
||||
|
||||
private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
|
||||
|
||||
@@ -24,18 +24,15 @@ import static android.service.notification.NotificationListenerService.Ranking.I
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.media.AudioAttributes;
|
||||
import android.os.Build;
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.NotificationListenerService;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.EventLogTags;
|
||||
|
||||
@@ -86,6 +86,155 @@ public class NotificationTestList extends TestActivity
|
||||
}
|
||||
|
||||
private Test[] mTests = new Test[] {
|
||||
new Test("Post a group") {
|
||||
public void run()
|
||||
{
|
||||
Notification n = new Notification.Builder(NotificationTestList.this)
|
||||
.setSmallIcon(R.drawable.icon2)
|
||||
.setContentTitle("Min priority group 1")
|
||||
.setLights(0xff0000ff, 1, 0)
|
||||
.setPriority(Notification.PRIORITY_MIN)
|
||||
.setGroup("group1")
|
||||
.build();
|
||||
mNM.notify(6000, n);
|
||||
n = new Notification.Builder(NotificationTestList.this)
|
||||
.setSmallIcon(R.drawable.icon2)
|
||||
.setContentTitle("low priority group 1")
|
||||
.setLights(0xff0000ff, 1, 0)
|
||||
.setPriority(Notification.PRIORITY_LOW)
|
||||
.setGroup("group1")
|
||||
.build();
|
||||
mNM.notify(6001, n);
|
||||
n = new Notification.Builder(NotificationTestList.this)
|
||||
.setSmallIcon(R.drawable.icon2)
|
||||
.setContentTitle("default priority group 1")
|
||||
.setLights(0xff0000ff, 1, 0)
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setGroup("group1")
|
||||
.build();
|
||||
mNM.notify(6002, n);
|
||||
n = new Notification.Builder(NotificationTestList.this)
|
||||
.setSmallIcon(R.drawable.icon2)
|
||||
.setContentTitle("summary group 1")
|
||||
.setLights(0xff0000ff, 1, 0)
|
||||
.setPriority(Notification.PRIORITY_MIN)
|
||||
.setGroup("group1")
|
||||
.setGroupSummary(true)
|
||||
.build();
|
||||
mNM.notify(6003, n);
|
||||
}
|
||||
},
|
||||
new Test("Post a group (2) w/o summary") {
|
||||
public void run()
|
||||
{
|
||||
Notification n = new Notification.Builder(NotificationTestList.this)
|
||||
.setSmallIcon(R.drawable.icon2)
|
||||
.setContentTitle("Min priority group 2")
|
||||
.setLights(0xff0000ff, 1, 0)
|
||||
.setPriority(Notification.PRIORITY_MIN)
|
||||
.setGroup("group2")
|
||||
.build();
|
||||
mNM.notify(6100, n);
|
||||
n = new Notification.Builder(NotificationTestList.this)
|
||||
.setSmallIcon(R.drawable.icon2)
|
||||
.setContentTitle("low priority group 2")
|
||||
.setLights(0xff0000ff, 1, 0)
|
||||
.setPriority(Notification.PRIORITY_LOW)
|
||||
.setGroup("group2")
|
||||
.build();
|
||||
mNM.notify(6101, n);
|
||||
n = new Notification.Builder(NotificationTestList.this)
|
||||
.setSmallIcon(R.drawable.icon2)
|
||||
.setContentTitle("default priority group 2")
|
||||
.setLights(0xff0000ff, 1, 0)
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setGroup("group2")
|
||||
.build();
|
||||
mNM.notify(6102, n);
|
||||
}
|
||||
},
|
||||
new Test("Summary for group 2") {
|
||||
public void run()
|
||||
{
|
||||
Notification n = new Notification.Builder(NotificationTestList.this)
|
||||
.setSmallIcon(R.drawable.icon2)
|
||||
.setContentTitle("summary group 2")
|
||||
.setLights(0xff0000ff, 1, 0)
|
||||
.setPriority(Notification.PRIORITY_MIN)
|
||||
.setGroup("group2")
|
||||
.setGroupSummary(true)
|
||||
.build();
|
||||
mNM.notify(6103, n);
|
||||
}
|
||||
},
|
||||
new Test("Group up public-secret") {
|
||||
public void run()
|
||||
{
|
||||
Notification n = new Notification.Builder(NotificationTestList.this)
|
||||
.setSmallIcon(R.drawable.icon2)
|
||||
.setContentTitle("public notification")
|
||||
.setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setVisibility(Notification.VISIBILITY_PUBLIC)
|
||||
.setGroup("public-secret")
|
||||
.build();
|
||||
mNM.notify("public", 7009, n);
|
||||
n = new Notification.Builder(NotificationTestList.this)
|
||||
.setSmallIcon(R.drawable.icon2)
|
||||
.setContentTitle("private only notification")
|
||||
.setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setVisibility(Notification.VISIBILITY_PRIVATE)
|
||||
.setGroup("public-secret")
|
||||
.build();
|
||||
mNM.notify("no public", 7010, n);
|
||||
n = new Notification.Builder(NotificationTestList.this)
|
||||
.setSmallIcon(R.drawable.icon2)
|
||||
.setContentTitle("private version of notification")
|
||||
.setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setVisibility(Notification.VISIBILITY_PRIVATE)
|
||||
.setGroup("public-secret")
|
||||
.setPublicVersion(new Notification.Builder(NotificationTestList.this)
|
||||
.setSmallIcon(R.drawable.icon2)
|
||||
.setContentTitle("public notification of private notification")
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setVisibility(Notification.VISIBILITY_PUBLIC)
|
||||
.build())
|
||||
.build();
|
||||
mNM.notify("priv with pub", 7011, n);
|
||||
n = new Notification.Builder(NotificationTestList.this)
|
||||
.setSmallIcon(R.drawable.icon2)
|
||||
.setContentTitle("secret notification")
|
||||
.setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setVisibility(Notification.VISIBILITY_SECRET)
|
||||
.setGroup("public-secret")
|
||||
.build();
|
||||
mNM.notify("secret", 7012, n);
|
||||
|
||||
Notification s = new Notification.Builder(NotificationTestList.this)
|
||||
.setSmallIcon(R.drawable.icon2)
|
||||
.setContentTitle("summary group public-secret")
|
||||
.setLights(0xff0000ff, 1, 0)
|
||||
.setPriority(Notification.PRIORITY_MIN)
|
||||
.setGroup("public-secret")
|
||||
.setGroupSummary(true)
|
||||
.build();
|
||||
mNM.notify(7113, s);
|
||||
}
|
||||
},
|
||||
new Test("Cancel priority autogroup") {
|
||||
public void run()
|
||||
{
|
||||
try {
|
||||
mNM.cancel(Integer.MAX_VALUE);
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(NotificationTestList.this, "cancel failed (yay)",
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
},
|
||||
new Test("Min priority") {
|
||||
public void run()
|
||||
{
|
||||
@@ -95,7 +244,7 @@ public class NotificationTestList extends TestActivity
|
||||
.setLights(0xff0000ff, 1, 0)
|
||||
.setPriority(Notification.PRIORITY_MIN)
|
||||
.build();
|
||||
mNM.notify(7000, n);
|
||||
mNM.notify("min", 7000, n);
|
||||
}
|
||||
},
|
||||
new Test("Min priority, high pri flag") {
|
||||
@@ -123,7 +272,7 @@ public class NotificationTestList extends TestActivity
|
||||
.setLights(0xff0000ff, 1, 0)
|
||||
.setPriority(Notification.PRIORITY_LOW)
|
||||
.build();
|
||||
mNM.notify(7002, n);
|
||||
mNM.notify("low", 7002, n);
|
||||
}
|
||||
},
|
||||
new Test("Default priority") {
|
||||
@@ -135,7 +284,7 @@ public class NotificationTestList extends TestActivity
|
||||
.setLights(0xff0000ff, 1, 0)
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.build();
|
||||
mNM.notify(7004, n);
|
||||
mNM.notify("default", 7004, n);
|
||||
}
|
||||
},
|
||||
new Test("High priority") {
|
||||
@@ -150,7 +299,7 @@ public class NotificationTestList extends TestActivity
|
||||
getPackageName() + "/raw/ringer"))
|
||||
.setPriority(Notification.PRIORITY_HIGH)
|
||||
.build();
|
||||
mNM.notify(7006, n);
|
||||
mNM.notify("high", 7006, n);
|
||||
}
|
||||
},
|
||||
new Test("Max priority") {
|
||||
@@ -166,7 +315,7 @@ public class NotificationTestList extends TestActivity
|
||||
.setPriority(Notification.PRIORITY_MAX)
|
||||
.setFullScreenIntent(makeIntent2(), false)
|
||||
.build();
|
||||
mNM.notify(7007, n);
|
||||
mNM.notify("max", 7007, n);
|
||||
}
|
||||
},
|
||||
new Test("Max priority with delay") {
|
||||
@@ -199,7 +348,7 @@ public class NotificationTestList extends TestActivity
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setVisibility(Notification.VISIBILITY_PUBLIC)
|
||||
.build();
|
||||
mNM.notify(7009, n);
|
||||
mNM.notify("public", 7009, n);
|
||||
}
|
||||
},
|
||||
new Test("private notification, no public") {
|
||||
@@ -212,7 +361,7 @@ public class NotificationTestList extends TestActivity
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setVisibility(Notification.VISIBILITY_PRIVATE)
|
||||
.build();
|
||||
mNM.notify(7010, n);
|
||||
mNM.notify("no public", 7010, n);
|
||||
}
|
||||
},
|
||||
new Test("private notification, has public") {
|
||||
@@ -231,7 +380,7 @@ public class NotificationTestList extends TestActivity
|
||||
.setVisibility(Notification.VISIBILITY_PUBLIC)
|
||||
.build())
|
||||
.build();
|
||||
mNM.notify(7011, n);
|
||||
mNM.notify("priv with pub", 7011, n);
|
||||
}
|
||||
},
|
||||
new Test("secret notification") {
|
||||
@@ -244,7 +393,7 @@ public class NotificationTestList extends TestActivity
|
||||
.setPriority(Notification.PRIORITY_DEFAULT)
|
||||
.setVisibility(Notification.VISIBILITY_SECRET)
|
||||
.build();
|
||||
mNM.notify(7012, n);
|
||||
mNM.notify("secret", 7012, n);
|
||||
}
|
||||
},
|
||||
new Test("Off") {
|
||||
|
||||
Reference in New Issue
Block a user