diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 68552d7d738f4..3741f0c2ae563 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -2622,7 +2622,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * except for legacy use cases. */ public boolean canShowHeadsUp() { - if (mOnKeyguard && !isDozing() && !isBypassEnabled()) { + if (mOnKeyguard && !isDozing() && !isBypassEnabled() && !mEntry.isStickyAndNotDemoted()) { return false; } return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt index 25f99c69d4543..092242814869d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt @@ -264,12 +264,18 @@ constructor( onLockscreen: Boolean ): Float { assert(view.isShowable(onLockscreen)) + var size = if (onLockscreen) { - view.getMinHeight(/* ignoreTemporaryStates= */ true).toFloat() + if (view is ExpandableNotificationRow && view.entry.isStickyAndNotDemoted) { + view.intrinsicHeight.toFloat() + } else { + view.getMinHeight(/* ignoreTemporaryStates= */ true).toFloat() + } } else { view.intrinsicHeight.toFloat() } + size += calculateGapAndDividerHeight(stack, previousView, current = view, visibleIndex) return size } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 8ef28ecbf5bcf..b1fb13e0e89f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -763,7 +763,7 @@ public class StackScrollAlgorithm { float unmodifiedEndLocation = childState.getYTranslation() + childState.height; if (mIsExpanded) { if (row.mustStayOnScreen() && !childState.headsUpIsVisible - && !row.showingPulsing()) { + && !row.showingPulsing() && !ambientState.isOnKeyguard()) { // Ensure that the heads up is always visible even when scrolled off clampHunToTop(mQuickQsOffsetHeight, ambientState.getStackTranslation(), row.getCollapsedHeight(), childState); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 1cd182b36c0a7..7d022192f9350 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -67,6 +67,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import org.junit.Assert; import org.junit.Before; @@ -104,6 +105,61 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { mNotificationTestHelper.setFeatureFlags(fakeFeatureFlags); } + @Test + public void testCanShowHeadsUp_notOnKeyguard_true() throws Exception { + ExpandableNotificationRow row = mNotificationTestHelper.createRow(); + + row.setOnKeyguard(false); + + assertTrue(row.canShowHeadsUp()); + } + + @Test + public void testCanShowHeadsUp_dozing_true() throws Exception { + ExpandableNotificationRow row = mNotificationTestHelper.createRow(); + + StatusBarStateController statusBarStateControllerMock = + mNotificationTestHelper.getStatusBarStateController(); + when(statusBarStateControllerMock.isDozing()).thenReturn(true); + + assertTrue(row.canShowHeadsUp()); + } + + @Test + public void testCanShowHeadsUp_bypassEnabled_true() throws Exception { + ExpandableNotificationRow row = mNotificationTestHelper.createRow(); + + KeyguardBypassController keyguardBypassControllerMock = + mNotificationTestHelper.getKeyguardBypassController(); + when(keyguardBypassControllerMock.getBypassEnabled()).thenReturn(true); + + assertTrue(row.canShowHeadsUp()); + } + + @Test + public void testCanShowHeadsUp_stickyAndNotDemoted_true() throws Exception { + ExpandableNotificationRow row = mNotificationTestHelper.createStickyRow(); + + assertTrue(row.canShowHeadsUp()); + } + + @Test + public void testCanShowHeadsUp_false() throws Exception { + ExpandableNotificationRow row = mNotificationTestHelper.createRow(); + + row.setOnKeyguard(true); + + StatusBarStateController statusBarStateControllerMock = + mNotificationTestHelper.getStatusBarStateController(); + when(statusBarStateControllerMock.isDozing()).thenReturn(false); + + KeyguardBypassController keyguardBypassControllerMock = + mNotificationTestHelper.getKeyguardBypassController(); + when(keyguardBypassControllerMock.getBypassEnabled()).thenReturn(false); + + assertFalse(row.canShowHeadsUp()); + } + @Test public void testUpdateBackgroundColors_isRecursive() throws Exception { ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index f92678fc31a8e..b0a46e15803fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.row; import static android.app.Notification.FLAG_BUBBLE; +import static android.app.Notification.FLAG_FSI_REQUESTED_BUT_DENIED; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; @@ -74,6 +75,7 @@ import com.android.systemui.statusbar.notification.people.PeopleNotificationIden import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; +import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.InflatedSmartReplyState; @@ -121,6 +123,7 @@ public class NotificationTestHelper { private final RowContentBindStage mBindStage; private final IconManager mIconManager; private final StatusBarStateController mStatusBarStateController; + private final KeyguardBypassController mKeyguardBypassController; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; private final OnUserInteractionCallback mOnUserInteractionCallback; private final NotificationDismissibilityProvider mDismissibilityProvider; @@ -139,6 +142,7 @@ public class NotificationTestHelper { dependency.injectMockDependency(MediaOutputDialogFactory.class); mMockLogger = mock(ExpandableNotificationRowLogger.class); mStatusBarStateController = mock(StatusBarStateController.class); + mKeyguardBypassController = mock(KeyguardBypassController.class); mGroupMembershipManager = mock(GroupMembershipManager.class); mGroupExpansionManager = mock(GroupExpansionManager.class); mHeadsUpManager = mock(HeadsUpManagerPhone.class); @@ -308,6 +312,21 @@ public class NotificationTestHelper { return row; } + /** + * Returns an {@link ExpandableNotificationRow} that shows as a sticky FSI HUN. + */ + public ExpandableNotificationRow createStickyRow() + throws Exception { + Notification n = createNotification(false /* isGroupSummary */, + null /* groupKey */, + makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */)); + n.flags |= FLAG_FSI_REQUESTED_BUT_DENIED; + ExpandableNotificationRow row = generateRow(n, PKG, UID, USER_HANDLE, + mDefaultInflationFlags, IMPORTANCE_HIGH); + return row; + } + + /** * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble. */ @@ -483,6 +502,10 @@ public class NotificationTestHelper { return mStatusBarStateController; } + public KeyguardBypassController getKeyguardBypassController() { + return mKeyguardBypassController; + } + private ExpandableNotificationRow generateRow( Notification notification, String pkg, @@ -541,7 +564,7 @@ public class NotificationTestHelper { APP_NAME, entry.getKey(), mMockLogger, - mock(KeyguardBypassController.class), + mKeyguardBypassController, mGroupMembershipManager, mGroupExpansionManager, mHeadsUpManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt index 55dae9d04019f..e6f10cdafe03a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt @@ -218,6 +218,25 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { assertThat(space).isEqualTo(5) } + @Test + fun spaceNeeded_fsiHunOnLockscreen_usesIntrinsicHeight() { + setGapHeight(0f) + // No divider height since we're testing one element where index = 0 + + val expandableView = createMockStickyRow(rowHeight) + whenever(expandableView.getMinHeight(any())).thenReturn(5) + whenever(expandableView.intrinsicHeight).thenReturn(10) + + val space = + sizeCalculator.spaceNeeded( + expandableView, + visibleIndex = 0, + previousView = null, + stack = stackLayout, + onLockscreen = true) + assertThat(space).isEqualTo(10) + } + @Test fun spaceNeeded_notOnLockscreen_usesIntrinsicHeight() { setGapHeight(0f) @@ -276,6 +295,25 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { return row } + private fun createMockStickyRow( + height: Float = rowHeight, + isRemoved: Boolean = false, + visibility: Int = VISIBLE + ): ExpandableNotificationRow { + val row = mock(ExpandableNotificationRow::class.java) + val entry = mock(NotificationEntry::class.java) + whenever(entry.isStickyAndNotDemoted).thenReturn(true) + + val sbn = mock(StatusBarNotification::class.java) + whenever(entry.sbn).thenReturn(sbn) + whenever(row.entry).thenReturn(entry) + whenever(row.isRemoved).thenReturn(isRemoved) + whenever(row.visibility).thenReturn(visibility) + whenever(row.getMinHeight(any())).thenReturn(height.toInt()) + whenever(row.intrinsicHeight).thenReturn(height.toInt()) + return row + } + private fun setGapHeight(height: Float) { whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())).thenReturn(height) whenever(stackLayout.calculateGapHeight(nullable(), nullable(), /* visibleIndex= */ eq(0)))