Merge "Dramatically simplify NotificationRankingUpdate." into qt-dev

This commit is contained in:
Daniel Sandler
2019-06-07 17:11:23 +00:00
committed by Android (Google) Code Review
4 changed files with 389 additions and 658 deletions

View File

@@ -42,7 +42,6 @@ import android.graphics.drawable.BitmapDrawable;
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.RemoteException;
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 com.android.internal.os.SomeArgs;
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 @@ public abstract class NotificationListenerService extends Service {
*/
@GuardedBy("mLock")
public final void applyUpdateLocked(NotificationRankingUpdate update) {
mRankingMap = new RankingMap(update);
mRankingMap = update.getRankingMap();
}
/** @hide */
@@ -1480,14 +1478,14 @@ public abstract class NotificationListenerService extends Service {
*/
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 @@ public abstract class NotificationListenerService extends Service {
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.
@@ -1736,6 +1797,31 @@ public abstract class NotificationListenerService extends Service {
mCanBubble = canBubble;
}
/**
* @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}
*/
@@ -1758,6 +1844,35 @@ public abstract class NotificationListenerService extends Service {
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 @@ public abstract class NotificationListenerService extends Service {
* 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 @@ public abstract class NotificationListenerService extends Service {
* @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 @@ public abstract class NotificationListenerService extends Service {
* 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 {

View File

@@ -15,7 +15,6 @@
*/
package android.service.notification;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,73 +22,18 @@ import android.os.Parcelable;
* @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
@@ -97,26 +41,18 @@ public class NotificationRankingUpdate implements Parcelable {
return 0;
}
@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 @@ public class NotificationRankingUpdate implements Parcelable {
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;
}
}

View File

@@ -7234,72 +7234,42 @@ public class NotificationManagerService extends SystemService {
@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) {

View File

@@ -20,7 +20,9 @@ import static android.service.notification.NotificationListenerService.Ranking.U
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.NotificationChannel;
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 @@ import java.util.List;
@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 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
}
}
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;
}