summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt250
5 files changed, 285 insertions, 17 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 9a82ecf01449..855798ca1e96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -40,6 +40,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -93,6 +94,7 @@ public class NotificationShelf extends ActivatableNotificationView {
private float mCornerAnimationDistance;
private float mActualWidth = -1;
private int mMaxIconsOnLockscreen;
+ private int mNotificationScrimPadding;
private boolean mCanModifyColorOfNotifications;
private boolean mCanInteract;
private NotificationStackScrollLayout mHostLayout;
@@ -136,6 +138,7 @@ public class NotificationShelf extends ActivatableNotificationView {
mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
mPaddingBetweenElements = res.getDimensionPixelSize(R.dimen.notification_divider_height);
mMaxIconsOnLockscreen = res.getInteger(R.integer.max_notif_icons_on_lockscreen);
+ mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
ViewGroup.LayoutParams layoutParams = getLayoutParams();
final int newShelfHeight = res.getDimensionPixelOffset(R.dimen.notification_shelf_height);
@@ -261,15 +264,31 @@ public class NotificationShelf extends ActivatableNotificationView {
viewState.hasItemsInStableShelf = false;
}
- final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
+ final float stackBottom = SceneContainerFlag.isEnabled()
+ ? getStackBottom(ambientState)
+ : ambientState.getStackY() + ambientState.getStackHeight();
+
if (viewState.hidden) {
// if the shelf is hidden, position it at the end of the stack (plus the clip
// padding), such that when it appears animated, it will smoothly move in from the
// bottom, without jump cutting any notifications
- viewState.setYTranslation(stackEnd + mPaddingBetweenElements);
+ viewState.setYTranslation(stackBottom + mPaddingBetweenElements);
} else {
- viewState.setYTranslation(stackEnd - viewState.height);
+ viewState.setYTranslation(stackBottom - viewState.height);
+ }
+ }
+
+ /**
+ * bottom-most position, where we can draw the stack
+ */
+ private float getStackBottom(AmbientState ambientState) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f;
+ float stackBottom = ambientState.getStackCutoff() - mNotificationScrimPadding;
+ if (ambientState.isExpansionChanging()) {
+ stackBottom = MathUtils.lerp(stackBottom * StackScrollAlgorithm.START_FRACTION,
+ stackBottom, ambientState.getExpansionFraction());
}
+ return stackBottom;
}
private int getSpeedBumpIndex() {
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 5f4e832f31a3..456c321a0c14 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
@@ -29,6 +29,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
@@ -63,6 +64,7 @@ public class AmbientState implements Dumpable {
* Used to read bouncer states.
*/
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private float mStackCutoff;
private int mScrollY;
private float mOverScrollTopAmount;
private float mOverScrollBottomAmount;
@@ -346,6 +348,21 @@ public class AmbientState implements Dumpable {
return mZDistanceBetweenElements;
}
+ /**
+ * Y coordinate in view pixels above which the bottom of the notification stack / shelf / footer
+ * must be.
+ */
+ public float getStackCutoff() {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f;
+ return mStackCutoff;
+ }
+
+ /** @see #getStackCutoff() */
+ public void setStackCutoff(float stackCutoff) {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+ this.mStackCutoff = stackCutoff;
+ }
+
public int getScrollY() {
return mScrollY;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 60a26dcb483d..0e77ed44293a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -837,7 +837,7 @@ public class NotificationStackScrollLayout
y = (int) mScrollViewFields.getStackTop();
drawDebugInfo(canvas, y, Color.RED, /* label= */ "getStackTop() = " + y);
- y = (int) mScrollViewFields.getStackCutoff();
+ y = (int) mAmbientState.getStackCutoff();
drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "getStackCutoff() = " + y);
y = (int) mScrollViewFields.getHeadsUpTop();
@@ -1221,7 +1221,7 @@ public class NotificationStackScrollLayout
@Override
public void setStackCutoff(float stackCutoff) {
- mScrollViewFields.setStackCutoff(stackCutoff);
+ mAmbientState.setStackCutoff(stackCutoff);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
index bffc1704cdb4..2e86ad97a5f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
@@ -34,11 +34,6 @@ class ScrollViewFields {
var scrimClippingShape: ShadeScrimShape? = null
/** Y coordinate in view pixels of the top of the notification stack */
var stackTop: Float = 0f
- /**
- * Y coordinate in view pixels above which the bottom of the notification stack / shelf / footer
- * must be.
- */
- var stackCutoff: Float = 0f
/** Y coordinate in view pixels of the top of the HUN */
var headsUpTop: Float = 0f
/** Whether the notifications are scrolled all the way to the top (i.e. when freshly opened) */
@@ -82,7 +77,6 @@ class ScrollViewFields {
pw.printSection("StackViewStates") {
pw.println("scrimClippingShape", scrimClippingShape)
pw.println("stackTop", stackTop)
- pw.println("stackCutoff", stackCutoff)
pw.println("headsUpTop", headsUpTop)
pw.println("isScrolledToTop", isScrolledToTop)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 48e8f88a15c0..9b0fd96e22e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -10,6 +10,8 @@ import androidx.test.filters.SmallTest
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.res.R
@@ -30,8 +32,8 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
/** Tests for {@link NotificationShelf}. */
@SmallTest
@@ -332,6 +334,144 @@ open class NotificationShelfTest : SysuiTestCase() {
}
@Test
+ fun updateState_lastViewAlmostBelowShelf_completelyInShelf() {
+ val viewStart = 0f
+ val shelfClipStart = 0.001f
+
+ val expandableView = mock(ExpandableView::class.java)
+ whenever(expandableView.shelfIcon).thenReturn(mock(StatusBarIconView::class.java))
+ whenever(expandableView.translationY).thenReturn(viewStart)
+ whenever(expandableView.actualHeight).thenReturn(20)
+
+ whenever(expandableView.minHeight).thenReturn(20)
+ whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(expandableView.isInShelf).thenReturn(true)
+
+ whenever(ambientState.isOnKeyguard).thenReturn(true)
+ whenever(ambientState.isExpansionChanging).thenReturn(false)
+ whenever(ambientState.isShadeExpanded).thenReturn(true)
+
+ val amountInShelf =
+ shelf.getAmountInShelf(
+ /* i= */ 0,
+ /* view= */ expandableView,
+ /* scrollingFast= */ false,
+ /* expandingAnimated= */ false,
+ /* isLastChild= */ true,
+ shelfClipStart
+ )
+ assertEquals(1f, amountInShelf)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun updateState_withViewInShelf_showShelf() {
+ // GIVEN a view is scrolled into the shelf
+ val stackCutoff = 200f
+ val scrimPadding =
+ context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings)
+ val shelfTop = stackCutoff - scrimPadding - shelf.height
+ val stackScrollAlgorithmState = StackScrollAlgorithmState()
+ val viewInShelf = mock(ExpandableView::class.java)
+
+ whenever(ambientState.stackCutoff).thenReturn(stackCutoff)
+ whenever(ambientState.isShadeExpanded).thenReturn(true)
+ whenever(ambientState.lastVisibleBackgroundChild).thenReturn(viewInShelf)
+ whenever(viewInShelf.viewState).thenReturn(ExpandableViewState())
+ whenever(viewInShelf.shelfIcon).thenReturn(mock(StatusBarIconView::class.java))
+ whenever(viewInShelf.translationY).thenReturn(shelfTop)
+ whenever(viewInShelf.actualHeight).thenReturn(10)
+ whenever(viewInShelf.isInShelf).thenReturn(true)
+ whenever(viewInShelf.minHeight).thenReturn(10)
+ whenever(viewInShelf.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(viewInShelf.isInShelf).thenReturn(true)
+
+ stackScrollAlgorithmState.visibleChildren.add(viewInShelf)
+ stackScrollAlgorithmState.firstViewInShelf = viewInShelf
+
+ // WHEN Shelf's ViewState is updated
+ shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+ // THEN the shelf is visible, and positioned correctly
+ val shelfState = shelf.viewState as NotificationShelf.ShelfState
+ assertEquals(false, shelfState.hidden)
+ assertEquals(shelf.height, shelfState.height)
+ assertEquals(shelfTop, shelfState.yTranslation)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun updateState_withViewInShelfDuringExpansion_showShelf() {
+ // GIVEN a view is scrolled into the shelf
+ val stackCutoff = 200f
+ val scrimPadding =
+ context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings)
+ val stackBottom = stackCutoff - scrimPadding
+ val shelfTop = stackBottom - shelf.height
+ val stackScrollAlgorithmState = StackScrollAlgorithmState()
+ val viewInShelf = mock(ExpandableView::class.java)
+
+ // AND a shade expansion is in progress
+ val shadeExpansionFraction = 0.5f
+
+ whenever(ambientState.stackCutoff).thenReturn(stackCutoff)
+ whenever(ambientState.isShadeExpanded).thenReturn(true)
+ whenever(ambientState.lastVisibleBackgroundChild).thenReturn(viewInShelf)
+ whenever(ambientState.isExpansionChanging).thenReturn(true)
+ whenever(ambientState.expansionFraction).thenReturn(shadeExpansionFraction)
+ whenever(viewInShelf.viewState).thenReturn(ExpandableViewState())
+ whenever(viewInShelf.shelfIcon).thenReturn(mock(StatusBarIconView::class.java))
+ whenever(viewInShelf.translationY).thenReturn(shelfTop)
+ whenever(viewInShelf.actualHeight).thenReturn(10)
+ whenever(viewInShelf.isInShelf).thenReturn(true)
+ whenever(viewInShelf.minHeight).thenReturn(10)
+ whenever(viewInShelf.shelfTransformationTarget).thenReturn(null) // use translationY
+ whenever(viewInShelf.isInShelf).thenReturn(true)
+
+ stackScrollAlgorithmState.visibleChildren.add(viewInShelf)
+ stackScrollAlgorithmState.firstViewInShelf = viewInShelf
+
+ // WHEN Shelf's ViewState is updated
+ shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+ // THEN the shelf is visible
+ val shelfState = shelf.viewState as NotificationShelf.ShelfState
+ assertEquals(false, shelfState.hidden)
+ assertEquals(shelf.height, shelfState.height)
+ // AND its translation is scaled by the shade expansion
+ assertEquals((stackBottom * 0.75f) - shelf.height, shelfState.yTranslation)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun updateState_withNullLastVisibleBackgroundChild_hideShelf_withSceneContainer() {
+ // GIVEN
+ val stackCutoff = 200f
+ val scrimPadding =
+ context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings)
+ val paddingBetweenElements =
+ context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
+ whenever(ambientState.stackCutoff).thenReturn(stackCutoff)
+ whenever(ambientState.isShadeExpanded).thenReturn(true)
+ val lastVisibleBackgroundChild = mock<ExpandableView>()
+ val expandableViewState = ExpandableViewState()
+ whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState)
+ val stackScrollAlgorithmState = StackScrollAlgorithmState()
+ stackScrollAlgorithmState.firstViewInShelf = mock()
+
+ whenever(ambientState.lastVisibleBackgroundChild).thenReturn(null)
+
+ // WHEN
+ shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+ // THEN
+ val shelfState = shelf.viewState as NotificationShelf.ShelfState
+ assertEquals(true, shelfState.hidden)
+ assertEquals(stackCutoff - scrimPadding + paddingBetweenElements, shelfState.yTranslation)
+ }
+
+ @Test
+ @DisableSceneContainer
fun updateState_withNullLastVisibleBackgroundChild_hideShelf() {
// GIVEN
whenever(ambientState.stackY).thenReturn(100f)
@@ -358,6 +498,35 @@ open class NotificationShelfTest : SysuiTestCase() {
}
@Test
+ @EnableSceneContainer
+ fun updateState_withNullFirstViewInShelf_hideShelf_withSceneContainer() {
+ // GIVEN
+ val stackCutoff = 200f
+ val scrimPadding =
+ context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings)
+ val paddingBetweenElements =
+ context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
+ whenever(ambientState.stackCutoff).thenReturn(stackCutoff)
+ whenever(ambientState.isShadeExpanded).thenReturn(true)
+ val lastVisibleBackgroundChild = mock<ExpandableView>()
+ val expandableViewState = ExpandableViewState()
+ whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState)
+ whenever(ambientState.lastVisibleBackgroundChild).thenReturn(lastVisibleBackgroundChild)
+ val stackScrollAlgorithmState = StackScrollAlgorithmState()
+
+ stackScrollAlgorithmState.firstViewInShelf = null
+
+ // WHEN
+ shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+ // THEN
+ val shelfState = shelf.viewState as NotificationShelf.ShelfState
+ assertEquals(true, shelfState.hidden)
+ assertEquals(stackCutoff - scrimPadding + paddingBetweenElements, shelfState.yTranslation)
+ }
+
+ @Test
+ @DisableSceneContainer
fun updateState_withNullFirstViewInShelf_hideShelf() {
// GIVEN
whenever(ambientState.stackY).thenReturn(100f)
@@ -384,6 +553,35 @@ open class NotificationShelfTest : SysuiTestCase() {
}
@Test
+ @EnableSceneContainer
+ fun updateState_withCollapsedShade_hideShelf_withSceneContainer() {
+ // GIVEN
+ val stackCutoff = 200f
+ val scrimPadding =
+ context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings)
+ val paddingBetweenElements =
+ context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
+ whenever(ambientState.stackCutoff).thenReturn(stackCutoff)
+ val lastVisibleBackgroundChild = mock<ExpandableView>()
+ val expandableViewState = ExpandableViewState()
+ whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState)
+ whenever(ambientState.lastVisibleBackgroundChild).thenReturn(lastVisibleBackgroundChild)
+ val stackScrollAlgorithmState = StackScrollAlgorithmState()
+ stackScrollAlgorithmState.firstViewInShelf = mock()
+
+ whenever(ambientState.isShadeExpanded).thenReturn(false)
+
+ // WHEN
+ shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+ // THEN
+ val shelfState = shelf.viewState as NotificationShelf.ShelfState
+ assertEquals(true, shelfState.hidden)
+ assertEquals(stackCutoff - scrimPadding + paddingBetweenElements, shelfState.yTranslation)
+ }
+
+ @Test
+ @DisableSceneContainer
fun updateState_withCollapsedShade_hideShelf() {
// GIVEN
whenever(ambientState.stackY).thenReturn(100f)
@@ -410,6 +608,49 @@ open class NotificationShelfTest : SysuiTestCase() {
}
@Test
+ @EnableSceneContainer
+ fun updateState_withHiddenSectionBeforeShelf_hideShelf_withSceneContianer() {
+ // GIVEN
+ val stackCutoff = 200f
+ whenever(ambientState.stackCutoff).thenReturn(stackCutoff)
+ val scrimPadding =
+ context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings)
+ val paddingBetweenElements =
+ context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
+ whenever(ambientState.isShadeExpanded).thenReturn(true)
+ val lastVisibleBackgroundChild = mock<ExpandableView>()
+ val expandableViewState = ExpandableViewState()
+ whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState)
+ val stackScrollAlgorithmState = StackScrollAlgorithmState()
+ whenever(ambientState.lastVisibleBackgroundChild).thenReturn(lastVisibleBackgroundChild)
+
+ val ssaVisibleChild = mock<ExpandableView>()
+ val ssaVisibleChildState = ExpandableViewState()
+ ssaVisibleChildState.hidden = true
+ whenever(ssaVisibleChild.viewState).thenReturn(ssaVisibleChildState)
+
+ val ssaVisibleChild1 = mock<ExpandableView>()
+ val ssaVisibleChildState1 = ExpandableViewState()
+ ssaVisibleChildState1.hidden = true
+ whenever(ssaVisibleChild1.viewState).thenReturn(ssaVisibleChildState1)
+
+ stackScrollAlgorithmState.visibleChildren.add(ssaVisibleChild)
+ stackScrollAlgorithmState.visibleChildren.add(ssaVisibleChild1)
+ whenever(ambientState.isExpansionChanging).thenReturn(true)
+ whenever(ambientState.expansionFraction).thenReturn(1f)
+ stackScrollAlgorithmState.firstViewInShelf = ssaVisibleChild1
+
+ // WHEN
+ shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+ // THEN
+ val shelfState = shelf.viewState as NotificationShelf.ShelfState
+ assertEquals(true, shelfState.hidden)
+ assertEquals(stackCutoff - scrimPadding + paddingBetweenElements, shelfState.yTranslation)
+ }
+
+ @Test
+ @DisableSceneContainer
fun updateState_withHiddenSectionBeforeShelf_hideShelf() {
// GIVEN
whenever(ambientState.stackY).thenReturn(100f)
@@ -461,12 +702,9 @@ open class NotificationShelfTest : SysuiTestCase() {
expectedAlpha: Float
) {
val sbnMock: StatusBarNotification = mock()
- val mockEntry = mock<NotificationEntry>().apply {
- whenever(this.sbn).thenReturn(sbnMock)
- }
+ val mockEntry = mock<NotificationEntry>().apply { whenever(this.sbn).thenReturn(sbnMock) }
val row = ExpandableNotificationRow(mContext, null, mockEntry)
- whenever(ambientState.lastVisibleBackgroundChild)
- .thenReturn(row)
+ whenever(ambientState.lastVisibleBackgroundChild).thenReturn(row)
whenever(ambientState.isExpansionChanging).thenReturn(true)
whenever(ambientState.expansionFraction).thenReturn(expansionFraction)
whenever(hostLayoutController.speedBumpIndex).thenReturn(0)