diff options
12 files changed, 336 insertions, 103 deletions
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java index 4b25c8832068..182a49758892 100644 --- a/core/java/android/service/notification/Adjustment.java +++ b/core/java/android/service/notification/Adjustment.java @@ -52,7 +52,7 @@ public final class Adjustment implements Parcelable { /** @hide */ @StringDef (prefix = { "KEY_" }, value = { KEY_CONTEXTUAL_ACTIONS, KEY_GROUP_KEY, KEY_IMPORTANCE, KEY_PEOPLE, KEY_SNOOZE_CRITERIA, - KEY_TEXT_REPLIES, KEY_USER_SENTIMENT + KEY_TEXT_REPLIES, KEY_USER_SENTIMENT, KEY_IMPORTANCE_PROPOSAL }) @Retention(RetentionPolicy.SOURCE) public @interface Keys {} @@ -122,6 +122,19 @@ public final class Adjustment implements Parcelable { public static final String KEY_IMPORTANCE = "key_importance"; /** + * Weaker than {@link #KEY_IMPORTANCE}, this adjustment suggests an importance rather than + * mandates an importance change. + * + * A notification listener can interpet this suggestion to show the user a prompt to change + * notification importance for the notification (or type, or app) moving forward. + * + * Data type: int, one of importance values e.g. + * {@link android.app.NotificationManager#IMPORTANCE_MIN}. + * @hide + */ + public static final String KEY_IMPORTANCE_PROPOSAL = "key_importance_proposal"; + + /** * Data type: float, a ranking score from 0 (lowest) to 1 (highest). * Used to rank notifications inside that fall under the same classification (i.e. alerting, * silenced). diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index ad2e9d510998..dc4cb9f09835 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1711,6 +1711,8 @@ public abstract class NotificationListenerService extends Service { private ShortcutInfo mShortcutInfo; private @RankingAdjustment int mRankingAdjustment; private boolean mIsBubble; + // Notification assistant importance suggestion + private int mProposedImportance; private static final int PARCEL_VERSION = 2; @@ -1748,6 +1750,7 @@ public abstract class NotificationListenerService extends Service { out.writeParcelable(mShortcutInfo, flags); out.writeInt(mRankingAdjustment); out.writeBoolean(mIsBubble); + out.writeInt(mProposedImportance); } /** @hide */ @@ -1786,6 +1789,7 @@ public abstract class NotificationListenerService extends Service { mShortcutInfo = in.readParcelable(cl, android.content.pm.ShortcutInfo.class); mRankingAdjustment = in.readInt(); mIsBubble = in.readBoolean(); + mProposedImportance = in.readInt(); } @@ -1878,6 +1882,22 @@ public abstract class NotificationListenerService extends Service { } /** + * Returns the proposed importance provided by the {@link NotificationAssistantService}. + * + * This can be used to suggest that the user change the importance of this type of + * notification moving forward. A value of + * {@link NotificationManager#IMPORTANCE_UNSPECIFIED} means that the NAS has not recommended + * a change to the importance, and no UI should be shown to the user. See + * {@link Adjustment#KEY_IMPORTANCE_PROPOSAL}. + * + * @return the importance of the notification + * @hide + */ + public @NotificationManager.Importance int getProposedImportance() { + return mProposedImportance; + } + + /** * If the system has overridden the group key, then this will be non-null, and this * key should be used to bundle notifications. */ @@ -2041,7 +2061,7 @@ public abstract class NotificationListenerService extends Service { boolean noisy, ArrayList<Notification.Action> smartActions, ArrayList<CharSequence> smartReplies, boolean canBubble, boolean isTextChanged, boolean isConversation, ShortcutInfo shortcutInfo, - int rankingAdjustment, boolean isBubble) { + int rankingAdjustment, boolean isBubble, int proposedImportance) { mKey = key; mRank = rank; mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW; @@ -2067,6 +2087,7 @@ public abstract class NotificationListenerService extends Service { mShortcutInfo = shortcutInfo; mRankingAdjustment = rankingAdjustment; mIsBubble = isBubble; + mProposedImportance = proposedImportance; } /** @@ -2107,7 +2128,8 @@ public abstract class NotificationListenerService extends Service { other.mIsConversation, other.mShortcutInfo, other.mRankingAdjustment, - other.mIsBubble); + other.mIsBubble, + other.mProposedImportance); } /** @@ -2166,7 +2188,8 @@ public abstract class NotificationListenerService extends Service { && Objects.equals((mShortcutInfo == null ? 0 : mShortcutInfo.getId()), (other.mShortcutInfo == null ? 0 : other.mShortcutInfo.getId())) && Objects.equals(mRankingAdjustment, other.mRankingAdjustment) - && Objects.equals(mIsBubble, other.mIsBubble); + && Objects.equals(mIsBubble, other.mIsBubble) + && Objects.equals(mProposedImportance, other.mProposedImportance); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java index 56b689efaa79..7d0ac1874056 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java @@ -290,7 +290,8 @@ public class NotificationListener extends NotificationListenerWithPlugins implem false, null, 0, - false + false, + 0 ); } return ranking; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt index 8275c0c24339..9b3626bfc9ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt @@ -127,6 +127,6 @@ class TargetSdkResolverTest : SysuiTestCase() { NotificationManager.IMPORTANCE_DEFAULT, null, null, null, null, null, true, 0, false, -1, false, null, null, false, false, - false, null, 0, false) + false, null, 0, false, 0) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/RankingBuilder.java index 045e6f19c667..7bcad456ff6e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/RankingBuilder.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/RankingBuilder.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; +import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; + import android.annotation.NonNull; import android.app.Notification; import android.app.NotificationChannel; @@ -57,6 +59,7 @@ public class RankingBuilder { private ShortcutInfo mShortcutInfo = null; private int mRankingAdjustment = 0; private boolean mIsBubble = false; + private int mProposedImportance = IMPORTANCE_UNSPECIFIED; public RankingBuilder() { } @@ -86,6 +89,7 @@ public class RankingBuilder { mShortcutInfo = ranking.getConversationShortcutInfo(); mRankingAdjustment = ranking.getRankingAdjustment(); mIsBubble = ranking.isBubble(); + mProposedImportance = ranking.getProposedImportance(); } public Ranking build() { @@ -114,7 +118,8 @@ public class RankingBuilder { mIsConversation, mShortcutInfo, mRankingAdjustment, - mIsBubble); + mIsBubble, + mProposedImportance); return ranking; } @@ -214,6 +219,11 @@ public class RankingBuilder { return this; } + public RankingBuilder setProposedImportance(@Importance int importance) { + mProposedImportance = importance; + return this; + } + public RankingBuilder setUserSentiment(int userSentiment) { mUserSentiment = userSentiment; return this; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 4d44c886fa22..6bc85824763c 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -239,7 +239,6 @@ import android.service.notification.NotificationRankingUpdate; import android.service.notification.NotificationRecordProto; import android.service.notification.NotificationServiceDumpProto; import android.service.notification.NotificationStats; -import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeProto; @@ -8526,95 +8525,6 @@ public class NotificationManagerService extends SystemService { } } - static class NotificationRecordExtractorData { - // Class that stores any field in a NotificationRecord that can change via an extractor. - // Used to cache previous data used in a sort. - int mPosition; - int mVisibility; - boolean mShowBadge; - boolean mAllowBubble; - boolean mIsBubble; - NotificationChannel mChannel; - String mGroupKey; - ArrayList<String> mOverridePeople; - ArrayList<SnoozeCriterion> mSnoozeCriteria; - Integer mUserSentiment; - Integer mSuppressVisually; - ArrayList<Notification.Action> mSystemSmartActions; - ArrayList<CharSequence> mSmartReplies; - int mImportance; - - // These fields may not trigger a reranking but diffs here may be logged. - float mRankingScore; - boolean mIsConversation; - - NotificationRecordExtractorData(int position, int visibility, boolean showBadge, - boolean allowBubble, boolean isBubble, NotificationChannel channel, String groupKey, - ArrayList<String> overridePeople, ArrayList<SnoozeCriterion> snoozeCriteria, - Integer userSentiment, Integer suppressVisually, - ArrayList<Notification.Action> systemSmartActions, - ArrayList<CharSequence> smartReplies, int importance, float rankingScore, - boolean isConversation) { - mPosition = position; - mVisibility = visibility; - mShowBadge = showBadge; - mAllowBubble = allowBubble; - mIsBubble = isBubble; - mChannel = channel; - mGroupKey = groupKey; - mOverridePeople = overridePeople; - mSnoozeCriteria = snoozeCriteria; - mUserSentiment = userSentiment; - mSuppressVisually = suppressVisually; - mSystemSmartActions = systemSmartActions; - mSmartReplies = smartReplies; - mImportance = importance; - mRankingScore = rankingScore; - mIsConversation = isConversation; - } - - // Returns whether the provided NotificationRecord differs from the cached data in any way. - // Should be guarded by mNotificationLock; not annotated here as this class is static. - boolean hasDiffForRankingLocked(NotificationRecord r, int newPosition) { - return mPosition != newPosition - || mVisibility != r.getPackageVisibilityOverride() - || mShowBadge != r.canShowBadge() - || mAllowBubble != r.canBubble() - || mIsBubble != r.getNotification().isBubbleNotification() - || !Objects.equals(mChannel, r.getChannel()) - || !Objects.equals(mGroupKey, r.getGroupKey()) - || !Objects.equals(mOverridePeople, r.getPeopleOverride()) - || !Objects.equals(mSnoozeCriteria, r.getSnoozeCriteria()) - || !Objects.equals(mUserSentiment, r.getUserSentiment()) - || !Objects.equals(mSuppressVisually, r.getSuppressedVisualEffects()) - || !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions()) - || !Objects.equals(mSmartReplies, r.getSmartReplies()) - || mImportance != r.getImportance(); - } - - // Returns whether the NotificationRecord has a change from this data for which we should - // log an update. This method specifically targets fields that may be changed via - // adjustments from the assistant. - // - // Fields here are the union of things in NotificationRecordLogger.shouldLogReported - // and NotificationRecord.applyAdjustments. - // - // Should be guarded by mNotificationLock; not annotated here as this class is static. - boolean hasDiffForLoggingLocked(NotificationRecord r, int newPosition) { - return mPosition != newPosition - || !Objects.equals(mChannel, r.getChannel()) - || !Objects.equals(mGroupKey, r.getGroupKey()) - || !Objects.equals(mOverridePeople, r.getPeopleOverride()) - || !Objects.equals(mSnoozeCriteria, r.getSnoozeCriteria()) - || !Objects.equals(mUserSentiment, r.getUserSentiment()) - || !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions()) - || !Objects.equals(mSmartReplies, r.getSmartReplies()) - || mImportance != r.getImportance() - || !r.rankingScoreMatches(mRankingScore) - || mIsConversation != r.isConversation(); - } - } - void handleRankingSort() { if (mRankingHelper == null) return; synchronized (mNotificationLock) { @@ -8640,7 +8550,8 @@ public class NotificationManagerService extends SystemService { r.getSmartReplies(), r.getImportance(), r.getRankingScore(), - r.isConversation()); + r.isConversation(), + r.getProposedImportance()); extractorDataBefore.put(r.getKey(), extractorData); mRankingHelper.extractSignals(r); } @@ -9935,7 +9846,8 @@ public class NotificationManagerService extends SystemService { record.getRankingScore() == 0 ? RANKING_UNCHANGED : (record.getRankingScore() > 0 ? RANKING_PROMOTED : RANKING_DEMOTED), - record.getNotification().isBubbleNotification() + record.getNotification().isBubbleNotification(), + record.getProposedImportance() ); rankings.add(ranking); } diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index cbaf485c077f..d3443066155f 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -210,6 +210,7 @@ public final class NotificationRecord { // Whether this notification record should have an update logged the next time notifications // are sorted. private boolean mPendingLogUpdate = false; + private int mProposedImportance = IMPORTANCE_UNSPECIFIED; public NotificationRecord(Context context, StatusBarNotification sbn, NotificationChannel channel) { @@ -499,6 +500,8 @@ public final class NotificationRecord { pw.println(prefix + "mImportance=" + NotificationListenerService.Ranking.importanceToString(mImportance)); pw.println(prefix + "mImportanceExplanation=" + getImportanceExplanation()); + pw.println(prefix + "mProposedImportance=" + + NotificationListenerService.Ranking.importanceToString(mProposedImportance)); pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked); pw.println(prefix + "mIntercept=" + mIntercept); pw.println(prefix + "mHidden==" + mHidden); @@ -738,6 +741,12 @@ public final class NotificationRecord { Adjustment.KEY_NOT_CONVERSATION, Boolean.toString(mIsNotConversationOverride)); } + if (signals.containsKey(Adjustment.KEY_IMPORTANCE_PROPOSAL)) { + mProposedImportance = signals.getInt(Adjustment.KEY_IMPORTANCE_PROPOSAL); + EventLogTags.writeNotificationAdjusted(getKey(), + Adjustment.KEY_IMPORTANCE_PROPOSAL, + Integer.toString(mProposedImportance)); + } if (!signals.isEmpty() && adjustment.getIssuer() != null) { mAdjustmentIssuer = adjustment.getIssuer(); } @@ -870,6 +879,10 @@ public final class NotificationRecord { return stats.naturalImportance; } + public int getProposedImportance() { + return mProposedImportance; + } + public float getRankingScore() { return mRankingScore; } diff --git a/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java b/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java new file mode 100644 index 000000000000..6dc9029f8928 --- /dev/null +++ b/services/core/java/com/android/server/notification/NotificationRecordExtractorData.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2023 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 com.android.server.notification; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.service.notification.SnoozeCriterion; + +import java.util.ArrayList; +import java.util.Objects; + +/** + * Class that stores any field in a NotificationRecord that can change via an extractor. + * Used to cache previous data used in a sort. + */ +public final class NotificationRecordExtractorData { + private final int mPosition; + private final int mVisibility; + private final boolean mShowBadge; + private final boolean mAllowBubble; + private final boolean mIsBubble; + private final NotificationChannel mChannel; + private final String mGroupKey; + private final ArrayList<String> mOverridePeople; + private final ArrayList<SnoozeCriterion> mSnoozeCriteria; + private final Integer mUserSentiment; + private final Integer mSuppressVisually; + private final ArrayList<Notification.Action> mSystemSmartActions; + private final ArrayList<CharSequence> mSmartReplies; + private final int mImportance; + + // These fields may not trigger a reranking but diffs here may be logged. + private final float mRankingScore; + private final boolean mIsConversation; + private final int mProposedImportance; + + NotificationRecordExtractorData(int position, int visibility, boolean showBadge, + boolean allowBubble, boolean isBubble, NotificationChannel channel, String groupKey, + ArrayList<String> overridePeople, ArrayList<SnoozeCriterion> snoozeCriteria, + Integer userSentiment, Integer suppressVisually, + ArrayList<Notification.Action> systemSmartActions, + ArrayList<CharSequence> smartReplies, int importance, float rankingScore, + boolean isConversation, int proposedImportance) { + mPosition = position; + mVisibility = visibility; + mShowBadge = showBadge; + mAllowBubble = allowBubble; + mIsBubble = isBubble; + mChannel = channel; + mGroupKey = groupKey; + mOverridePeople = overridePeople; + mSnoozeCriteria = snoozeCriteria; + mUserSentiment = userSentiment; + mSuppressVisually = suppressVisually; + mSystemSmartActions = systemSmartActions; + mSmartReplies = smartReplies; + mImportance = importance; + mRankingScore = rankingScore; + mIsConversation = isConversation; + mProposedImportance = proposedImportance; + } + + // Returns whether the provided NotificationRecord differs from the cached data in any way. + // Should be guarded by mNotificationLock; not annotated here as this class is static. + boolean hasDiffForRankingLocked(NotificationRecord r, int newPosition) { + return mPosition != newPosition + || mVisibility != r.getPackageVisibilityOverride() + || mShowBadge != r.canShowBadge() + || mAllowBubble != r.canBubble() + || mIsBubble != r.getNotification().isBubbleNotification() + || !Objects.equals(mChannel, r.getChannel()) + || !Objects.equals(mGroupKey, r.getGroupKey()) + || !Objects.equals(mOverridePeople, r.getPeopleOverride()) + || !Objects.equals(mSnoozeCriteria, r.getSnoozeCriteria()) + || !Objects.equals(mUserSentiment, r.getUserSentiment()) + || !Objects.equals(mSuppressVisually, r.getSuppressedVisualEffects()) + || !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions()) + || !Objects.equals(mSmartReplies, r.getSmartReplies()) + || mImportance != r.getImportance() + || mProposedImportance != r.getProposedImportance(); + } + + // Returns whether the NotificationRecord has a change from this data for which we should + // log an update. This method specifically targets fields that may be changed via + // adjustments from the assistant. + // + // Fields here are the union of things in NotificationRecordLogger.shouldLogReported + // and NotificationRecord.applyAdjustments. + // + // Should be guarded by mNotificationLock; not annotated here as this class is static. + boolean hasDiffForLoggingLocked(NotificationRecord r, int newPosition) { + return mPosition != newPosition + || !Objects.equals(mChannel, r.getChannel()) + || !Objects.equals(mGroupKey, r.getGroupKey()) + || !Objects.equals(mOverridePeople, r.getPeopleOverride()) + || !Objects.equals(mSnoozeCriteria, r.getSnoozeCriteria()) + || !Objects.equals(mUserSentiment, r.getUserSentiment()) + || !Objects.equals(mSystemSmartActions, r.getSystemGeneratedSmartActions()) + || !Objects.equals(mSmartReplies, r.getSmartReplies()) + || mImportance != r.getImportance() + || !r.rankingScoreMatches(mRankingScore) + || mIsConversation != r.isConversation() + || mProposedImportance != r.getProposedImportance(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index 66c3f0730404..b12e6ad563d1 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -1678,7 +1678,7 @@ public final class DataManagerTest { mParentNotificationChannel.getImportance(), null, null, mParentNotificationChannel, null, null, true, 0, false, -1, false, null, null, - false, false, false, null, 0, false); + false, false, false, null, 0, false, 0); return true; }).when(mRankingMap).getRanking(eq(GENERIC_KEY), any(NotificationListenerService.Ranking.class)); @@ -1704,7 +1704,7 @@ public final class DataManagerTest { mNotificationChannel.getImportance(), null, null, mNotificationChannel, null, null, true, 0, false, -1, false, null, null, false, - false, false, null, 0, false); + false, false, null, 0, false, 0); return true; }).when(mRankingMap).getRanking(eq(CUSTOM_KEY), any(NotificationListenerService.Ranking.class)); 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 12cd834d1d66..8a99c2cdcc6f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -193,7 +193,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { tweak.isConversation(), tweak.getConversationShortcutInfo(), tweak.getRankingAdjustment(), - tweak.isBubble() + tweak.isBubble(), + tweak.getProposedImportance() ); assertNotEquals(nru, nru2); } @@ -274,7 +275,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { isConversation(i), getShortcutInfo(i), getRankingAdjustment(i), - isBubble(i) + isBubble(i), + getProposedImportance(i) ); rankings[i] = ranking; } @@ -402,6 +404,10 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { return index % 3 - 1; } + private int getProposedImportance(int index) { + return index % 5 - 1; + } + private boolean isBubble(int index) { return index % 4 == 0; } @@ -443,6 +449,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { assertEquals(comment, a.getConversationShortcutInfo().getId(), b.getConversationShortcutInfo().getId()); assertActionsEqual(a.getSmartActions(), b.getSmartActions()); + assertEquals(a.getProposedImportance(), b.getProposedImportance()); } private void detailedAssertEquals(RankingMap a, RankingMap b) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java new file mode 100644 index 000000000000..87e86cb00f56 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordExtractorDataTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2023 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 com.android.server.notification; + +import static android.app.NotificationManager.IMPORTANCE_HIGH; +import static android.app.NotificationManager.IMPORTANCE_LOW; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.PendingIntent; +import android.content.Intent; +import android.graphics.drawable.Icon; +import android.os.Bundle; +import android.os.UserHandle; +import android.service.notification.Adjustment; +import android.service.notification.SnoozeCriterion; +import android.service.notification.StatusBarNotification; + +import com.android.server.UiServiceTestCase; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Objects; + +public class NotificationRecordExtractorDataTest extends UiServiceTestCase { + + @Test + public void testHasDiffs_noDiffs() { + NotificationRecord r = generateRecord(); + + NotificationRecordExtractorData extractorData = new NotificationRecordExtractorData( + 1, + r.getPackageVisibilityOverride(), + r.canShowBadge(), + r.canBubble(), + r.getNotification().isBubbleNotification(), + r.getChannel(), + r.getGroupKey(), + r.getPeopleOverride(), + r.getSnoozeCriteria(), + r.getUserSentiment(), + r.getSuppressedVisualEffects(), + r.getSystemGeneratedSmartActions(), + r.getSmartReplies(), + r.getImportance(), + r.getRankingScore(), + r.isConversation(), + r.getProposedImportance()); + + assertFalse(extractorData.hasDiffForRankingLocked(r, 1)); + assertFalse(extractorData.hasDiffForLoggingLocked(r, 1)); + } + + @Test + public void testHasDiffs_proposedImportanceChange() { + NotificationRecord r = generateRecord(); + + NotificationRecordExtractorData extractorData = new NotificationRecordExtractorData( + 1, + r.getPackageVisibilityOverride(), + r.canShowBadge(), + r.canBubble(), + r.getNotification().isBubbleNotification(), + r.getChannel(), + r.getGroupKey(), + r.getPeopleOverride(), + r.getSnoozeCriteria(), + r.getUserSentiment(), + r.getSuppressedVisualEffects(), + r.getSystemGeneratedSmartActions(), + r.getSmartReplies(), + r.getImportance(), + r.getRankingScore(), + r.isConversation(), + r.getProposedImportance()); + + Bundle signals = new Bundle(); + signals.putInt(Adjustment.KEY_IMPORTANCE_PROPOSAL, IMPORTANCE_HIGH); + Adjustment adjustment = new Adjustment("pkg", r.getKey(), signals, "", 0); + r.addAdjustment(adjustment); + r.applyAdjustments(); + + assertTrue(extractorData.hasDiffForRankingLocked(r, 1)); + assertTrue(extractorData.hasDiffForLoggingLocked(r, 1)); + } + + private NotificationRecord generateRecord() { + NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW); + final Notification.Builder builder = new Notification.Builder(getContext()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon); + Notification n = builder.build(); + StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0, + 0, n, UserHandle.ALL, null, System.currentTimeMillis()); + return new NotificationRecord(getContext(), sbn, channel); + } +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java index 5468220d9564..14b004827ece 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java @@ -19,6 +19,7 @@ import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; +import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static android.service.notification.Adjustment.KEY_IMPORTANCE; import static android.service.notification.Adjustment.KEY_NOT_CONVERSATION; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING; @@ -755,6 +756,24 @@ public class NotificationRecordTest extends UiServiceTestCase { } @Test + public void testProposedImportance() { + StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */, + true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, + false /* lights */, false /* defaultLights */, groupId /* group */); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + + assertEquals(IMPORTANCE_UNSPECIFIED, record.getProposedImportance()); + + Bundle signals = new Bundle(); + signals.putInt(Adjustment.KEY_IMPORTANCE_PROPOSAL, IMPORTANCE_DEFAULT); + record.addAdjustment(new Adjustment(mPkg, record.getKey(), signals, null, sbn.getUserId())); + + record.applyAdjustments(); + + assertEquals(IMPORTANCE_DEFAULT, record.getProposedImportance()); + } + + @Test public void testAppImportance_returnsCorrectly() { StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, |