Merge "Fix Notification clipping flicker during AOD=>LS" into udc-dev
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index ae7c216..b0f3f59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -93,6 +93,12 @@
private boolean mAppearing;
private float mPulseHeight = MAX_PULSE_HEIGHT;
+ /**
+ * The ExpandableNotificationRow that is pulsing, or the one that was pulsing
+ * when the device started to transition from AOD to LockScreen.
+ */
+ private ExpandableNotificationRow mPulsingRow;
+
/** Fraction of lockscreen to shade animation (on lockscreen swipe down). */
private float mFractionToShade;
@@ -564,6 +570,19 @@
return mPulsing && entry.isAlerting();
}
+ public void setPulsingRow(ExpandableNotificationRow row) {
+ mPulsingRow = row;
+ }
+
+ /**
+ * @param row The row to check
+ * @return true if row is the pulsing row when the device started to transition from AOD to lock
+ * screen
+ */
+ public boolean isPulsingRow(ExpandableView row) {
+ return mPulsingRow == row;
+ }
+
public boolean isPanelTracking() {
return mPanelTracking;
}
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 92d767a..6f1c378 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
@@ -548,7 +548,7 @@
ExpandableViewState viewState = view.getViewState();
viewState.location = ExpandableViewState.LOCATION_UNKNOWN;
- final float expansionFraction = getExpansionFractionWithoutShelf(
+ float expansionFraction = getExpansionFractionWithoutShelf(
algorithmState, ambientState);
// Add gap between sections.
@@ -619,6 +619,11 @@
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;
+ }
// Clip height of view right before shelf.
viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction);
}
@@ -700,9 +705,11 @@
&& !(child instanceof FooterView);
}
- private void updatePulsingStates(StackScrollAlgorithmState algorithmState,
+ @VisibleForTesting
+ void updatePulsingStates(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
+ ExpandableNotificationRow pulsingRow = null;
for (int i = 0; i < childCount; i++) {
View child = algorithmState.visibleChildren.get(i);
if (!(child instanceof ExpandableNotificationRow)) {
@@ -714,6 +721,19 @@
}
ExpandableViewState viewState = row.getViewState();
viewState.hidden = false;
+ pulsingRow = row;
+ }
+
+ // Set AmbientState#pulsingRow to the current pulsing row when on AOD.
+ // Set AmbientState#pulsingRow=null when on lockscreen, since AmbientState#pulsingRow
+ // is only used for skipping the unfurl animation for (the notification that was already
+ // showing at full height on AOD) during the AOD=>lockscreen transition, where
+ // dozeAmount=[1f, 0f). We also need to reset the pulsingRow once it is no longer used
+ // because it will interfere with future unfurling animations - for example, during the
+ // LS=>AOD animation, the pulsingRow may stay at full height when it should squish with the
+ // rest of the stack.
+ if (ambientState.getDozeAmount() == 0.0f || ambientState.getDozeAmount() == 1.0f) {
+ ambientState.setPulsingRow(pulsingRow);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 7f20f1e..e12d179 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -716,6 +716,94 @@
.isLessThan(px(R.dimen.heads_up_pinned_elevation))
}
+ @Test
+ fun aodToLockScreen_hasPulsingNotification_pulsingNotificationRowDoesNotChange() {
+ // Given: Before AOD to LockScreen, there was a pulsing notification
+ val pulsingNotificationView = createPulsingViewMock()
+ val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
+ algorithmState.visibleChildren.add(pulsingNotificationView)
+ ambientState.setPulsingRow(pulsingNotificationView)
+
+ // When: during AOD to LockScreen, any dozeAmount between (0, 1.0) is equivalent as a middle
+ // stage; here we use 0.5 for testing.
+ // stackScrollAlgorithm.updatePulsingStates is called
+ ambientState.dozeAmount = 0.5f
+ stackScrollAlgorithm.updatePulsingStates(algorithmState, ambientState)
+
+ // Then: ambientState.pulsingRow should still be pulsingNotificationView
+ assertTrue(ambientState.isPulsingRow(pulsingNotificationView))
+ }
+
+ @Test
+ fun deviceOnAod_hasPulsingNotification_recordPulsingNotificationRow() {
+ // Given: Device is on AOD, there is a pulsing notification
+ // ambientState.pulsingRow is null before stackScrollAlgorithm.updatePulsingStates
+ ambientState.dozeAmount = 1.0f
+ val pulsingNotificationView = createPulsingViewMock()
+ val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
+ algorithmState.visibleChildren.add(pulsingNotificationView)
+ ambientState.setPulsingRow(null)
+
+ // When: stackScrollAlgorithm.updatePulsingStates is called
+ stackScrollAlgorithm.updatePulsingStates(algorithmState, ambientState)
+
+ // Then: ambientState.pulsingRow should record the pulsingNotificationView
+ assertTrue(ambientState.isPulsingRow(pulsingNotificationView))
+ }
+
+ @Test
+ fun deviceOnLockScreen_hasPulsingNotificationBefore_clearPulsingNotificationRowRecord() {
+ // Given: Device finished AOD to LockScreen, there was a pulsing notification, and
+ // ambientState.pulsingRow was not null before AOD to LockScreen
+ // pulsingNotificationView.showingPulsing() returns false since the device is on LockScreen
+ ambientState.dozeAmount = 0.0f
+ val pulsingNotificationView = createPulsingViewMock()
+ whenever(pulsingNotificationView.showingPulsing()).thenReturn(false)
+ val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
+ algorithmState.visibleChildren.add(pulsingNotificationView)
+ ambientState.setPulsingRow(pulsingNotificationView)
+
+ // When: stackScrollAlgorithm.updatePulsingStates is called
+ stackScrollAlgorithm.updatePulsingStates(algorithmState, ambientState)
+
+ // Then: ambientState.pulsingRow should be null
+ assertTrue(ambientState.isPulsingRow(null))
+ }
+
+ @Test
+ fun aodToLockScreen_hasPulsingNotification_pulsingNotificationRowShowAtFullHeight() {
+ // Given: Before AOD to LockScreen, there was a pulsing notification
+ val pulsingNotificationView = createPulsingViewMock()
+ val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
+ algorithmState.visibleChildren.add(pulsingNotificationView)
+ ambientState.setPulsingRow(pulsingNotificationView)
+
+ // When: during AOD to LockScreen, any dozeAmount between (0, 1.0) is equivalent as a middle
+ // stage; here we use 0.5 for testing. The expansionFraction is also 0.5.
+ // stackScrollAlgorithm.resetViewStates is called.
+ ambientState.dozeAmount = 0.5f
+ setExpansionFractionWithoutShelfDuringAodToLockScreen(
+ ambientState,
+ algorithmState,
+ fraction = 0.5f
+ )
+ stackScrollAlgorithm.resetViewStates(ambientState, 0)
+
+ // Then: pulsingNotificationView should show at full height
+ assertEquals(
+ stackScrollAlgorithm.getMaxAllowedChildHeight(pulsingNotificationView),
+ pulsingNotificationView.viewState.height
+ )
+
+ // After: reset dozeAmount and expansionFraction
+ ambientState.dozeAmount = 0f
+ setExpansionFractionWithoutShelfDuringAodToLockScreen(
+ ambientState,
+ algorithmState,
+ fraction = 1f
+ )
+ }
+
private fun createHunViewMock(
isShadeOpen: Boolean,
fullyVisible: Boolean,
@@ -744,6 +832,29 @@
headsUpIsVisible = fullyVisible
}
+ private fun createPulsingViewMock(
+ ) =
+ mock<ExpandableNotificationRow>().apply {
+ whenever(this.viewState).thenReturn(ExpandableViewState())
+ whenever(this.showingPulsing()).thenReturn(true)
+ }
+
+ private fun setExpansionFractionWithoutShelfDuringAodToLockScreen(
+ ambientState: AmbientState,
+ algorithmState: StackScrollAlgorithm.StackScrollAlgorithmState,
+ fraction: Float
+ ) {
+ // showingShelf: false
+ algorithmState.firstViewInShelf = null
+ // scrimPadding: 0, because device is on lock screen
+ ambientState.setStatusBarState(StatusBarState.KEYGUARD)
+ ambientState.dozeAmount = 0.0f
+ // set stackEndHeight and stackHeight
+ // ExpansionFractionWithoutShelf == stackHeight / stackEndHeight
+ ambientState.stackEndHeight = 100f
+ ambientState.stackHeight = ambientState.stackEndHeight * fraction
+ }
+
private fun resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction: Float,
expectedAlpha: Float,