diff options
5 files changed, 204 insertions, 6 deletions
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 759d1ed0393f..b1cb6cf91cb8 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -29,6 +29,7 @@ <!-- Package Info --> <RelativeLayout + android:id="@+id/header" android:layout_width="match_parent" android:layout_height="wrap_content" android:clipChildren="false" @@ -140,6 +141,12 @@ android:layout_height="match_parent" style="@style/TextAppearance.NotificationInfo.Button"/> <TextView + android:id="@+id/minimize" + android:text="@string/inline_minimize_button" + android:layout_width="wrap_content" + android:layout_height="match_parent" + style="@style/TextAppearance.NotificationInfo.Button" /> + <TextView android:id="@+id/keep" android:text="@string/inline_keep_button" android:layout_width="wrap_content" @@ -157,10 +164,11 @@ android:visibility="gone" android:orientation="horizontal" > <TextView + android:id="@+id/confirmation_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/notification_channel_disabled" - style="@style/TextAppearance.NotificationInfo.Secondary.Warning"/> + style="@style/TextAppearance.NotificationInfo.Confirmation"/> <TextView android:id="@+id/undo" android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 89e6da38ffa7..4bd997d9b989 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1526,6 +1526,9 @@ <!-- Notification Inline Controls: Shown when a channel's notifications are currently blocked --> <string name="notification_channel_disabled">You won\'t see these notifications anymore</string> + <!-- Notification inline controls: Shown when a channel's notifications are minimized --> + <string name="notification_channel_minimized">These notifications will be minimized</string> + <!-- Notification Inline controls: continue receiving notifications prompt, channel level --> <string name="inline_blocking_helper">You usually dismiss these notifications. \nKeep showing them?</string> @@ -1539,6 +1542,9 @@ <!-- Notification inline controls: keep getting notifications button --> <string name="inline_keep_button">Keep showing</string> + <!-- Notification inline controls: minimize notifications button --> + <string name="inline_minimize_button">Minimize</string> + <!-- Notification Inline controls: continue receiving notifications prompt, app level --> <string name="inline_keep_showing_app">Keep showing notifications from this app?</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index d65e37d3e93e..1470dfa4d05d 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -459,6 +459,12 @@ <item name="android:alpha">0.87</item> </style> + <style name="TextAppearance.NotificationInfo.Confirmation"> + <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:textSize">14sp</item> + <item name="android:alpha">0.87</item> + </style> + <style name="TextAppearance.NotificationInfo.Secondary"> <item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:textSize">14sp</item> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java index afe906c28ea5..b1ad30c940fb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar; +import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; import android.animation.Animator; @@ -39,6 +40,7 @@ import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; +import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.widget.ImageView; import android.widget.LinearLayout; @@ -73,6 +75,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private boolean mNonblockable; private StatusBarNotification mSbn; private AnimatorSet mExpandAnimation; + private boolean mIsForeground; private CheckSaveListener mCheckSaveListener; private OnSettingsClickListener mOnSettingsClickListener; @@ -84,7 +87,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G closeControls(v); }; - private OnClickListener mOnStopNotifications = v -> { + private OnClickListener mOnStopMinNotifications = v -> { swapContent(false); }; @@ -150,6 +153,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mSingleNotificationChannel = notificationChannel; mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance(); mNegativeUserSentiment = negativeUserSentiment; + mIsForeground = + (mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage( pkg, mAppUid, false /* includeDeleted */); @@ -290,14 +295,23 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private void bindButtons() { View block = findViewById(R.id.block); - block.setOnClickListener(mOnStopNotifications); + block.setOnClickListener(mOnStopMinNotifications); TextView keep = findViewById(R.id.keep); keep.setOnClickListener(mOnKeepShowing); findViewById(R.id.undo).setOnClickListener(mOnUndo); + View minimize = findViewById(R.id.minimize); + minimize.setOnClickListener(mOnStopMinNotifications); if (mNonblockable) { keep.setText(R.string.notification_done); block.setVisibility(GONE); + minimize.setVisibility(GONE); + } else if (mIsForeground) { + block.setVisibility(GONE); + minimize.setVisibility(VISIBLE); + } else if (!mIsForeground) { + block.setVisibility(VISIBLE); + minimize.setVisibility(GONE); } // app settings link @@ -306,7 +320,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mSbn.getId(), mSbn.getTag()); if (settingsIntent != null && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) { - settingsLinkView.setVisibility(View.VISIBLE); + settingsLinkView.setVisibility(VISIBLE); settingsLinkView.setText(mContext.getString(R.string.notification_app_settings, mSbn.getNotification().getSettingsText())); settingsLinkView.setOnClickListener((View view) -> { @@ -322,14 +336,21 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mExpandAnimation.cancel(); } + View prompt = findViewById(R.id.prompt); + ViewGroup confirmation = findViewById(R.id.confirmation); + 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); } - View prompt = findViewById(R.id.prompt); - View confirmation = findViewById(R.id.confirmation); ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA, prompt.getAlpha(), showPrompt ? 1f : 0f); promptAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT); @@ -339,6 +360,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G prompt.setVisibility(showPrompt ? VISIBLE : GONE); confirmation.setVisibility(showPrompt ? GONE : VISIBLE); + header.setVisibility(showPrompt ? VISIBLE : GONE); mExpandAnimation = new AnimatorSet(); mExpandAnimation.playTogether(promptAnim, confirmAnim); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java index b8d9b192aac4..01664b28dba1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar; import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE; 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.view.View.GONE; @@ -147,6 +149,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); final TextView textView = mNotificationInfo.findViewById(R.id.pkgname); assertTrue(textView.getText().toString().contains("App Name")); + assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); } @Test @@ -213,6 +216,27 @@ 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, null); + final View block = mNotificationInfo.findViewById(R.id.block); + final View minimize = mNotificationInfo.findViewById(R.id.minimize); + assertEquals(VISIBLE, block.getVisibility()); + assertEquals(GONE, minimize.getVisibility()); + } + + @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, null); + final View block = mNotificationInfo.findViewById(R.id.block); + final View minimize = mNotificationInfo.findViewById(R.id.minimize); + assertEquals(GONE, block.getVisibility()); + assertEquals(VISIBLE, minimize.getVisibility()); + } + + @Test public void testBindNotification_SetsOnClickListenerForSettings() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, @@ -324,6 +348,18 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test + public void testDoesNotUpdateNotificationChannelAfterImportanceChangedMin() + throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + + mNotificationInfo.findViewById(R.id.minimize).performClick(); + verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), any()); + } + + @Test public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged() throws Exception { int originalImportance = mNotificationChannel.getImportance(); @@ -377,6 +413,39 @@ public class NotificationInfoTest extends SysuiTestCase { anyString(), eq(TEST_UID), updated.capture()); assertTrue((updated.getValue().getUserLockedFields() & USER_LOCKED_IMPORTANCE) != 0); + assertEquals(IMPORTANCE_NONE, updated.getValue().getImportance()); + } + + @Test + public void testNonBlockableAppDoesNotBecomeMin() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, + null, Collections.singleton(TEST_PACKAGE_NAME)); + mNotificationInfo.findViewById(R.id.minimize).performClick(); + waitForUndoButton(); + verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), any()); + } + + @Test + public void testMinChangedCallsUpdateNotificationChannel() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + + mNotificationInfo.findViewById(R.id.minimize).performClick(); + waitForUndoButton(); + mNotificationInfo.handleCloseControls(true, false); + + ArgumentCaptor<NotificationChannel> 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_MIN, updated.getValue().getImportance()); } @Test @@ -416,6 +485,40 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test + public void testMinUndoDoesNotMinNotificationChannel() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + + mNotificationInfo.findViewById(R.id.minimize).performClick(); + waitForUndoButton(); + mNotificationInfo.findViewById(R.id.undo).performClick(); + waitForStopButton(); + mNotificationInfo.handleCloseControls(true, false); + + ArgumentCaptor<NotificationChannel> updated = + ArgumentCaptor.forClass(NotificationChannel.class); + verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), updated.capture()); + assertTrue(0 != (mNotificationChannel.getUserLockedFields() & USER_LOCKED_IMPORTANCE)); + assertEquals(IMPORTANCE_LOW, mNotificationChannel.getImportance()); + } + + @Test + public void testCloseControlsDoesNotUpdateiMinIfSaveIsFalse() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, + Collections.singleton(TEST_PACKAGE_NAME)); + + mNotificationInfo.findViewById(R.id.minimize).performClick(); + waitForUndoButton(); + mNotificationInfo.handleCloseControls(false, false); + verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( + eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel)); + } + + @Test public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, @@ -557,4 +660,57 @@ public class NotificationInfoTest extends SysuiTestCase { public void testWillBeRemovedReturnsFalseBeforeBind() throws Exception { assertFalse(mNotificationInfo.willBeRemoved()); } + + @Test + public void testUndoText_min() throws Exception { + mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, + Collections.singleton(TEST_PACKAGE_NAME)); + + mNotificationInfo.findViewById(R.id.minimize).performClick(); + waitForUndoButton(); + TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text); + assertTrue(confirmationText.getText().toString().contains("minimized")); + } + + @Test + public void testUndoText_block() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, + Collections.singleton(TEST_PACKAGE_NAME)); + + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); + TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text); + assertTrue(confirmationText.getText().toString().contains("won't see")); + } + + @Test + public void testNoHeaderOnConfirmation() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, + Collections.singleton(TEST_PACKAGE_NAME)); + + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); + assertEquals(GONE, mNotificationInfo.findViewById(R.id.header).getVisibility()); + } + + @Test + public void testHeaderOnUndo() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, + Collections.singleton(TEST_PACKAGE_NAME)); + + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); + mNotificationInfo.findViewById(R.id.undo).performClick(); + waitForStopButton(); + assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); + } } |