Merge "Dramatically simplify NotificationRankingUpdate." into qt-dev
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 3ec21e3..e02fd9f 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -42,7 +42,6 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Build;
-import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -53,7 +52,6 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.Log;
import android.widget.RemoteViews;
@@ -64,8 +62,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* A service that receives calls from the system when new notifications are
@@ -1442,7 +1440,7 @@
*/
@GuardedBy("mLock")
public final void applyUpdateLocked(NotificationRankingUpdate update) {
- mRankingMap = new RankingMap(update);
+ mRankingMap = update.getRankingMap();
}
/** @hide */
@@ -1480,14 +1478,14 @@
*/
public static final int USER_SENTIMENT_POSITIVE = 1;
- /** @hide */
+ /** @hide */
@IntDef(prefix = { "USER_SENTIMENT_" }, value = {
USER_SENTIMENT_NEGATIVE, USER_SENTIMENT_NEUTRAL, USER_SENTIMENT_POSITIVE
})
@Retention(RetentionPolicy.SOURCE)
public @interface UserSentiment {}
- private String mKey;
+ private @NonNull String mKey;
private int mRank = -1;
private boolean mIsAmbient;
private boolean mMatchesInterruptionFilter;
@@ -1512,7 +1510,70 @@
private ArrayList<CharSequence> mSmartReplies;
private boolean mCanBubble;
- public Ranking() {}
+ private static final int PARCEL_VERSION = 2;
+
+ public Ranking() { }
+
+ // You can parcel it, but it's not Parcelable
+ /** @hide */
+ @VisibleForTesting
+ public void writeToParcel(Parcel out, int flags) {
+ final long start = out.dataPosition();
+ out.writeInt(PARCEL_VERSION);
+ out.writeString(mKey);
+ out.writeInt(mRank);
+ out.writeBoolean(mIsAmbient);
+ out.writeBoolean(mMatchesInterruptionFilter);
+ out.writeInt(mVisibilityOverride);
+ out.writeInt(mSuppressedVisualEffects);
+ out.writeInt(mImportance);
+ out.writeCharSequence(mImportanceExplanation);
+ out.writeString(mOverrideGroupKey);
+ out.writeParcelable(mChannel, flags);
+ out.writeStringList(mOverridePeople);
+ out.writeTypedList(mSnoozeCriteria, flags);
+ out.writeBoolean(mShowBadge);
+ out.writeInt(mUserSentiment);
+ out.writeBoolean(mHidden);
+ out.writeLong(mLastAudiblyAlertedMs);
+ out.writeBoolean(mNoisy);
+ out.writeTypedList(mSmartActions, flags);
+ out.writeCharSequenceList(mSmartReplies);
+ out.writeBoolean(mCanBubble);
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public Ranking(Parcel in) {
+ final ClassLoader cl = getClass().getClassLoader();
+
+ final int version = in.readInt();
+ if (version != PARCEL_VERSION) {
+ throw new IllegalArgumentException("malformed Ranking parcel: " + in + " version "
+ + version + ", expected " + PARCEL_VERSION);
+ }
+ mKey = in.readString();
+ mRank = in.readInt();
+ mIsAmbient = in.readBoolean();
+ mMatchesInterruptionFilter = in.readBoolean();
+ mVisibilityOverride = in.readInt();
+ mSuppressedVisualEffects = in.readInt();
+ mImportance = in.readInt();
+ mImportanceExplanation = in.readCharSequence(); // may be null
+ mOverrideGroupKey = in.readString(); // may be null
+ mChannel = (NotificationChannel) in.readParcelable(cl); // may be null
+ mOverridePeople = in.createStringArrayList();
+ mSnoozeCriteria = in.createTypedArrayList(SnoozeCriterion.CREATOR);
+ mShowBadge = in.readBoolean();
+ mUserSentiment = in.readInt();
+ mHidden = in.readBoolean();
+ mLastAudiblyAlertedMs = in.readLong();
+ mNoisy = in.readBoolean();
+ mSmartActions = in.createTypedArrayList(Notification.Action.CREATOR);
+ mSmartReplies = in.readCharSequenceList();
+ mCanBubble = in.readBoolean();
+ }
+
/**
* Returns the key of the notification this Ranking applies to.
@@ -1737,6 +1798,31 @@
}
/**
+ * @hide
+ */
+ public void populate(Ranking other) {
+ populate(other.mKey,
+ other.mRank,
+ other.mMatchesInterruptionFilter,
+ other.mVisibilityOverride,
+ other.mSuppressedVisualEffects,
+ other.mImportance,
+ other.mImportanceExplanation,
+ other.mOverrideGroupKey,
+ other.mChannel,
+ other.mOverridePeople,
+ other.mSnoozeCriteria,
+ other.mShowBadge,
+ other.mUserSentiment,
+ other.mHidden,
+ other.mLastAudiblyAlertedMs,
+ other.mNoisy,
+ other.mSmartActions,
+ other.mSmartReplies,
+ other.mCanBubble);
+ }
+
+ /**
* {@hide}
*/
public static String importanceToString(int importance) {
@@ -1758,6 +1844,35 @@
return "UNKNOWN(" + String.valueOf(importance) + ")";
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Ranking other = (Ranking) o;
+ return Objects.equals(mKey, other.mKey)
+ && Objects.equals(mRank, other.mRank)
+ && Objects.equals(mMatchesInterruptionFilter, other.mMatchesInterruptionFilter)
+ && Objects.equals(mVisibilityOverride, other.mVisibilityOverride)
+ && Objects.equals(mSuppressedVisualEffects, other.mSuppressedVisualEffects)
+ && Objects.equals(mImportance, other.mImportance)
+ && Objects.equals(mImportanceExplanation, other.mImportanceExplanation)
+ && Objects.equals(mOverrideGroupKey, other.mOverrideGroupKey)
+ && Objects.equals(mChannel, other.mChannel)
+ && Objects.equals(mOverridePeople, other.mOverridePeople)
+ && Objects.equals(mSnoozeCriteria, other.mSnoozeCriteria)
+ && Objects.equals(mShowBadge, other.mShowBadge)
+ && Objects.equals(mUserSentiment, other.mUserSentiment)
+ && Objects.equals(mHidden, other.mHidden)
+ && Objects.equals(mLastAudiblyAlertedMs, other.mLastAudiblyAlertedMs)
+ && Objects.equals(mNoisy, other.mNoisy)
+ // Action.equals() doesn't exist so let's just compare list lengths
+ && ((mSmartActions == null ? 0 : mSmartActions.size())
+ == (other.mSmartActions == null ? 0 : other.mSmartActions.size()))
+ && Objects.equals(mSmartReplies, other.mSmartReplies)
+ && Objects.equals(mCanBubble, other.mCanBubble);
+ }
}
/**
@@ -1769,30 +1884,74 @@
* notifications active at the time of retrieval.
*/
public static class RankingMap implements Parcelable {
- private final NotificationRankingUpdate mRankingUpdate;
- private ArrayMap<String,Integer> mRanks;
- private ArraySet<Object> mIntercepted;
- private ArrayMap<String, Integer> mVisibilityOverrides;
- private ArrayMap<String, Integer> mSuppressedVisualEffects;
- private ArrayMap<String, Integer> mImportance;
- private ArrayMap<String, String> mImportanceExplanation;
- private ArrayMap<String, String> mOverrideGroupKeys;
- private ArrayMap<String, NotificationChannel> mChannels;
- private ArrayMap<String, ArrayList<String>> mOverridePeople;
- private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria;
- private ArrayMap<String, Boolean> mShowBadge;
- private ArrayMap<String, Integer> mUserSentiment;
- private ArrayMap<String, Boolean> mHidden;
- private ArrayMap<String, Long> mLastAudiblyAlerted;
- private ArrayMap<String, Boolean> mNoisy;
- private ArrayMap<String, ArrayList<Notification.Action>> mSmartActions;
- private ArrayMap<String, ArrayList<CharSequence>> mSmartReplies;
- private boolean[] mCanBubble;
+ private ArrayList<String> mOrderedKeys = new ArrayList<>();
+ // Note: all String keys should be intern'd as pointers into mOrderedKeys
+ private ArrayMap<String, Ranking> mRankings = new ArrayMap<>();
- private RankingMap(NotificationRankingUpdate rankingUpdate) {
- mRankingUpdate = rankingUpdate;
+ /**
+ * @hide
+ */
+ public RankingMap(Ranking[] rankings) {
+ for (int i = 0; i < rankings.length; i++) {
+ final String key = rankings[i].getKey();
+ mOrderedKeys.add(key);
+ mRankings.put(key, rankings[i]);
+ }
}
+ // -- parcelable interface --
+
+ private RankingMap(Parcel in) {
+ final ClassLoader cl = getClass().getClassLoader();
+ final int count = in.readInt();
+ mOrderedKeys.ensureCapacity(count);
+ mRankings.ensureCapacity(count);
+ for (int i = 0; i < count; i++) {
+ final Ranking r = new Ranking(in);
+ final String key = r.getKey();
+ mOrderedKeys.add(key);
+ mRankings.put(key, r);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ RankingMap other = (RankingMap) o;
+
+ return mOrderedKeys.equals(other.mOrderedKeys)
+ && mRankings.equals(other.mRankings);
+
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ final int count = mOrderedKeys.size();
+ out.writeInt(count);
+ for (int i = 0; i < count; i++) {
+ mRankings.get(mOrderedKeys.get(i)).writeToParcel(out, flags);
+ }
+ }
+
+ public static final @android.annotation.NonNull Creator<RankingMap> CREATOR = new Creator<RankingMap>() {
+ @Override
+ public RankingMap createFromParcel(Parcel source) {
+ return new RankingMap(source);
+ }
+
+ @Override
+ public RankingMap[] newArray(int size) {
+ return new RankingMap[size];
+ }
+ };
+
/**
* Request the list of notification keys in their current ranking
* order.
@@ -1800,7 +1959,7 @@
* @return An array of active notification keys, in their ranking order.
*/
public String[] getOrderedKeys() {
- return mRankingUpdate.getOrderedKeys();
+ return mOrderedKeys.toArray(new String[0]);
}
/**
@@ -1808,381 +1967,26 @@
* with the given key.
*
* @return true if a valid key has been passed and outRanking has
- * been populated; false otherwise
+ * been populated; false otherwise
*/
public boolean getRanking(String key, Ranking outRanking) {
- int rank = getRank(key);
- outRanking.populate(key, rank, !isIntercepted(key),
- getVisibilityOverride(key), getSuppressedVisualEffects(key),
- getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
- getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
- getShowBadge(key), getUserSentiment(key), getHidden(key),
- getLastAudiblyAlerted(key), getNoisy(key), getSmartActions(key),
- getSmartReplies(key), canBubble(key));
- return rank >= 0;
- }
-
- private int getRank(String key) {
- synchronized (this) {
- if (mRanks == null) {
- buildRanksLocked();
- }
+ if (mRankings.containsKey(key)) {
+ outRanking.populate(mRankings.get(key));
+ return true;
}
- Integer rank = mRanks.get(key);
- return rank != null ? rank : -1;
+ return false;
}
- private boolean isIntercepted(String key) {
- synchronized (this) {
- if (mIntercepted == null) {
- buildInterceptedSetLocked();
- }
- }
- return mIntercepted.contains(key);
+ /**
+ * Get a reference to the actual Ranking object corresponding to the key.
+ * Used only by unit tests.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public Ranking getRawRankingObject(String key) {
+ return mRankings.get(key);
}
-
- private int getVisibilityOverride(String key) {
- synchronized (this) {
- if (mVisibilityOverrides == null) {
- buildVisibilityOverridesLocked();
- }
- }
- Integer override = mVisibilityOverrides.get(key);
- if (override == null) {
- return Ranking.VISIBILITY_NO_OVERRIDE;
- }
- return override.intValue();
- }
-
- private int getSuppressedVisualEffects(String key) {
- synchronized (this) {
- if (mSuppressedVisualEffects == null) {
- buildSuppressedVisualEffectsLocked();
- }
- }
- Integer suppressed = mSuppressedVisualEffects.get(key);
- if (suppressed == null) {
- return 0;
- }
- return suppressed.intValue();
- }
-
- private int getImportance(String key) {
- synchronized (this) {
- if (mImportance == null) {
- buildImportanceLocked();
- }
- }
- Integer importance = mImportance.get(key);
- if (importance == null) {
- return NotificationManager.IMPORTANCE_DEFAULT;
- }
- return importance.intValue();
- }
-
- private String getImportanceExplanation(String key) {
- synchronized (this) {
- if (mImportanceExplanation == null) {
- buildImportanceExplanationLocked();
- }
- }
- return mImportanceExplanation.get(key);
- }
-
- private String getOverrideGroupKey(String key) {
- synchronized (this) {
- if (mOverrideGroupKeys == null) {
- buildOverrideGroupKeys();
- }
- }
- return mOverrideGroupKeys.get(key);
- }
-
- private NotificationChannel getChannel(String key) {
- synchronized (this) {
- if (mChannels == null) {
- buildChannelsLocked();
- }
- }
- return mChannels.get(key);
- }
-
- private ArrayList<String> getOverridePeople(String key) {
- synchronized (this) {
- if (mOverridePeople == null) {
- buildOverridePeopleLocked();
- }
- }
- return mOverridePeople.get(key);
- }
-
- private ArrayList<SnoozeCriterion> getSnoozeCriteria(String key) {
- synchronized (this) {
- if (mSnoozeCriteria == null) {
- buildSnoozeCriteriaLocked();
- }
- }
- return mSnoozeCriteria.get(key);
- }
-
- private boolean getShowBadge(String key) {
- synchronized (this) {
- if (mShowBadge == null) {
- buildShowBadgeLocked();
- }
- }
- Boolean showBadge = mShowBadge.get(key);
- return showBadge == null ? false : showBadge.booleanValue();
- }
-
- private int getUserSentiment(String key) {
- synchronized (this) {
- if (mUserSentiment == null) {
- buildUserSentimentLocked();
- }
- }
- Integer userSentiment = mUserSentiment.get(key);
- return userSentiment == null
- ? Ranking.USER_SENTIMENT_NEUTRAL : userSentiment.intValue();
- }
-
- private boolean getHidden(String key) {
- synchronized (this) {
- if (mHidden == null) {
- buildHiddenLocked();
- }
- }
- Boolean hidden = mHidden.get(key);
- return hidden == null ? false : hidden.booleanValue();
- }
-
- private long getLastAudiblyAlerted(String key) {
- synchronized (this) {
- if (mLastAudiblyAlerted == null) {
- buildLastAudiblyAlertedLocked();
- }
- }
- Long lastAudibleAlerted = mLastAudiblyAlerted.get(key);
- return lastAudibleAlerted == null ? -1 : lastAudibleAlerted.longValue();
- }
-
- private boolean getNoisy(String key) {
- synchronized (this) {
- if (mNoisy == null) {
- buildNoisyLocked();
- }
- }
- Boolean noisy = mNoisy.get(key);
- return noisy == null ? false : noisy.booleanValue();
- }
-
- private ArrayList<Notification.Action> getSmartActions(String key) {
- synchronized (this) {
- if (mSmartActions == null) {
- buildSmartActions();
- }
- }
- return mSmartActions.get(key);
- }
-
- private ArrayList<CharSequence> getSmartReplies(String key) {
- synchronized (this) {
- if (mSmartReplies == null) {
- buildSmartReplies();
- }
- }
- return mSmartReplies.get(key);
- }
-
- private boolean canBubble(String key) {
- synchronized (this) {
- if (mRanks == null) {
- buildRanksLocked();
- }
- if (mCanBubble == null) {
- mCanBubble = mRankingUpdate.getCanBubble();
- }
- }
- int keyIndex = mRanks.getOrDefault(key, -1);
- return keyIndex >= 0 ? mCanBubble[keyIndex] : false;
- }
-
- // Locked by 'this'
- private void buildRanksLocked() {
- String[] orderedKeys = mRankingUpdate.getOrderedKeys();
- mRanks = new ArrayMap<>(orderedKeys.length);
- for (int i = 0; i < orderedKeys.length; i++) {
- String key = orderedKeys[i];
- mRanks.put(key, i);
- }
- }
-
- // Locked by 'this'
- private void buildInterceptedSetLocked() {
- String[] dndInterceptedKeys = mRankingUpdate.getInterceptedKeys();
- mIntercepted = new ArraySet<>(dndInterceptedKeys.length);
- Collections.addAll(mIntercepted, dndInterceptedKeys);
- }
-
- private ArrayMap<String, Integer> buildIntMapFromBundle(Bundle bundle) {
- ArrayMap<String, Integer> newMap = new ArrayMap<>(bundle.size());
- for (String key : bundle.keySet()) {
- newMap.put(key, bundle.getInt(key));
- }
- return newMap;
- }
-
- private ArrayMap<String, String> buildStringMapFromBundle(Bundle bundle) {
- ArrayMap<String, String> newMap = new ArrayMap<>(bundle.size());
- for (String key : bundle.keySet()) {
- newMap.put(key, bundle.getString(key));
- }
- return newMap;
- }
-
- private ArrayMap<String, Boolean> buildBooleanMapFromBundle(Bundle bundle) {
- ArrayMap<String, Boolean> newMap = new ArrayMap<>(bundle.size());
- for (String key : bundle.keySet()) {
- newMap.put(key, bundle.getBoolean(key));
- }
- return newMap;
- }
-
- private ArrayMap<String, Long> buildLongMapFromBundle(Bundle bundle) {
- ArrayMap<String, Long> newMap = new ArrayMap<>(bundle.size());
- for (String key : bundle.keySet()) {
- newMap.put(key, bundle.getLong(key));
- }
- return newMap;
- }
-
- // Locked by 'this'
- private void buildVisibilityOverridesLocked() {
- mVisibilityOverrides = buildIntMapFromBundle(mRankingUpdate.getVisibilityOverrides());
- }
-
- // Locked by 'this'
- private void buildSuppressedVisualEffectsLocked() {
- mSuppressedVisualEffects =
- buildIntMapFromBundle(mRankingUpdate.getSuppressedVisualEffects());
- }
-
- // Locked by 'this'
- private void buildImportanceLocked() {
- String[] orderedKeys = mRankingUpdate.getOrderedKeys();
- int[] importance = mRankingUpdate.getImportance();
- mImportance = new ArrayMap<>(orderedKeys.length);
- for (int i = 0; i < orderedKeys.length; i++) {
- String key = orderedKeys[i];
- mImportance.put(key, importance[i]);
- }
- }
-
- // Locked by 'this'
- private void buildImportanceExplanationLocked() {
- mImportanceExplanation =
- buildStringMapFromBundle(mRankingUpdate.getImportanceExplanation());
- }
-
- // Locked by 'this'
- private void buildOverrideGroupKeys() {
- mOverrideGroupKeys = buildStringMapFromBundle(mRankingUpdate.getOverrideGroupKeys());
- }
-
- // Locked by 'this'
- private void buildChannelsLocked() {
- Bundle channels = mRankingUpdate.getChannels();
- mChannels = new ArrayMap<>(channels.size());
- for (String key : channels.keySet()) {
- mChannels.put(key, channels.getParcelable(key));
- }
- }
-
- // Locked by 'this'
- private void buildOverridePeopleLocked() {
- Bundle overridePeople = mRankingUpdate.getOverridePeople();
- mOverridePeople = new ArrayMap<>(overridePeople.size());
- for (String key : overridePeople.keySet()) {
- mOverridePeople.put(key, overridePeople.getStringArrayList(key));
- }
- }
-
- // Locked by 'this'
- private void buildSnoozeCriteriaLocked() {
- Bundle snoozeCriteria = mRankingUpdate.getSnoozeCriteria();
- mSnoozeCriteria = new ArrayMap<>(snoozeCriteria.size());
- for (String key : snoozeCriteria.keySet()) {
- mSnoozeCriteria.put(key, snoozeCriteria.getParcelableArrayList(key));
- }
- }
-
- // Locked by 'this'
- private void buildShowBadgeLocked() {
- mShowBadge = buildBooleanMapFromBundle(mRankingUpdate.getShowBadge());
- }
-
- // Locked by 'this'
- private void buildUserSentimentLocked() {
- mUserSentiment = buildIntMapFromBundle(mRankingUpdate.getUserSentiment());
- }
-
- // Locked by 'this'
- private void buildHiddenLocked() {
- mHidden = buildBooleanMapFromBundle(mRankingUpdate.getHidden());
- }
-
- // Locked by 'this'
- private void buildLastAudiblyAlertedLocked() {
- mLastAudiblyAlerted = buildLongMapFromBundle(mRankingUpdate.getLastAudiblyAlerted());
- }
-
- // Locked by 'this'
- private void buildNoisyLocked() {
- mNoisy = buildBooleanMapFromBundle(mRankingUpdate.getNoisy());
- }
-
- // Locked by 'this'
- private void buildSmartActions() {
- Bundle smartActions = mRankingUpdate.getSmartActions();
- mSmartActions = new ArrayMap<>(smartActions.size());
- for (String key : smartActions.keySet()) {
- mSmartActions.put(key, smartActions.getParcelableArrayList(key));
- }
- }
-
- // Locked by 'this'
- private void buildSmartReplies() {
- Bundle smartReplies = mRankingUpdate.getSmartReplies();
- mSmartReplies = new ArrayMap<>(smartReplies.size());
- for (String key : smartReplies.keySet()) {
- mSmartReplies.put(key, smartReplies.getCharSequenceArrayList(key));
- }
- }
-
- // ----------- Parcelable
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeParcelable(mRankingUpdate, flags);
- }
-
- public static final @android.annotation.NonNull Creator<RankingMap> CREATOR = new Creator<RankingMap>() {
- @Override
- public RankingMap createFromParcel(Parcel source) {
- NotificationRankingUpdate rankingUpdate = source.readParcelable(null);
- return new RankingMap(rankingUpdate);
- }
-
- @Override
- public RankingMap[] newArray(int size) {
- return new RankingMap[size];
- }
- };
}
private final class MyHandler extends Handler {
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index c5c70f8..675c5cd 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -15,7 +15,6 @@
*/
package android.service.notification;
-import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,73 +22,18 @@
* @hide
*/
public class NotificationRankingUpdate implements Parcelable {
- // TODO: Support incremental updates.
- private final String[] mKeys;
- private final String[] mInterceptedKeys;
- private final Bundle mVisibilityOverrides;
- private final Bundle mSuppressedVisualEffects;
- private final int[] mImportance;
- private final Bundle mImportanceExplanation;
- private final Bundle mOverrideGroupKeys;
- private final Bundle mChannels;
- private final Bundle mOverridePeople;
- private final Bundle mSnoozeCriteria;
- private final Bundle mShowBadge;
- private final Bundle mUserSentiment;
- private final Bundle mHidden;
- private final Bundle mSmartActions;
- private final Bundle mSmartReplies;
- private final Bundle mLastAudiblyAlerted;
- private final Bundle mNoisy;
- private final boolean[] mCanBubble;
+ private final NotificationListenerService.RankingMap mRankingMap;
- public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
- Bundle visibilityOverrides, Bundle suppressedVisualEffects,
- int[] importance, Bundle explanation, Bundle overrideGroupKeys,
- Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
- Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions,
- Bundle smartReplies, Bundle lastAudiblyAlerted, Bundle noisy, boolean[] canBubble) {
- mKeys = keys;
- mInterceptedKeys = interceptedKeys;
- mVisibilityOverrides = visibilityOverrides;
- mSuppressedVisualEffects = suppressedVisualEffects;
- mImportance = importance;
- mImportanceExplanation = explanation;
- mOverrideGroupKeys = overrideGroupKeys;
- mChannels = channels;
- mOverridePeople = overridePeople;
- mSnoozeCriteria = snoozeCriteria;
- mShowBadge = showBadge;
- mUserSentiment = userSentiment;
- mHidden = hidden;
- mSmartActions = smartActions;
- mSmartReplies = smartReplies;
- mLastAudiblyAlerted = lastAudiblyAlerted;
- mNoisy = noisy;
- mCanBubble = canBubble;
+ public NotificationRankingUpdate(NotificationListenerService.Ranking[] rankings) {
+ mRankingMap = new NotificationListenerService.RankingMap(rankings);
}
public NotificationRankingUpdate(Parcel in) {
- mKeys = in.readStringArray();
- mInterceptedKeys = in.readStringArray();
- mVisibilityOverrides = in.readBundle();
- mSuppressedVisualEffects = in.readBundle();
- mImportance = new int[mKeys.length];
- in.readIntArray(mImportance);
- mImportanceExplanation = in.readBundle();
- mOverrideGroupKeys = in.readBundle();
- mChannels = in.readBundle();
- mOverridePeople = in.readBundle();
- mSnoozeCriteria = in.readBundle();
- mShowBadge = in.readBundle();
- mUserSentiment = in.readBundle();
- mHidden = in.readBundle();
- mSmartActions = in.readBundle();
- mSmartReplies = in.readBundle();
- mLastAudiblyAlerted = in.readBundle();
- mNoisy = in.readBundle();
- mCanBubble = new boolean[mKeys.length];
- in.readBooleanArray(mCanBubble);
+ mRankingMap = in.readParcelable(getClass().getClassLoader());
+ }
+
+ public NotificationListenerService.RankingMap getRankingMap() {
+ return mRankingMap;
}
@Override
@@ -98,25 +42,17 @@
}
@Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ NotificationRankingUpdate other = (NotificationRankingUpdate) o;
+ return mRankingMap.equals(other.mRankingMap);
+ }
+
+ @Override
public void writeToParcel(Parcel out, int flags) {
- out.writeStringArray(mKeys);
- out.writeStringArray(mInterceptedKeys);
- out.writeBundle(mVisibilityOverrides);
- out.writeBundle(mSuppressedVisualEffects);
- out.writeIntArray(mImportance);
- out.writeBundle(mImportanceExplanation);
- out.writeBundle(mOverrideGroupKeys);
- out.writeBundle(mChannels);
- out.writeBundle(mOverridePeople);
- out.writeBundle(mSnoozeCriteria);
- out.writeBundle(mShowBadge);
- out.writeBundle(mUserSentiment);
- out.writeBundle(mHidden);
- out.writeBundle(mSmartActions);
- out.writeBundle(mSmartReplies);
- out.writeBundle(mLastAudiblyAlerted);
- out.writeBundle(mNoisy);
- out.writeBooleanArray(mCanBubble);
+ out.writeParcelable(mRankingMap, flags);
}
public static final @android.annotation.NonNull Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -129,76 +65,4 @@
return new NotificationRankingUpdate[size];
}
};
-
- public String[] getOrderedKeys() {
- return mKeys;
- }
-
- public String[] getInterceptedKeys() {
- return mInterceptedKeys;
- }
-
- public Bundle getVisibilityOverrides() {
- return mVisibilityOverrides;
- }
-
- public Bundle getSuppressedVisualEffects() {
- return mSuppressedVisualEffects;
- }
-
- public int[] getImportance() {
- return mImportance;
- }
-
- public Bundle getImportanceExplanation() {
- return mImportanceExplanation;
- }
-
- public Bundle getOverrideGroupKeys() {
- return mOverrideGroupKeys;
- }
-
- public Bundle getChannels() {
- return mChannels;
- }
-
- public Bundle getOverridePeople() {
- return mOverridePeople;
- }
-
- public Bundle getSnoozeCriteria() {
- return mSnoozeCriteria;
- }
-
- public Bundle getShowBadge() {
- return mShowBadge;
- }
-
- public Bundle getUserSentiment() {
- return mUserSentiment;
- }
-
- public Bundle getHidden() {
- return mHidden;
- }
-
- public Bundle getSmartActions() {
- return mSmartActions;
- }
-
- public Bundle getSmartReplies() {
- return mSmartReplies;
- }
-
- public Bundle getLastAudiblyAlerted() {
- return mLastAudiblyAlerted;
- }
-
- public Bundle getNoisy() {
- return mNoisy;
- }
-
- public boolean[] getCanBubble() {
- return mCanBubble;
- }
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 8485f46..82b16de 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7234,72 +7234,42 @@
@GuardedBy("mNotificationLock")
private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
final int N = mNotificationList.size();
- 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();
- Bundle channels = new Bundle();
- Bundle overridePeople = new Bundle();
- Bundle snoozeCriteria = new Bundle();
- Bundle showBadge = new Bundle();
- Bundle userSentiment = new Bundle();
- Bundle hidden = new Bundle();
- Bundle systemGeneratedSmartActions = new Bundle();
- Bundle smartReplies = new Bundle();
- Bundle lastAudiblyAlerted = new Bundle();
- Bundle noisy = new Bundle();
- ArrayList<Boolean> canBubble = new ArrayList<>(N);
+ final ArrayList<NotificationListenerService.Ranking> rankings = new ArrayList<>();
+
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
if (!isVisibleToListener(record.sbn, info)) {
continue;
}
final String key = record.sbn.getKey();
- keys.add(key);
- importance.add(record.getImportance());
- if (record.getImportanceExplanation() != null) {
- explanation.putCharSequence(key, record.getImportanceExplanation());
- }
- if (record.isIntercepted()) {
- interceptedKeys.add(key);
+ final NotificationListenerService.Ranking ranking =
+ new NotificationListenerService.Ranking();
+ ranking.populate(
+ key,
+ rankings.size(),
+ !record.isIntercepted(),
+ record.getPackageVisibilityOverride(),
+ record.getSuppressedVisualEffects(),
+ record.getImportance(),
+ record.getImportanceExplanation(),
+ record.sbn.getOverrideGroupKey(),
+ record.getChannel(),
+ record.getPeopleOverride(),
+ record.getSnoozeCriteria(),
+ record.canShowBadge(),
+ record.getUserSentiment(),
+ record.isHidden(),
+ record.getLastAudiblyAlertedMs(),
+ record.getSound() != null || record.getVibration() != null,
+ record.getSystemGeneratedSmartActions(),
+ record.getSmartReplies(),
+ record.canBubble()
+ );
+ rankings.add(ranking);
+ }
- }
- suppressedVisualEffects.putInt(key, record.getSuppressedVisualEffects());
- if (record.getPackageVisibilityOverride()
- != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
- visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
- }
- overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
- channels.putParcelable(key, record.getChannel());
- overridePeople.putStringArrayList(key, record.getPeopleOverride());
- snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
- showBadge.putBoolean(key, record.canShowBadge());
- userSentiment.putInt(key, record.getUserSentiment());
- hidden.putBoolean(key, record.isHidden());
- systemGeneratedSmartActions.putParcelableArrayList(key,
- record.getSystemGeneratedSmartActions());
- smartReplies.putCharSequenceArrayList(key, record.getSmartReplies());
- lastAudiblyAlerted.putLong(key, record.getLastAudiblyAlertedMs());
- noisy.putBoolean(key, record.getSound() != null || record.getVibration() != null);
- canBubble.add(record.canBubble());
- }
- final int M = keys.size();
- String[] keysAr = keys.toArray(new String[M]);
- String[] interceptedKeysAr = interceptedKeys.toArray(new String[interceptedKeys.size()]);
- int[] importanceAr = new int[M];
- boolean[] canBubbleAr = new boolean[M];
- for (int i = 0; i < M; i++) {
- importanceAr[i] = importance.get(i);
- canBubbleAr[i] = canBubble.get(i);
- }
- return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
- suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
- channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden,
- systemGeneratedSmartActions, smartReplies, lastAudiblyAlerted, noisy,
- canBubbleAr);
+ return new NotificationRankingUpdate(
+ rankings.toArray(new NotificationListenerService.Ranking[0]));
}
boolean hasCompanionDevice(ManagedServiceInfo info) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 52c199a3..bee3b2b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -20,7 +20,9 @@
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -33,10 +35,11 @@
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Binder;
-import android.os.Bundle;
import android.os.IBinder;
+import android.os.Parcel;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.SnoozeCriterion;
import android.test.suitebuilder.annotation.SmallTest;
@@ -55,8 +58,6 @@
@RunWith(AndroidJUnit4.class)
public class NotificationListenerServiceTest extends UiServiceTestCase {
- private String[] mKeys = new String[] { "key", "key1", "key2", "key3", "key4"};
-
@Test
public void testGetActiveNotifications_notNull() throws Exception {
TestListenerService service = new TestListenerService();
@@ -97,52 +98,144 @@
}
}
- private NotificationRankingUpdate generateUpdate() {
- List<String> interceptedKeys = new ArrayList<>();
- Bundle visibilityOverrides = new Bundle();
- Bundle overrideGroupKeys = new Bundle();
- Bundle suppressedVisualEffects = new Bundle();
- Bundle explanation = new Bundle();
- Bundle channels = new Bundle();
- Bundle overridePeople = new Bundle();
- Bundle snoozeCriteria = new Bundle();
- Bundle showBadge = new Bundle();
- int[] importance = new int[mKeys.length];
- Bundle userSentiment = new Bundle();
- Bundle mHidden = new Bundle();
- Bundle smartActions = new Bundle();
- Bundle smartReplies = new Bundle();
- Bundle lastAudiblyAlerted = new Bundle();
- Bundle noisy = new Bundle();
- boolean[] canBubble = new boolean[mKeys.length];
+ // Tests parceling of NotificationRankingUpdate, and by extension, RankingMap and Ranking.
+ @Test
+ public void testRankingUpdate_parcel() {
+ NotificationRankingUpdate nru = generateUpdate();
+ Parcel parcel = Parcel.obtain();
+ nru.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ NotificationRankingUpdate nru1 = NotificationRankingUpdate.CREATOR.createFromParcel(parcel);
+ assertEquals(nru, nru1);
+ }
- for (int i = 0; i < mKeys.length; i++) {
- String key = mKeys[i];
- visibilityOverrides.putInt(key, getVisibilityOverride(i));
- overrideGroupKeys.putString(key, getOverrideGroupKey(key));
- if (isIntercepted(i)) {
- interceptedKeys.add(key);
- }
- suppressedVisualEffects.putInt(key, getSuppressedVisualEffects(i));
- importance[i] = getImportance(i);
- explanation.putString(key, getExplanation(key));
- channels.putParcelable(key, getChannel(key, i));
- overridePeople.putStringArrayList(key, getPeople(key, i));
- snoozeCriteria.putParcelableArrayList(key, getSnoozeCriteria(key, i));
- showBadge.putBoolean(key, getShowBadge(i));
- userSentiment.putInt(key, getUserSentiment(i));
- mHidden.putBoolean(key, getHidden(i));
- smartActions.putParcelableArrayList(key, getSmartActions(key, i));
- smartReplies.putCharSequenceArrayList(key, getSmartReplies(key, i));
- lastAudiblyAlerted.putLong(key, lastAudiblyAlerted(i));
- noisy.putBoolean(key, getNoisy(i));
- canBubble[i] = canBubble(i);
+ private void detailedAssertEquals(RankingMap a, RankingMap b) {
+ Ranking arank = new Ranking();
+ Ranking brank = new Ranking();
+ assertArrayEquals(a.getOrderedKeys(), b.getOrderedKeys());
+ for (String key : a.getOrderedKeys()) {
+ a.getRanking(key, arank);
+ b.getRanking(key, brank);
+ detailedAssertEquals("ranking for key <" + key + ">", arank, brank);
}
- NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
- interceptedKeys.toArray(new String[0]), visibilityOverrides,
- suppressedVisualEffects, importance, explanation, overrideGroupKeys,
- channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden,
- smartActions, smartReplies, lastAudiblyAlerted, noisy, canBubble);
+ }
+
+ // Tests parceling of RankingMap and RankingMap.equals
+ @Test
+ public void testRankingMap_parcel() {
+ RankingMap rmap = generateUpdate().getRankingMap();
+ Parcel parcel = Parcel.obtain();
+ rmap.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ RankingMap rmap1 = RankingMap.CREATOR.createFromParcel(parcel);
+
+ detailedAssertEquals(rmap, rmap1);
+ assertEquals(rmap, rmap1);
+ }
+
+ private void detailedAssertEquals(String comment, Ranking a, Ranking b) {
+ assertEquals(comment, a.getKey(), b.getKey());
+ assertEquals(comment, a.getRank(), b.getRank());
+ assertEquals(comment, a.matchesInterruptionFilter(), b.matchesInterruptionFilter());
+ assertEquals(comment, a.getVisibilityOverride(), b.getVisibilityOverride());
+ assertEquals(comment, a.getSuppressedVisualEffects(), b.getSuppressedVisualEffects());
+ assertEquals(comment, a.getImportance(), b.getImportance());
+ assertEquals(comment, a.getImportanceExplanation(), b.getImportanceExplanation());
+ assertEquals(comment, a.getOverrideGroupKey(), b.getOverrideGroupKey());
+ assertEquals(comment, a.getChannel(), b.getChannel());
+ assertEquals(comment, a.getAdditionalPeople(), b.getAdditionalPeople());
+ assertEquals(comment, a.getSnoozeCriteria(), b.getSnoozeCriteria());
+ assertEquals(comment, a.canShowBadge(), b.canShowBadge());
+ assertEquals(comment, a.getUserSentiment(), b.getUserSentiment());
+ assertEquals(comment, a.isSuspended(), b.isSuspended());
+ assertEquals(comment, a.getLastAudiblyAlertedMillis(), b.getLastAudiblyAlertedMillis());
+ assertEquals(comment, a.isNoisy(), b.isNoisy());
+ assertEquals(comment, a.getSmartReplies(), b.getSmartReplies());
+ assertEquals(comment, a.canBubble(), b.canBubble());
+ assertActionsEqual(a.getSmartActions(), b.getSmartActions());
+ }
+
+ // Tests parceling of Ranking and Ranking.equals
+ @Test
+ public void testRanking_parcel() {
+ Ranking ranking = generateUpdate().getRankingMap().getRawRankingObject(mKeys[0]);
+ Parcel parcel = Parcel.obtain();
+ ranking.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ Ranking ranking1 = new Ranking(parcel);
+ detailedAssertEquals("rankings differ: ", ranking, ranking1);
+ assertEquals(ranking, ranking1);
+ }
+
+ private void detailedAssertEquals(NotificationRankingUpdate a, NotificationRankingUpdate b) {
+ assertEquals(a.getRankingMap(), b.getRankingMap());
+ }
+
+ // Tests NotificationRankingUpdate.equals(), and by extension, RankingMap and Ranking.
+ @Test
+ public void testRankingUpdate_equals() {
+ NotificationRankingUpdate nru = generateUpdate();
+ NotificationRankingUpdate nru2 = generateUpdate();
+ detailedAssertEquals(nru, nru2);
+ assertEquals(nru, nru2);
+ Ranking tweak = nru2.getRankingMap().getRawRankingObject(mKeys[0]);
+ tweak.populate(
+ tweak.getKey(),
+ tweak.getRank(),
+ !tweak.matchesInterruptionFilter(), // note the inversion here!
+ tweak.getVisibilityOverride(),
+ tweak.getSuppressedVisualEffects(),
+ tweak.getImportance(),
+ tweak.getImportanceExplanation(),
+ tweak.getOverrideGroupKey(),
+ tweak.getChannel(),
+ (ArrayList) tweak.getAdditionalPeople(),
+ (ArrayList) tweak.getSnoozeCriteria(),
+ tweak.canShowBadge(),
+ tweak.getUserSentiment(),
+ tweak.isSuspended(),
+ tweak.getLastAudiblyAlertedMillis(),
+ tweak.isNoisy(),
+ (ArrayList) tweak.getSmartActions(),
+ (ArrayList) tweak.getSmartReplies(),
+ tweak.canBubble()
+ );
+ assertNotEquals(nru, nru2);
+ }
+
+ // Test data
+
+ private String[] mKeys = new String[] { "key", "key1", "key2", "key3", "key4"};
+
+ private NotificationRankingUpdate generateUpdate() {
+ Ranking[] rankings = new Ranking[mKeys.length];
+ for (int i = 0; i < mKeys.length; i++) {
+ final String key = mKeys[i];
+ Ranking ranking = new Ranking();
+ ranking.populate(
+ key,
+ i,
+ !isIntercepted(i),
+ getVisibilityOverride(i),
+ getSuppressedVisualEffects(i),
+ getImportance(i),
+ getExplanation(key),
+ getOverrideGroupKey(key),
+ getChannel(key, i),
+ getPeople(key, i),
+ getSnoozeCriteria(key, i),
+ getShowBadge(i),
+ getUserSentiment(i),
+ getHidden(i),
+ lastAudiblyAlerted(i),
+ getNoisy(i),
+ getSmartActions(key, i),
+ getSmartReplies(key, i),
+ canBubble(i)
+ );
+ rankings[i] = ranking;
+ }
+ NotificationRankingUpdate update = new NotificationRankingUpdate(rankings);
return update;
}