Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +14 −8 Original line number Diff line number Diff line Loading @@ -315,7 +315,8 @@ public class StackScrollAlgorithm { float newNotificationEnd = newYTranslation + newHeight; boolean isHeadsUp = (child instanceof ExpandableNotificationRow) && child.isPinned(); if (mClipNotificationScrollToTop && ((isHeadsUp && !firstHeadsUp) || child.isHeadsUpAnimatingAway()) && !firstHeadsUp && (isHeadsUp || child.isHeadsUpAnimatingAway()) && newNotificationEnd > firstHeadsUpEnd && !ambientState.isShadeExpanded()) { // The bottom of this view is peeking out from under the previous view. Loading Loading @@ -619,13 +620,12 @@ public class StackScrollAlgorithm { updateViewWithShelf(view, viewState, shelfStart); } } // Avoid pulsing notification flicker during AOD to LS // A pulsing notification is already expanded, no need to expand it again with animation if (ambientState.isPulsingRow(view)) { expansionFraction = 1.0f; viewState.height = getMaxAllowedChildHeight(view); if (!view.isPinned() && !view.isHeadsUpAnimatingAway() && !ambientState.isPulsingRow(view)) { // The expansion fraction should not affect HUNs or pulsing notifications. viewState.height *= expansionFraction; } // Clip height of view right before shelf. viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction); } algorithmState.mCurrentYPosition += Loading Loading @@ -785,6 +785,8 @@ public class StackScrollAlgorithm { } } if (row.isPinned()) { // Make sure row yTranslation is at maximum the HUN yTranslation, // which accounts for AmbientState.stackTopMargin in split-shade. childState.setYTranslation( Math.max(childState.getYTranslation(), headsUpTranslation)); childState.height = Math.max(row.getIntrinsicHeight(), childState.height); Loading @@ -809,7 +811,11 @@ public class StackScrollAlgorithm { } } if (row.isHeadsUpAnimatingAway()) { childState.setYTranslation(Math.max(childState.getYTranslation(), mHeadsUpInset)); // Make sure row yTranslation is at maximum the HUN yTranslation, // which accounts for AmbientState.stackTopMargin in split-shade. childState.setYTranslation( Math.max(childState.getYTranslation(), headsUpTranslation)); // keep it visible for the animation childState.hidden = false; } } Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt +96 −14 Original line number Diff line number Diff line Loading @@ -35,7 +35,6 @@ import org.mockito.Mockito.`when` as whenever @SmallTest class StackScrollAlgorithmTest : SysuiTestCase() { @JvmField @Rule var expect: Expect = Expect.create() Loading Loading @@ -82,26 +81,58 @@ class StackScrollAlgorithmTest : SysuiTestCase() { fun resetViewStates_defaultHun_yTranslationIsInset() { whenever(notificationRow.isPinned).thenReturn(true) whenever(notificationRow.isHeadsUp).thenReturn(true) stackScrollAlgorithm.resetViewStates(ambientState, 0) assertThat(notificationRow.viewState.yTranslation) .isEqualTo(stackScrollAlgorithm.mHeadsUpInset) resetViewStates_hunYTranslationIsInset() } @Test fun resetViewStates_stackMargin_changesHunYTranslation() { fun resetViewStates_defaultHunWithStackMargin_changesHunYTranslation() { whenever(notificationRow.isPinned).thenReturn(true) whenever(notificationRow.isHeadsUp).thenReturn(true) val minHeadsUpTranslation = context.resources .getDimensionPixelSize(R.dimen.notification_side_paddings) resetViewStates_stackMargin_changesHunYTranslation() } // split shade case with top margin introduced by shade's status bar ambientState.stackTopMargin = 100 stackScrollAlgorithm.resetViewStates(ambientState, 0) @Test fun resetViewStates_hunAnimatingAway_yTranslationIsInset() { whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true) resetViewStates_hunYTranslationIsInset() } // top margin presence should decrease heads up translation up to minHeadsUpTranslation assertThat(notificationRow.viewState.yTranslation).isEqualTo(minHeadsUpTranslation) @Test fun resetViewStates_hunAnimatingAway_StackMarginChangesHunYTranslation() { whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true) resetViewStates_stackMargin_changesHunYTranslation() } @Test fun resetViewStates_hunAnimatingAway_bottomNotClipped() { whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true) stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0) assertThat(notificationRow.viewState.clipBottomAmount).isEqualTo(0) } @Test fun resetViewStates_hunsOverlapping_bottomHunClipped() { val topHun = mockExpandableNotificationRow() val bottomHun = mockExpandableNotificationRow() whenever(topHun.isHeadsUp).thenReturn(true) whenever(topHun.isPinned).thenReturn(true) whenever(bottomHun.isHeadsUp).thenReturn(true) whenever(bottomHun.isPinned).thenReturn(true) resetViewStates_hunsOverlapping_bottomHunClipped(topHun, bottomHun) } @Test fun resetViewStates_hunsOverlappingAndBottomHunAnimatingAway_bottomHunClipped() { val topHun = mockExpandableNotificationRow() val bottomHun = mockExpandableNotificationRow() whenever(topHun.isHeadsUp).thenReturn(true) whenever(topHun.isPinned).thenReturn(true) whenever(bottomHun.isHeadsUpAnimatingAway).thenReturn(true) resetViewStates_hunsOverlapping_bottomHunClipped(topHun, bottomHun) } @Test Loading Loading @@ -855,6 +886,57 @@ class StackScrollAlgorithmTest : SysuiTestCase() { ambientState.stackHeight = ambientState.stackEndHeight * fraction } private fun resetViewStates_hunYTranslationIsInset() { stackScrollAlgorithm.resetViewStates(ambientState, 0) assertThat(notificationRow.viewState.yTranslation) .isEqualTo(stackScrollAlgorithm.mHeadsUpInset) } private fun resetViewStates_stackMargin_changesHunYTranslation() { val stackTopMargin = 50 val headsUpTranslationY = stackScrollAlgorithm.mHeadsUpInset - stackTopMargin // we need the shelf to mock the real-life behaviour of StackScrollAlgorithm#updateChild ambientState.shelf = notificationShelf // split shade case with top margin introduced by shade's status bar ambientState.stackTopMargin = stackTopMargin stackScrollAlgorithm.resetViewStates(ambientState, 0) // heads up translation should be decreased by the top margin assertThat(notificationRow.viewState.yTranslation).isEqualTo(headsUpTranslationY) } private fun resetViewStates_hunsOverlapping_bottomHunClipped( topHun: ExpandableNotificationRow, bottomHun: ExpandableNotificationRow ) { val topHunHeight = mContext.resources.getDimensionPixelSize( R.dimen.notification_content_min_height) val bottomHunHeight = mContext.resources.getDimensionPixelSize( R.dimen.notification_max_heads_up_height) whenever(topHun.intrinsicHeight).thenReturn(topHunHeight) whenever(bottomHun.intrinsicHeight).thenReturn(bottomHunHeight) // we need the shelf to mock the real-life behaviour of StackScrollAlgorithm#updateChild ambientState.shelf = notificationShelf // add two overlapping HUNs hostView.removeAllViews() hostView.addView(topHun) hostView.addView(bottomHun) stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0) // the height shouldn't change assertThat(topHun.viewState.height).isEqualTo(topHunHeight) assertThat(bottomHun.viewState.height).isEqualTo(bottomHunHeight) // the HUN at the bottom should be clipped assertThat(topHun.viewState.clipBottomAmount).isEqualTo(0) assertThat(bottomHun.viewState.clipBottomAmount).isEqualTo(bottomHunHeight - topHunHeight) } private fun resetViewStates_expansionChanging_notificationAlphaUpdated( expansionFraction: Float, expectedAlpha: Float, Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +14 −8 Original line number Diff line number Diff line Loading @@ -315,7 +315,8 @@ public class StackScrollAlgorithm { float newNotificationEnd = newYTranslation + newHeight; boolean isHeadsUp = (child instanceof ExpandableNotificationRow) && child.isPinned(); if (mClipNotificationScrollToTop && ((isHeadsUp && !firstHeadsUp) || child.isHeadsUpAnimatingAway()) && !firstHeadsUp && (isHeadsUp || child.isHeadsUpAnimatingAway()) && newNotificationEnd > firstHeadsUpEnd && !ambientState.isShadeExpanded()) { // The bottom of this view is peeking out from under the previous view. Loading Loading @@ -619,13 +620,12 @@ public class StackScrollAlgorithm { updateViewWithShelf(view, viewState, shelfStart); } } // Avoid pulsing notification flicker during AOD to LS // A pulsing notification is already expanded, no need to expand it again with animation if (ambientState.isPulsingRow(view)) { expansionFraction = 1.0f; viewState.height = getMaxAllowedChildHeight(view); if (!view.isPinned() && !view.isHeadsUpAnimatingAway() && !ambientState.isPulsingRow(view)) { // The expansion fraction should not affect HUNs or pulsing notifications. viewState.height *= expansionFraction; } // Clip height of view right before shelf. viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction); } algorithmState.mCurrentYPosition += Loading Loading @@ -785,6 +785,8 @@ public class StackScrollAlgorithm { } } if (row.isPinned()) { // Make sure row yTranslation is at maximum the HUN yTranslation, // which accounts for AmbientState.stackTopMargin in split-shade. childState.setYTranslation( Math.max(childState.getYTranslation(), headsUpTranslation)); childState.height = Math.max(row.getIntrinsicHeight(), childState.height); Loading @@ -809,7 +811,11 @@ public class StackScrollAlgorithm { } } if (row.isHeadsUpAnimatingAway()) { childState.setYTranslation(Math.max(childState.getYTranslation(), mHeadsUpInset)); // Make sure row yTranslation is at maximum the HUN yTranslation, // which accounts for AmbientState.stackTopMargin in split-shade. childState.setYTranslation( Math.max(childState.getYTranslation(), headsUpTranslation)); // keep it visible for the animation childState.hidden = false; } } Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt +96 −14 Original line number Diff line number Diff line Loading @@ -35,7 +35,6 @@ import org.mockito.Mockito.`when` as whenever @SmallTest class StackScrollAlgorithmTest : SysuiTestCase() { @JvmField @Rule var expect: Expect = Expect.create() Loading Loading @@ -82,26 +81,58 @@ class StackScrollAlgorithmTest : SysuiTestCase() { fun resetViewStates_defaultHun_yTranslationIsInset() { whenever(notificationRow.isPinned).thenReturn(true) whenever(notificationRow.isHeadsUp).thenReturn(true) stackScrollAlgorithm.resetViewStates(ambientState, 0) assertThat(notificationRow.viewState.yTranslation) .isEqualTo(stackScrollAlgorithm.mHeadsUpInset) resetViewStates_hunYTranslationIsInset() } @Test fun resetViewStates_stackMargin_changesHunYTranslation() { fun resetViewStates_defaultHunWithStackMargin_changesHunYTranslation() { whenever(notificationRow.isPinned).thenReturn(true) whenever(notificationRow.isHeadsUp).thenReturn(true) val minHeadsUpTranslation = context.resources .getDimensionPixelSize(R.dimen.notification_side_paddings) resetViewStates_stackMargin_changesHunYTranslation() } // split shade case with top margin introduced by shade's status bar ambientState.stackTopMargin = 100 stackScrollAlgorithm.resetViewStates(ambientState, 0) @Test fun resetViewStates_hunAnimatingAway_yTranslationIsInset() { whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true) resetViewStates_hunYTranslationIsInset() } // top margin presence should decrease heads up translation up to minHeadsUpTranslation assertThat(notificationRow.viewState.yTranslation).isEqualTo(minHeadsUpTranslation) @Test fun resetViewStates_hunAnimatingAway_StackMarginChangesHunYTranslation() { whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true) resetViewStates_stackMargin_changesHunYTranslation() } @Test fun resetViewStates_hunAnimatingAway_bottomNotClipped() { whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true) stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0) assertThat(notificationRow.viewState.clipBottomAmount).isEqualTo(0) } @Test fun resetViewStates_hunsOverlapping_bottomHunClipped() { val topHun = mockExpandableNotificationRow() val bottomHun = mockExpandableNotificationRow() whenever(topHun.isHeadsUp).thenReturn(true) whenever(topHun.isPinned).thenReturn(true) whenever(bottomHun.isHeadsUp).thenReturn(true) whenever(bottomHun.isPinned).thenReturn(true) resetViewStates_hunsOverlapping_bottomHunClipped(topHun, bottomHun) } @Test fun resetViewStates_hunsOverlappingAndBottomHunAnimatingAway_bottomHunClipped() { val topHun = mockExpandableNotificationRow() val bottomHun = mockExpandableNotificationRow() whenever(topHun.isHeadsUp).thenReturn(true) whenever(topHun.isPinned).thenReturn(true) whenever(bottomHun.isHeadsUpAnimatingAway).thenReturn(true) resetViewStates_hunsOverlapping_bottomHunClipped(topHun, bottomHun) } @Test Loading Loading @@ -855,6 +886,57 @@ class StackScrollAlgorithmTest : SysuiTestCase() { ambientState.stackHeight = ambientState.stackEndHeight * fraction } private fun resetViewStates_hunYTranslationIsInset() { stackScrollAlgorithm.resetViewStates(ambientState, 0) assertThat(notificationRow.viewState.yTranslation) .isEqualTo(stackScrollAlgorithm.mHeadsUpInset) } private fun resetViewStates_stackMargin_changesHunYTranslation() { val stackTopMargin = 50 val headsUpTranslationY = stackScrollAlgorithm.mHeadsUpInset - stackTopMargin // we need the shelf to mock the real-life behaviour of StackScrollAlgorithm#updateChild ambientState.shelf = notificationShelf // split shade case with top margin introduced by shade's status bar ambientState.stackTopMargin = stackTopMargin stackScrollAlgorithm.resetViewStates(ambientState, 0) // heads up translation should be decreased by the top margin assertThat(notificationRow.viewState.yTranslation).isEqualTo(headsUpTranslationY) } private fun resetViewStates_hunsOverlapping_bottomHunClipped( topHun: ExpandableNotificationRow, bottomHun: ExpandableNotificationRow ) { val topHunHeight = mContext.resources.getDimensionPixelSize( R.dimen.notification_content_min_height) val bottomHunHeight = mContext.resources.getDimensionPixelSize( R.dimen.notification_max_heads_up_height) whenever(topHun.intrinsicHeight).thenReturn(topHunHeight) whenever(bottomHun.intrinsicHeight).thenReturn(bottomHunHeight) // we need the shelf to mock the real-life behaviour of StackScrollAlgorithm#updateChild ambientState.shelf = notificationShelf // add two overlapping HUNs hostView.removeAllViews() hostView.addView(topHun) hostView.addView(bottomHun) stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0) // the height shouldn't change assertThat(topHun.viewState.height).isEqualTo(topHunHeight) assertThat(bottomHun.viewState.height).isEqualTo(bottomHunHeight) // the HUN at the bottom should be clipped assertThat(topHun.viewState.clipBottomAmount).isEqualTo(0) assertThat(bottomHun.viewState.clipBottomAmount).isEqualTo(bottomHunHeight - topHunHeight) } private fun resetViewStates_expansionChanging_notificationAlphaUpdated( expansionFraction: Float, expectedAlpha: Float, Loading