diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 56e6aea815ecd..64eae0cf4635d 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -1462,6 +1462,7 @@ public abstract class NotificationListenerService extends Service { private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL; private boolean mHidden; private boolean mAudiblyAlerted; + private boolean mNoisy; private ArrayList mSmartActions; private ArrayList mSmartReplies; @@ -1636,6 +1637,11 @@ public abstract class NotificationListenerService extends Service { return mAudiblyAlerted; } + /** @hide */ + public boolean isNoisy() { + return mNoisy; + } + /** * @hide */ @@ -1646,7 +1652,8 @@ public abstract class NotificationListenerService extends Service { NotificationChannel channel, ArrayList overridePeople, ArrayList snoozeCriteria, boolean showBadge, int userSentiment, boolean hidden, boolean audiblyAlerted, - ArrayList smartActions, ArrayList smartReplies) { + boolean noisy, ArrayList smartActions, + ArrayList smartReplies) { mKey = key; mRank = rank; mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW; @@ -1663,6 +1670,7 @@ public abstract class NotificationListenerService extends Service { mUserSentiment = userSentiment; mHidden = hidden; mAudiblyAlerted = audiblyAlerted; + mNoisy = noisy; mSmartActions = smartActions; mSmartReplies = smartReplies; } @@ -1715,6 +1723,7 @@ public abstract class NotificationListenerService extends Service { private ArrayMap mUserSentiment; private ArrayMap mHidden; private ArrayMap mAudiblyAlerted; + private ArrayMap mNoisy; private ArrayMap> mSmartActions; private ArrayMap> mSmartReplies; @@ -1746,7 +1755,8 @@ public abstract class NotificationListenerService extends Service { getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key), getChannel(key), getOverridePeople(key), getSnoozeCriteria(key), getShowBadge(key), getUserSentiment(key), getHidden(key), - getAudiblyAlerted(key), getSmartActions(key), getSmartReplies(key)); + getAudiblyAlerted(key), getNoisy(key), getSmartActions(key), + getSmartReplies(key)); return rank >= 0; } @@ -1894,6 +1904,16 @@ public abstract class NotificationListenerService extends Service { return audiblyAlerted == null ? false : audiblyAlerted.booleanValue(); } + private boolean getNoisy(String key) { + synchronized (this) { + if (mNoisy == null) { + buildNoisyLocked(); + } + } + Boolean noisy = mNoisy.get(key); + return noisy == null ? false : noisy.booleanValue(); + } + private ArrayList getSmartActions(String key) { synchronized (this) { if (mSmartActions == null) { @@ -2033,6 +2053,11 @@ public abstract class NotificationListenerService extends Service { mAudiblyAlerted = buildBooleanMapFromBundle(mRankingUpdate.getAudiblyAlerted()); } + // Locked by 'this' + private void buildNoisyLocked() { + mNoisy = buildBooleanMapFromBundle(mRankingUpdate.getNoisy()); + } + // Locked by 'this' private void buildSmartActions() { Bundle smartActions = mRankingUpdate.getSmartActions(); diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java index b561bfd98038e..f80df93364f40 100644 --- a/core/java/android/service/notification/NotificationRankingUpdate.java +++ b/core/java/android/service/notification/NotificationRankingUpdate.java @@ -40,13 +40,14 @@ public class NotificationRankingUpdate implements Parcelable { private final Bundle mSmartActions; private final Bundle mSmartReplies; private final Bundle mAudiblyAlerted; + private final Bundle mNoisy; 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 audiblyAlerted) { + Bundle smartReplies, Bundle audiblyAlerted, Bundle noisy) { mKeys = keys; mInterceptedKeys = interceptedKeys; mVisibilityOverrides = visibilityOverrides; @@ -63,6 +64,7 @@ public class NotificationRankingUpdate implements Parcelable { mSmartActions = smartActions; mSmartReplies = smartReplies; mAudiblyAlerted = audiblyAlerted; + mNoisy = noisy; } public NotificationRankingUpdate(Parcel in) { @@ -83,6 +85,7 @@ public class NotificationRankingUpdate implements Parcelable { mSmartActions = in.readBundle(); mSmartReplies = in.readBundle(); mAudiblyAlerted = in.readBundle(); + mNoisy = in.readBundle(); } @Override @@ -108,6 +111,7 @@ public class NotificationRankingUpdate implements Parcelable { out.writeBundle(mSmartActions); out.writeBundle(mSmartReplies); out.writeBundle(mAudiblyAlerted); + out.writeBundle(mNoisy); } public static final Parcelable.Creator CREATOR @@ -184,4 +188,8 @@ public class NotificationRankingUpdate implements Parcelable { public Bundle getAudiblyAlerted() { return mAudiblyAlerted; } + + public Bundle getNoisy() { + return mNoisy; + } } diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index f138685e98105..c86ebe70db5c4 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -151,6 +151,13 @@ android:layout_height="match_parent" android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing" style="@style/TextAppearance.NotificationInfo.Button" /> + These notifications will be minimized + + These notifications will be shown silently + + + These notifications will alert you + You usually dismiss these notifications. \nKeep showing them? @@ -1556,6 +1562,12 @@ Minimize + + Show silently + + + Show and alert + Keep showing notifications from this app? diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java index b84b77c6d1050..f7c07f370cb1c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java @@ -102,6 +102,8 @@ public class NotificationData { public StatusBarNotification notification; public NotificationChannel channel; public boolean audiblyAlerted; + public boolean noisy; + public int importance; public StatusBarIconView icon; public StatusBarIconView expandedIcon; public ExpandableNotificationRow row; // the outer expanded view @@ -155,6 +157,8 @@ public class NotificationData { public void populateFromRanking(@NonNull Ranking ranking) { channel = ranking.getChannel(); audiblyAlerted = ranking.audiblyAlerted(); + noisy = ranking.isNoisy(); + importance = ranking.getImportance(); snoozeCriteria = ranking.getSnoozeCriteria(); userSentiment = ranking.getUserSentiment(); smartActions = ranking.getSmartActions() == null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java index 28c07a34beed1..43b5503682cc1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationCounters.java @@ -30,6 +30,9 @@ public class NotificationCounters { /** Counter tag for when the user hits 'stop notifications' in the blocking helper. */ public static final String BLOCKING_HELPER_STOP_NOTIFICATIONS = "blocking_helper_stop_notifications"; + /** Counter tag for when the user hits 'show silently' in the blocking helper. */ + public static final String BLOCKING_HELPER_TOGGLE_SILENT = + "blocking_helper_toggle_silent"; /** Counter tag for when the user hits 'keep showing' in the blocking helper. */ public static final String BLOCKING_HELPER_KEEP_SHOWING = "blocking_helper_keep_showing"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java index 0a197daf91864..fbe9c5d40beb5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java @@ -23,7 +23,6 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Handler; -import androidx.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -31,6 +30,8 @@ import android.view.ViewAnimationUtils; import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; +import androidx.annotation.Nullable; + import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; @@ -98,6 +99,11 @@ public class NotificationGuts extends FrameLayout { * Return whether something changed and needs to be saved, possibly requiring a bouncer. */ boolean shouldBeSaved(); + + /** + * Called when the guts view has finished its close animation. + */ + default void onFinishedClosing() {} } public interface OnGutsClosedListener { @@ -304,7 +310,7 @@ public class NotificationGuts extends FrameLayout { x, y, r, 0); a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); - a.addListener(new AnimateCloseListener(this /* view */)); + a.addListener(new AnimateCloseListener(this /* view */, mGutsContent)); a.start(); } else { // Fade in the blocking helper. @@ -312,11 +318,12 @@ public class NotificationGuts extends FrameLayout { .alpha(0f) .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE) .setInterpolator(Interpolators.ALPHA_OUT) - .setListener(new AnimateCloseListener(this /* view */)) + .setListener(new AnimateCloseListener(this, /* view */mGutsContent)) .start(); } } else { Log.w(TAG, "Failed to animate guts close"); + mGutsContent.onFinishedClosing(); } } @@ -414,15 +421,18 @@ public class NotificationGuts extends FrameLayout { /** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */ private static class AnimateCloseListener extends AnimatorListenerAdapter { final View mView; + private final GutsContent mGutsContent; - private AnimateCloseListener(View view) { + private AnimateCloseListener(View view, GutsContent gutsContent) { mView = view; + mGutsContent = gutsContent; } @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); mView.setVisibility(View.GONE); + mGutsContent.onFinishedClosing(); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 24999525ab728..b838c9b5482d7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -28,7 +28,6 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.Uri; -import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; @@ -46,15 +45,13 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; -import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; import com.android.systemui.statusbar.NotificationLifetimeExtender; import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.StatusBar; @@ -298,7 +295,9 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mDeviceProvisionedController.isDeviceProvisioned(), row.getIsNonblockable(), isForBlockingHelper, - row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE); + row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE, + row.getEntry().noisy, + row.getEntry().importance); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index 903c27277b709..522da4d514705 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -16,13 +16,18 @@ package com.android.systemui.statusbar.notification.row; +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_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; +import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.annotation.IntDef; import android.annotation.Nullable; import android.app.INotificationManager; import android.app.Notification; @@ -54,6 +59,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.logging.NotificationCounters; import java.util.List; @@ -65,6 +71,17 @@ import java.util.List; public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent { private static final String TAG = "InfoGuts"; + @IntDef(prefix = { "SWAP_CONTENT_" }, value = { + SWAP_CONTENT_UNDO, + SWAP_CONTENT_TOGGLE_SILENT, + SWAP_CONTENT_BLOCK, + }) + @interface SwapContentAction {} + + private static final int SWAP_CONTENT_UNDO = 0; + private static final int SWAP_CONTENT_TOGGLE_SILENT = 1; + private static final int SWAP_CONTENT_BLOCK = 2; + private INotificationManager mINotificationManager; private PackageManager mPm; private MetricsLogger mMetricsLogger; @@ -74,7 +91,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private int mAppUid; private int mNumUniqueChannelsInRow; private NotificationChannel mSingleNotificationChannel; - private int mStartingUserImportance; + private int mStartingChannelImportance; + private int mStartingChannelOrNotificationImportance; private int mChosenImportance; private boolean mIsSingleDefaultChannel; private boolean mIsNonblockable; @@ -82,6 +100,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private AnimatorSet mExpandAnimation; private boolean mIsForeground; private boolean mIsDeviceProvisioned; + private boolean mIsNoisy; private CheckSaveListener mCheckSaveListener; private OnSettingsClickListener mOnSettingsClickListener; @@ -102,10 +121,22 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G closeControls(v); }; + private OnClickListener mOnToggleSilent = v -> { + Runnable saveImportance = () -> { + mExitReason = NotificationCounters.BLOCKING_HELPER_TOGGLE_SILENT; + swapContent(SWAP_CONTENT_TOGGLE_SILENT); + }; + if (mCheckSaveListener != null) { + mCheckSaveListener.checkSave(saveImportance, mSbn); + } else { + saveImportance.run(); + } + }; + private OnClickListener mOnStopOrMinimizeNotifications = v -> { Runnable saveImportance = () -> { mExitReason = NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS; - swapContent(false); + swapContent(SWAP_CONTENT_BLOCK); }; if (mCheckSaveListener != null) { mCheckSaveListener.checkSave(saveImportance, mSbn); @@ -118,7 +149,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G // Reset exit counter that we'll log and record an undo event separately (not an exit event) mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED; logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO); - swapContent(true); + swapContent(SWAP_CONTENT_UNDO); }; public NotificationInfo(Context context, AttributeSet attrs) { @@ -152,12 +183,15 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G final OnSettingsClickListener onSettingsClick, final OnAppSettingsClickListener onAppSettingsClick, boolean isDeviceProvisioned, - boolean isNonblockable) + boolean isNonblockable, + boolean isNoisy, + int importance) throws RemoteException { bindNotification(pm, iNotificationManager, pkg, notificationChannel, numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick, onAppSettingsClick, isDeviceProvisioned, isNonblockable, - false /* isBlockingHelper */, false /* isUserSentimentNegative */); + false /* isBlockingHelper */, false /* isUserSentimentNegative */, isNoisy, + importance); } public void bindNotification( @@ -173,7 +207,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G boolean isDeviceProvisioned, boolean isNonblockable, boolean isForBlockingHelper, - boolean isUserSentimentNegative) + boolean isUserSentimentNegative, + boolean isNoisy, + int importance) throws RemoteException { mINotificationManager = iNotificationManager; mMetricsLogger = Dependency.get(MetricsLogger.class); @@ -186,7 +222,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mCheckSaveListener = checkSaveListener; mOnSettingsClickListener = onSettingsClick; mSingleNotificationChannel = notificationChannel; - mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance(); + int channelImportance = mSingleNotificationChannel.getImportance(); + mStartingChannelImportance = mChosenImportance = channelImportance; + mStartingChannelOrNotificationImportance = + channelImportance == IMPORTANCE_UNSPECIFIED ? importance : channelImportance; mNegativeUserSentiment = isUserSentimentNegative; mIsNonblockable = isNonblockable; mIsForeground = @@ -194,6 +233,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mIsForBlockingHelper = isForBlockingHelper; mAppUid = mSbn.getUid(); mIsDeviceProvisioned = isDeviceProvisioned; + mIsNoisy = isNoisy; int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage( pkg, mAppUid, false /* includeDeleted */); @@ -306,7 +346,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } private boolean hasImportanceChanged() { - return mSingleNotificationChannel != null && mStartingUserImportance != mChosenImportance; + return mSingleNotificationChannel != null + && mStartingChannelImportance != mChosenImportance; } private void saveImportance() { @@ -320,34 +361,46 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G */ private void updateImportance() { MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE, - mChosenImportance - mStartingUserImportance); + mChosenImportance - mStartingChannelImportance); Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER)); bgHandler.post(new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid, mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null, - mStartingUserImportance, mChosenImportance)); + mStartingChannelImportance, mChosenImportance)); } private void bindButtons() { // Set up stay-in-notification actions View block = findViewById(R.id.block); TextView keep = findViewById(R.id.keep); + TextView silent = findViewById(R.id.toggle_silent); View minimize = findViewById(R.id.minimize); findViewById(R.id.undo).setOnClickListener(mOnUndo); block.setOnClickListener(mOnStopOrMinimizeNotifications); keep.setOnClickListener(mOnKeepShowing); + silent.setOnClickListener(mOnToggleSilent); minimize.setOnClickListener(mOnStopOrMinimizeNotifications); if (mIsNonblockable) { keep.setText(android.R.string.ok); block.setVisibility(GONE); + silent.setVisibility(GONE); minimize.setVisibility(GONE); } else if (mIsForeground) { block.setVisibility(GONE); + silent.setVisibility(GONE); minimize.setVisibility(VISIBLE); - } else if (!mIsForeground) { + } else { block.setVisibility(VISIBLE); + boolean showToggleSilent = mIsNoisy + && NotificationUtils.useNewInterruptionModel(mContext); + silent.setVisibility(showToggleSilent ? VISIBLE : GONE); + boolean isCurrentlyAlerting = + mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT; + silent.setText(isCurrentlyAlerting + ? R.string.inline_silent_button_silent + : R.string.inline_silent_button_alert); minimize.setVisibility(GONE); } @@ -368,7 +421,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } - private void swapContent(boolean showPrompt) { + private void swapContent(@SwapContentAction int action) { if (mExpandAnimation != null) { mExpandAnimation.cancel(); } @@ -378,26 +431,43 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G TextView confirmationText = findViewById(R.id.confirmation_text); View header = findViewById(R.id.header); - if (showPrompt) { - mChosenImportance = mStartingUserImportance; - } else if (mIsForeground) { - mChosenImportance = IMPORTANCE_MIN; - confirmationText.setText(R.string.notification_channel_minimized); - } else { - mChosenImportance = IMPORTANCE_NONE; - confirmationText.setText(R.string.notification_channel_disabled); + switch (action) { + case SWAP_CONTENT_UNDO: + mChosenImportance = mStartingChannelImportance; + break; + case SWAP_CONTENT_TOGGLE_SILENT: + if (mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT) { + mChosenImportance = IMPORTANCE_LOW; + confirmationText.setText(R.string.notification_channel_silenced); + } else { + mChosenImportance = IMPORTANCE_HIGH; + confirmationText.setText(R.string.notification_channel_unsilenced); + } + break; + case SWAP_CONTENT_BLOCK: + if (mIsForeground) { + mChosenImportance = IMPORTANCE_MIN; + confirmationText.setText(R.string.notification_channel_minimized); + } else { + mChosenImportance = IMPORTANCE_NONE; + confirmationText.setText(R.string.notification_channel_disabled); + } + break; + default: + throw new IllegalArgumentException(); } + boolean isUndo = action == SWAP_CONTENT_UNDO; ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA, - prompt.getAlpha(), showPrompt ? 1f : 0f); - promptAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT); + prompt.getAlpha(), isUndo ? 1f : 0f); + promptAnim.setInterpolator(isUndo ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT); ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA, - confirmation.getAlpha(), showPrompt ? 0f : 1f); - confirmAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN); + confirmation.getAlpha(), isUndo ? 0f : 1f); + confirmAnim.setInterpolator(isUndo ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN); - prompt.setVisibility(showPrompt ? VISIBLE : GONE); - confirmation.setVisibility(showPrompt ? GONE : VISIBLE); - header.setVisibility(showPrompt ? VISIBLE : GONE); + prompt.setVisibility(isUndo ? VISIBLE : GONE); + confirmation.setVisibility(isUndo ? GONE : VISIBLE); + header.setVisibility(isUndo ? VISIBLE : GONE); mExpandAnimation = new AnimatorSet(); mExpandAnimation.playTogether(promptAnim, confirmAnim); @@ -413,8 +483,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G @Override public void onAnimationEnd(Animator animation) { if (!cancelled) { - prompt.setVisibility(showPrompt ? VISIBLE : GONE); - confirmation.setVisibility(showPrompt ? GONE : VISIBLE); + prompt.setVisibility(isUndo ? VISIBLE : GONE); + confirmation.setVisibility(isUndo ? GONE : VISIBLE); } } }); @@ -427,6 +497,25 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } + @Override + public void onFinishedClosing() { + mStartingChannelImportance = mChosenImportance; + if (mChosenImportance != IMPORTANCE_UNSPECIFIED) { + mStartingChannelOrNotificationImportance = mChosenImportance; + } + mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED; + + View prompt = findViewById(R.id.prompt); + ViewGroup confirmation = findViewById(R.id.confirmation); + View header = findViewById(R.id.header); + prompt.setVisibility(VISIBLE); + prompt.setAlpha(1f); + confirmation.setVisibility(GONE); + confirmation.setAlpha(1f); + header.setVisibility(VISIBLE); + header.setAlpha(1f); + } + @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java index 0251f64fd6dfa..8e6bfe3a5f916 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java @@ -437,15 +437,15 @@ public class NotificationDataTest extends SysuiTestCase { outRanking.getImportance(), outRanking.getImportanceExplanation(), outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null, outRanking.canShowBadge(), outRanking.getUserSentiment(), true, - false, null, null); + false, false, null, null); } else if (key.equals(TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY)) { outRanking.populate(key, outRanking.getRank(), outRanking.matchesInterruptionFilter(), outRanking.getVisibilityOverride(), 255, outRanking.getImportance(), outRanking.getImportanceExplanation(), outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null, - outRanking.canShowBadge(), outRanking.getUserSentiment(), true, false, null, - null); + outRanking.canShowBadge(), outRanking.getUserSentiment(), true, false, + false, null, null); } else { outRanking.populate(key, outRanking.getRank(), outRanking.matchesInterruptionFilter(), @@ -453,8 +453,7 @@ public class NotificationDataTest extends SysuiTestCase { outRanking.getImportance(), outRanking.getImportanceExplanation(), outRanking.getOverrideGroupKey(), NOTIFICATION_CHANNEL, null, null, outRanking.canShowBadge(), outRanking.getUserSentiment(), false, false, - null, - null); + false, null, null); } return true; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index 9c68e7d9ef3a8..9f8a5cc0afdfa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -167,7 +167,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { 0, NotificationManager.IMPORTANCE_DEFAULT, null, null, - null, null, null, true, sentiment, false, false, null, null); + null, null, null, true, sentiment, false, false, false, null, null); return true; }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class)); } @@ -186,7 +186,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { null, null, null, null, null, true, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, false, - smartActions, null); + false, smartActions, null); return true; }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index ee35449e05daa..626726d4aa4fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -19,8 +19,10 @@ package com.android.systemui.statusbar.notification.row; import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; +import static android.service.notification.NotificationListenerService.Ranking + .USER_SENTIMENT_NEGATIVE; -import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; @@ -34,15 +36,14 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; -import android.app.NotificationManager; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Binder; @@ -57,11 +58,12 @@ import android.view.View; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; -import com.android.systemui.statusbar.notification.NotificationData; -import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationTestHelper; -import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; +import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager + .OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -71,8 +73,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.junit.MockitoRule; import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; /** * Tests for {@link NotificationGutsManager}. @@ -84,7 +86,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId"; private NotificationChannel mTestNotificationChannel = new NotificationChannel( - TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT); + TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT); private TestableLooper mTestableLooper; private Handler mHandler; private NotificationTestHelper mHelper; @@ -297,7 +299,9 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(false), eq(false), eq(true) /* isForBlockingHelper */, - eq(true) /* isUserSentimentNegative */); + eq(true) /* isUserSentimentNegative */, + eq(false) /*isNoisy */, + eq(0)); } @Test @@ -324,7 +328,69 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(false), eq(false), eq(false) /* isForBlockingHelper */, - eq(true) /* isUserSentimentNegative */); + eq(true) /* isUserSentimentNegative */, + eq(false) /*isNoisy */, + eq(0)); + } + + @Test + public void testInitializeNotificationInfoView_noisy() throws Exception { + NotificationInfo notificationInfoView = mock(NotificationInfo.class); + ExpandableNotificationRow row = spy(mHelper.createRow()); + row.setBlockingHelperShowing(true); + row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; + row.getEntry().noisy = true; + when(row.getIsNonblockable()).thenReturn(false); + StatusBarNotification statusBarNotification = row.getStatusBarNotification(); + + mGutsManager.initializeNotificationInfo(row, notificationInfoView); + + verify(notificationInfoView).bindNotification( + any(PackageManager.class), + any(INotificationManager.class), + eq(statusBarNotification.getPackageName()), + any(NotificationChannel.class), + anyInt(), + eq(statusBarNotification), + any(NotificationInfo.CheckSaveListener.class), + any(NotificationInfo.OnSettingsClickListener.class), + any(NotificationInfo.OnAppSettingsClickListener.class), + eq(false), + eq(false), + eq(true) /* isForBlockingHelper */, + eq(true) /* isUserSentimentNegative */, + eq(true) /*isNoisy */, + eq(0)); + } + + @Test + public void testInitializeNotificationInfoView_importance() throws Exception { + NotificationInfo notificationInfoView = mock(NotificationInfo.class); + ExpandableNotificationRow row = spy(mHelper.createRow()); + row.setBlockingHelperShowing(true); + row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; + row.getEntry().importance = IMPORTANCE_DEFAULT; + when(row.getIsNonblockable()).thenReturn(false); + StatusBarNotification statusBarNotification = row.getStatusBarNotification(); + + mGutsManager.initializeNotificationInfo(row, notificationInfoView); + + verify(notificationInfoView).bindNotification( + any(PackageManager.class), + any(INotificationManager.class), + eq(statusBarNotification.getPackageName()), + any(NotificationChannel.class), + anyInt(), + eq(statusBarNotification), + any(NotificationInfo.CheckSaveListener.class), + any(NotificationInfo.OnSettingsClickListener.class), + any(NotificationInfo.OnAppSettingsClickListener.class), + eq(false), + eq(false), + eq(true) /* isForBlockingHelper */, + eq(true) /* isUserSentimentNegative */, + eq(false) /*isNoisy */, + eq(IMPORTANCE_DEFAULT)); } @Test @@ -352,7 +418,9 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(true), eq(false), eq(false) /* isForBlockingHelper */, - eq(true) /* isUserSentimentNegative */); + eq(true) /* isUserSentimentNegative */, + eq(false) /*isNoisy */, + eq(0)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index ca968a8af85bc..37441963e32db 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -17,11 +17,14 @@ package com.android.systemui.statusbar.notification.row; 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_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME; +import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL; import static android.view.View.GONE; import static android.view.View.VISIBLE; @@ -56,6 +59,7 @@ import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.IBinder; import android.os.UserHandle; +import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -72,6 +76,7 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -150,6 +155,15 @@ public class NotificationInfoTest extends SysuiTestCase { IMPORTANCE_LOW); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, new Notification(), UserHandle.CURRENT, null, 0); + + Settings.Secure.putInt(mContext.getContentResolver(), + NOTIFICATION_NEW_INTERRUPTION_MODEL, 1); + } + + @After + public void tearDown() { + Settings.Secure.putInt(mContext.getContentResolver(), + NOTIFICATION_NEW_INTERRUPTION_MODEL, 0); } // TODO: if tests are taking too long replace this with something that makes the animation @@ -172,7 +186,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_SetsTextApplicationName() throws Exception { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.pkgname); assertTrue(textView.getText().toString().contains("App Name")); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -184,7 +199,8 @@ public class NotificationInfoTest extends SysuiTestCase { when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) .thenReturn(iconDrawable); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon); assertEquals(iconDrawable, iconView.getDrawable()); } @@ -192,7 +208,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(GONE, groupNameView.getVisibility()); final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider); @@ -208,7 +225,8 @@ public class NotificationInfoTest extends SysuiTestCase { eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID))) .thenReturn(notificationChannelGroup); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(View.VISIBLE, groupNameView.getVisibility()); assertEquals("Test Group Name", groupNameView.getText()); @@ -219,7 +237,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SetsTextChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(TEST_CHANNEL_NAME, textView.getText()); } @@ -228,7 +247,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true, - false); + false, false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, textView.getVisibility()); } @@ -241,7 +260,7 @@ public class NotificationInfoTest extends SysuiTestCase { eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true, - false); + false, false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @@ -249,7 +268,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @@ -257,18 +277,71 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_BlockButton() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final View block = mNotificationInfo.findViewById(R.id.block); + final View toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); final View minimize = mNotificationInfo.findViewById(R.id.minimize); assertEquals(VISIBLE, block.getVisibility()); + assertEquals(GONE, toggleSilent.getVisibility()); assertEquals(GONE, minimize.getVisibility()); } + @Test + public void testBindNotification_SilenceButton() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_DEFAULT); + final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); + assertEquals(VISIBLE, toggleSilent.getVisibility()); + assertEquals( + mContext.getString(R.string.inline_silent_button_silent), toggleSilent.getText()); + } + + @Test + public void testBindNotification_UnSilenceButton() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_LOW); + final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); + assertEquals(VISIBLE, toggleSilent.getVisibility()); + assertEquals( + mContext.getString(R.string.inline_silent_button_alert), toggleSilent.getText()); + } + + @Test + public void testBindNotification_SilenceButton_ChannelImportanceUnspecified() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_DEFAULT); + final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); + assertEquals(VISIBLE, toggleSilent.getVisibility()); + assertEquals( + mContext.getString(R.string.inline_silent_button_silent), toggleSilent.getText()); + } + + @Test + public void testBindNotification_UnSilenceButton_ChannelImportanceUnspecified() + throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_LOW); + final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); + assertEquals(VISIBLE, toggleSilent.getVisibility()); + assertEquals( + mContext.getString(R.string.inline_silent_button_alert), toggleSilent.getText()); + } + @Test public void testBindNotification_MinButton() throws Exception { mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final View block = mNotificationInfo.findViewById(R.id.block); final View minimize = mNotificationInfo.findViewById(R.id.minimize); assertEquals(GONE, block.getVisibility()); @@ -283,7 +356,7 @@ public class NotificationInfoTest extends SysuiTestCase { (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); - }, null, true, false); + }, null, true, false, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); settingsButton.performClick(); @@ -294,7 +367,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -306,7 +380,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); - }, null, false, false); + }, null, false, false, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -314,11 +388,12 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, (View v, NotificationChannel c, int appUid) -> { - }, null, true, false); + }, null, true, false, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertEquals(View.VISIBLE, settingsButton.getVisibility()); } @@ -326,7 +401,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent"); verify(mMetricsLogger, times(0)).count(anyString(), anyInt()); } @@ -335,7 +411,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true, - true, true); + true, true, false, IMPORTANCE_DEFAULT); mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent"); verify(mMetricsLogger, times(1)).count(anyString(), anyInt()); } @@ -348,7 +424,7 @@ public class NotificationInfoTest extends SysuiTestCase { (View v, NotificationChannel c, int appUid) -> { assertEquals(null, c); latch.countDown(); - }, null, true, true); + }, null, true, true, false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.info).performClick(); // Verify that listener was triggered. @@ -361,7 +437,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null, - null, true, true); + null, true, true, false, IMPORTANCE_DEFAULT); final TextView channelNameView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, channelNameView.getVisibility()); @@ -372,7 +448,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null, - null, true, true); + null, true, true, false, IMPORTANCE_DEFAULT); final TextView blockView = mNotificationInfo.findViewById(R.id.block); assertEquals(GONE, blockView.getVisibility()); } @@ -381,7 +457,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testbindNotification_BlockingHelper() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false, - true, true); + true, true, false, IMPORTANCE_DEFAULT); final TextView view = mNotificationInfo.findViewById(R.id.block_prompt); assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText()); @@ -390,7 +466,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); final TextView view = mNotificationInfo.findViewById(R.id.block_prompt); assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.notification_unblockable_desc), @@ -400,7 +477,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); @@ -410,7 +488,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -423,7 +502,8 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); mTestableLooper.processAllMessages(); @@ -431,12 +511,41 @@ public class NotificationInfoTest extends SysuiTestCase { anyString(), eq(TEST_UID), any()); } + @Test + public void testDoesNotUpdateNotificationChannelAfterImportanceChangedSilenced() + throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_DEFAULT); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + mTestableLooper.processAllMessages(); + verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), any()); + } + + @Test + public void testDoesNotUpdateNotificationChannelAfterImportanceChangedUnSilenced() + throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_DEFAULT); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + mTestableLooper.processAllMessages(); + verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), any()); + } + @Test public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged() throws Exception { int originalImportance = mNotificationChannel.getImportance(); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); mTestableLooper.processAllMessages(); @@ -450,7 +559,8 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); @@ -468,7 +578,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , - true, false /* isNonblockable */); + true, false /* isNonblockable */, false, /* isNoisy */IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -489,7 +599,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , - true, false /* isNonblockable */); + true, false /* isNonblockable */, false, /* isNoisy */IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -510,7 +620,7 @@ public class NotificationInfoTest extends SysuiTestCase { null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */); + true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT); NotificationGuts guts = spy(new NotificationGuts(mContext, null)); when(guts.getWindowToken()).thenReturn(mock(IBinder.class)); @@ -538,7 +648,7 @@ public class NotificationInfoTest extends SysuiTestCase { 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */); + true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT); NotificationGuts guts = spy(new NotificationGuts(mContext, null)); when(guts.getWindowToken()).thenReturn(mock(IBinder.class)); @@ -566,7 +676,8 @@ public class NotificationInfoTest extends SysuiTestCase { 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , false /* isNonblockable */, true /* isForBlockingHelper */, - true, true /* isUserSentimentNegative */); + true, true /* isUserSentimentNegative */, false, /* isNoisy */ + IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true /* save */, false /* force */); @@ -585,7 +696,7 @@ public class NotificationInfoTest extends SysuiTestCase { null /* onSettingsClick */, null /* onAppSettingsClick */, true /* provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */); + true /* isUserSentimentNegative */, false, /* isNoisy */IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -607,7 +718,8 @@ public class NotificationInfoTest extends SysuiTestCase { false /* isNonblockable */, true /* isForBlockingHelper */, true, - false /* isUserSentimentNegative */); + false /* isUserSentimentNegative */, + false, /* isNoisy */IMPORTANCE_DEFAULT); NotificationGuts guts = mock(NotificationGuts.class); doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean()); mNotificationInfo.setGutsParent(guts); @@ -621,7 +733,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -634,7 +747,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBlockChangedCallsUpdateNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -666,7 +780,8 @@ public class NotificationInfoTest extends SysuiTestCase { true /*provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */); + true /* isUserSentimentNegative */, + false, /* isNoisy */IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -687,7 +802,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testNonBlockableAppDoesNotBecomeMin() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -701,7 +817,8 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -721,7 +838,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testKeepUpdatesNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); @@ -738,7 +856,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBlockUndoDoesNotBlockNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -759,7 +878,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testMinUndoDoesNotMinNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -776,11 +896,98 @@ public class NotificationInfoTest extends SysuiTestCase { assertEquals(IMPORTANCE_LOW, mNotificationChannel.getImportance()); } + @Test + public void testSilenceCallsUpdateNotificationChannel() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_DEFAULT); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + waitForUndoButton(); + mNotificationInfo.handleCloseControls(true, false); + + mTestableLooper.processAllMessages(); + ArgumentCaptor updated = + ArgumentCaptor.forClass(NotificationChannel.class); + verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), updated.capture()); + assertTrue((updated.getValue().getUserLockedFields() + & USER_LOCKED_IMPORTANCE) != 0); + assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance()); + } + + @Test + public void testUnSilenceCallsUpdateNotificationChannel() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + waitForUndoButton(); + mNotificationInfo.handleCloseControls(true, false); + + mTestableLooper.processAllMessages(); + ArgumentCaptor updated = + ArgumentCaptor.forClass(NotificationChannel.class); + verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), updated.capture()); + assertTrue((updated.getValue().getUserLockedFields() + & USER_LOCKED_IMPORTANCE) != 0); + assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance()); + } + + @Test + public void testSilenceCallsUpdateNotificationChannel_channelImportanceUnspecified() + throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + true, IMPORTANCE_DEFAULT); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + waitForUndoButton(); + mNotificationInfo.handleCloseControls(true, false); + + mTestableLooper.processAllMessages(); + ArgumentCaptor updated = + ArgumentCaptor.forClass(NotificationChannel.class); + verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), updated.capture()); + assertTrue((updated.getValue().getUserLockedFields() + & USER_LOCKED_IMPORTANCE) != 0); + assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance()); + } + + @Test + public void testUnSilenceCallsUpdateNotificationChannel_channelImportanceUnspecified() + throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, + false, IMPORTANCE_LOW); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + waitForUndoButton(); + mNotificationInfo.handleCloseControls(true, false); + + mTestableLooper.processAllMessages(); + ArgumentCaptor updated = + ArgumentCaptor.forClass(NotificationChannel.class); + verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), updated.capture()); + assertTrue((updated.getValue().getUserLockedFields() + & USER_LOCKED_IMPORTANCE) != 0); + assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance()); + } + @Test public void testCloseControlsDoesNotUpdateiMinIfSaveIsFalse() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -795,7 +1002,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -812,7 +1020,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { - }, null, null, true, true); + }, null, null, true, true, false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -830,7 +1038,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { saveImportance.run(); - }, null, null, true, false); + }, null, null, true, false, false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -866,7 +1074,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, (View v, Intent intent) -> { latch.countDown(); - }, true, false); + }, true, false, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); settingsLink.performClick(); @@ -894,7 +1102,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null, (View v, Intent intent) -> { latch.countDown(); - }, true, false); + }, true, false, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); settingsLink.performClick(); @@ -913,7 +1121,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null, - null, true, false); + null, true, false, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -933,7 +1141,8 @@ public class NotificationInfoTest extends SysuiTestCase { 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false); + TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false, + false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -956,7 +1165,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true, - true, true); + true, true, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -972,7 +1181,8 @@ public class NotificationInfoTest extends SysuiTestCase { mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -984,7 +1194,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testUndoText_block() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -992,11 +1203,40 @@ public class NotificationInfoTest extends SysuiTestCase { assertTrue(confirmationText.getText().toString().contains("won't see")); } + @Test + public void testUndoText_silence() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + true, IMPORTANCE_DEFAULT); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + waitForUndoButton(); + TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text); + assertEquals(mContext.getString(R.string.notification_channel_silenced), + confirmationText.getText()); + } + + @Test + public void testUndoText_unsilence() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + true, IMPORTANCE_DEFAULT); + + mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); + waitForUndoButton(); + TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text); + assertEquals(mContext.getString(R.string.notification_channel_unsilenced), + confirmationText.getText()); + } + @Test public void testNoHeaderOnConfirmation() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -1007,7 +1247,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testHeaderOnUndo() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f87a5f7a1a521..b404c41c211dd 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -6564,6 +6564,7 @@ public class NotificationManagerService extends SystemService { Bundle smartActions = new Bundle(); Bundle smartReplies = new Bundle(); Bundle audiblyAlerted = new Bundle(); + Bundle noisy = new Bundle(); for (int i = 0; i < N; i++) { NotificationRecord record = mNotificationList.get(i); if (!isVisibleToListener(record.sbn, info)) { @@ -6594,6 +6595,7 @@ public class NotificationManagerService extends SystemService { smartActions.putParcelableArrayList(key, record.getSmartActions()); smartReplies.putCharSequenceArrayList(key, record.getSmartReplies()); audiblyAlerted.putBoolean(key, record.getAudiblyAlerted()); + noisy.putBoolean(key, record.getSound() != null || record.getVibration() != null); } final int M = keys.size(); String[] keysAr = keys.toArray(new String[M]); @@ -6605,7 +6607,7 @@ public class NotificationManagerService extends SystemService { return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides, suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys, channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden, - smartActions, smartReplies, audiblyAlerted); + smartActions, smartReplies, audiblyAlerted, noisy); } 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 0d7b5843f47e9..bcba15df8756d 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java @@ -114,6 +114,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { Bundle smartActions = new Bundle(); Bundle smartReplies = new Bundle(); Bundle audiblyAlerted = new Bundle(); + Bundle noisy = new Bundle(); for (int i = 0; i < mKeys.length; i++) { String key = mKeys[i]; @@ -134,12 +135,13 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { smartActions.putParcelableArrayList(key, getSmartActions(key, i)); smartReplies.putCharSequenceArrayList(key, getSmartReplies(key, i)); audiblyAlerted.putBoolean(key, audiblyAlerted(i)); + noisy.putBoolean(key, getNoisy(i)); } NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys, interceptedKeys.toArray(new String[0]), visibilityOverrides, suppressedVisualEffects, importance, explanation, overrideGroupKeys, channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden, - smartActions, smartReplies, audiblyAlerted); + smartActions, smartReplies, audiblyAlerted, noisy); return update; } @@ -195,6 +197,10 @@ public class NotificationListenerServiceTest extends UiServiceTestCase { return index < 2; } + private boolean getNoisy(int index) { + return index < 1; + } + private ArrayList getPeople(String key, int index) { ArrayList people = new ArrayList<>(); for (int i = 0; i < index; i++) {