summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimatorTest.kt87
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt28
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt102
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimator.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java19
9 files changed, 410 insertions, 27 deletions
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index f2c76ba25079..7e8d5496a390 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -203,6 +203,16 @@ flag {
}
flag {
+ name: "notifications_hun_shared_animation_values"
+ namespace: "systemui"
+ description: "Adds a shared class for fetching HUN animation values."
+ bug: "393369891"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "notification_undo_guts_on_config_changed"
namespace: "systemui"
description: "Fixes a bug where a theme or font change while notification guts were open"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimatorTest.kt
new file mode 100644
index 000000000000..206eb89db94f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimatorTest.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.headsup
+
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.Before
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+class HeadsUpAnimatorTest : SysuiTestCase() {
+ @Before
+ fun setUp() {
+ context.getOrCreateTestableResources().apply {
+ this.addOverride(R.dimen.heads_up_appear_y_above_screen, TEST_Y_ABOVE_SCREEN)
+ }
+ }
+
+ @Test
+ fun getHeadsUpYTranslation_fromBottomTrue_usesBottomAndYAbove() {
+ val underTest = HeadsUpAnimator(context)
+ underTest.stackTopMargin = 30
+ underTest.headsUpAppearHeightBottom = 300
+
+ val yTranslation = underTest.getHeadsUpYTranslation(isHeadsUpFromBottom = true)
+
+ assertThat(yTranslation).isEqualTo(TEST_Y_ABOVE_SCREEN + 300)
+ }
+
+ @Test
+ fun getHeadsUpYTranslation_fromBottomFalse_usesTopMarginAndYAbove() {
+ val underTest = HeadsUpAnimator(context)
+ underTest.stackTopMargin = 30
+ underTest.headsUpAppearHeightBottom = 300
+
+ val yTranslation = underTest.getHeadsUpYTranslation(isHeadsUpFromBottom = false)
+
+ assertThat(yTranslation).isEqualTo(-30 - TEST_Y_ABOVE_SCREEN)
+ }
+
+ @Test
+ fun getHeadsUpYTranslation_resourcesUpdated() {
+ val underTest = HeadsUpAnimator(context)
+ underTest.stackTopMargin = 30
+ underTest.headsUpAppearHeightBottom = 300
+
+ val yTranslation = underTest.getHeadsUpYTranslation(isHeadsUpFromBottom = true)
+
+ assertThat(yTranslation).isEqualTo(TEST_Y_ABOVE_SCREEN + 300)
+
+ // WHEN the resource is updated
+ val newYAbove = 600
+ context.getOrCreateTestableResources().apply {
+ this.addOverride(R.dimen.heads_up_appear_y_above_screen, newYAbove)
+ }
+ underTest.updateResources(context)
+
+ // THEN HeadsUpAnimator knows about it
+ assertThat(underTest.getHeadsUpYTranslation(isHeadsUpFromBottom = true))
+ .isEqualTo(newYAbove + 300)
+ }
+
+ companion object {
+ private const val TEST_Y_ABOVE_SCREEN = 50
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index ce655ef44f07..954515015fd9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -27,6 +27,8 @@ import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShade
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView.FooterViewState
import com.android.systemui.statusbar.notification.headsup.AvalancheController
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -55,7 +57,8 @@ class StackScrollAlgorithmTest(flags: FlagsParameterization) : SysuiTestCase() {
private val avalancheController = mock<AvalancheController>()
private val hostView = FrameLayout(context)
- private val stackScrollAlgorithm = StackScrollAlgorithm(context, hostView)
+ private lateinit var headsUpAnimator: HeadsUpAnimator
+ private lateinit var stackScrollAlgorithm: StackScrollAlgorithm
private val notificationRow = mock<ExpandableNotificationRow>()
private val notificationEntry = mock<NotificationEntry>()
private val notificationEntryAdapter = mock<EntryAdapter>()
@@ -101,7 +104,10 @@ class StackScrollAlgorithmTest(flags: FlagsParameterization) : SysuiTestCase() {
@JvmStatic
@Parameters(name = "{0}")
fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ return FlagsParameterization.allCombinationsOf(
+ NotificationsHunSharedAnimationValues.FLAG_NAME
+ )
+ .andSceneContainer()
}
}
@@ -123,6 +129,15 @@ class StackScrollAlgorithmTest(flags: FlagsParameterization) : SysuiTestCase() {
ambientState.isSmallScreen = true
hostView.addView(notificationRow)
+
+ if (NotificationsHunSharedAnimationValues.isEnabled) {
+ headsUpAnimator = HeadsUpAnimator(context)
+ }
+ stackScrollAlgorithm = StackScrollAlgorithm(
+ context,
+ hostView,
+ if (::headsUpAnimator.isInitialized) headsUpAnimator else null,
+ )
}
private fun isTv(): Boolean {
@@ -403,7 +418,11 @@ class StackScrollAlgorithmTest(flags: FlagsParameterization) : SysuiTestCase() {
ambientState.setLayoutMinHeight(2500) // Mock the height of shade
ambientState.stackY = 2500f // Scroll over the max translation
stackScrollAlgorithm.setIsExpanded(true) // Mark the shade open
- stackScrollAlgorithm.setHeadsUpAppearHeightBottom(bottomOfScreen.toInt())
+ if (NotificationsHunSharedAnimationValues.isEnabled) {
+ headsUpAnimator.headsUpAppearHeightBottom = bottomOfScreen.toInt()
+ } else {
+ stackScrollAlgorithm.setHeadsUpAppearHeightBottom(bottomOfScreen.toInt())
+ }
whenever(notificationRow.mustStayOnScreen()).thenReturn(true)
whenever(notificationRow.isHeadsUp).thenReturn(true)
whenever(notificationRow.isAboveShelf).thenReturn(true)
@@ -419,6 +438,9 @@ class StackScrollAlgorithmTest(flags: FlagsParameterization) : SysuiTestCase() {
val topMargin = 100f
ambientState.maxHeadsUpTranslation = 2000f
ambientState.stackTopMargin = topMargin.toInt()
+ if (NotificationsHunSharedAnimationValues.isEnabled) {
+ headsUpAnimator.stackTopMargin = topMargin.toInt()
+ }
whenever(notificationRow.intrinsicHeight).thenReturn(100)
whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
index e7be20e7c3cb..4a761917b693 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
@@ -16,12 +16,16 @@
package com.android.systemui.statusbar.notification.stack
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent
import com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR
@@ -49,10 +53,10 @@ private const val HEADS_UP_ABOVE_SCREEN = 80
@RunWith(AndroidJUnit4::class)
@RunWithLooper
class StackStateAnimatorTest : SysuiTestCase() {
-
@get:Rule val animatorTestRule = AnimatorTestRule(this)
private lateinit var stackStateAnimator: StackStateAnimator
+ private lateinit var headsUpAnimator: HeadsUpAnimator
private val stackScroller: NotificationStackScrollLayout = mock()
private val view: ExpandableView = mock()
private val viewState: ExpandableViewState =
@@ -69,11 +73,20 @@ class StackStateAnimatorTest : SysuiTestCase() {
whenever(stackScroller.context).thenReturn(context)
whenever(view.viewState).thenReturn(viewState)
- stackStateAnimator = StackStateAnimator(mContext, stackScroller)
+
+ if (NotificationsHunSharedAnimationValues.isEnabled) {
+ headsUpAnimator = HeadsUpAnimator(context)
+ }
+ stackStateAnimator = StackStateAnimator(
+ mContext,
+ stackScroller,
+ if (::headsUpAnimator.isInitialized) headsUpAnimator else null,
+ )
}
@Test
- fun startAnimationForEvents_headsUpFromTop_startsHeadsUpAppearAnim() {
+ @DisableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+ fun startAnimationForEvents_headsUpFromTop_startsHeadsUpAppearAnim_flagOff() {
val topMargin = 50f
val expectedStartY = -topMargin - stackStateAnimator.mHeadsUpAppearStartAboveScreen
val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR)
@@ -94,7 +107,30 @@ class StackStateAnimatorTest : SysuiTestCase() {
}
@Test
- fun startAnimationForEvents_headsUpFromBottom_startsHeadsUpAppearAnim() {
+ @EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+ fun startAnimationForEvents_headsUpFromTop_startsHeadsUpAppearAnim_flagOn() {
+ val topMargin = 50f
+ val expectedStartY = -topMargin - HEADS_UP_ABOVE_SCREEN
+ val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR)
+ headsUpAnimator.stackTopMargin = topMargin.toInt()
+
+ stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
+
+ verify(view).setFinalActualHeight(VIEW_HEIGHT)
+ verify(view, description("should animate from the top")).translationY = expectedStartY
+ verify(view)
+ .performAddAnimation(
+ /* delay= */ 0L,
+ /* duration= */ ANIMATION_DURATION_HEADS_UP_APPEAR.toLong(),
+ /* isHeadsUpAppear= */ true,
+ /* isHeadsUpCycling= */ false,
+ /* onEndRunnable= */ null,
+ )
+ }
+
+ @Test
+ @DisableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+ fun startAnimationForEvents_headsUpFromBottom_startsHeadsUpAppearAnim_flagOff() {
val screenHeight = 2000f
val expectedStartY = screenHeight + stackStateAnimator.mHeadsUpAppearStartAboveScreen
val event =
@@ -118,7 +154,63 @@ class StackStateAnimatorTest : SysuiTestCase() {
}
@Test
- fun startAnimationForEvents_startsHeadsUpDisappearAnim() {
+ @EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+ fun startAnimationForEvents_headsUpFromBottom_startsHeadsUpAppearAnim_flagOn() {
+ val screenHeight = 2000f
+ val expectedStartY = screenHeight + HEADS_UP_ABOVE_SCREEN
+ val event =
+ AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR).apply {
+ headsUpFromBottom = true
+ }
+ headsUpAnimator.headsUpAppearHeightBottom = screenHeight.toInt()
+
+ stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
+
+ verify(view).setFinalActualHeight(VIEW_HEIGHT)
+ verify(view, description("should animate from the bottom")).translationY = expectedStartY
+ verify(view)
+ .performAddAnimation(
+ /* delay= */ 0L,
+ /* duration= */ ANIMATION_DURATION_HEADS_UP_APPEAR.toLong(),
+ /* isHeadsUpAppear= */ true,
+ /* isHeadsUpCycling= */ false,
+ /* onEndRunnable= */ null,
+ )
+ }
+
+ @Test
+ @DisableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+ fun startAnimationForEvents_startsHeadsUpDisappearAnim_flagOff() {
+ val disappearDuration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR.toLong()
+ val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR)
+ clearInvocations(view)
+ stackStateAnimator.startAnimationForEvents(arrayListOf(event), 0)
+
+ verify(view)
+ .performRemoveAnimation(
+ /* duration= */ eq(disappearDuration),
+ /* delay= */ eq(0L),
+ /* translationDirection= */ eq(0f),
+ /* isHeadsUpAnimation= */ eq(true),
+ /* isHeadsUpCycling= */ eq(false),
+ /* onStartedRunnable= */ any(),
+ /* onFinishedRunnable= */ runnableCaptor.capture(),
+ /* animationListener= */ any(),
+ /* clipSide= */ eq(ExpandableView.ClipSide.BOTTOM),
+ )
+
+ animatorTestRule.advanceTimeBy(disappearDuration) // move to the end of SSA animations
+ runnableCaptor.value.run() // execute the end runnable
+
+ verify(view, description("should be translated to the heads up appear start"))
+ .translationY = -stackStateAnimator.mHeadsUpAppearStartAboveScreen
+ verify(view, description("should be called at the end of the disappear animation"))
+ .removeFromTransientContainer()
+ }
+
+ @Test
+ @EnableFlags(NotificationsHunSharedAnimationValues.FLAG_NAME)
+ fun startAnimationForEvents_startsHeadsUpDisappearAnim_flagOn() {
val disappearDuration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR.toLong()
val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR)
clearInvocations(view)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimator.kt
new file mode 100644
index 000000000000..f9bd805a2628
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/HeadsUpAnimator.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.headsup
+
+import android.content.Context
+import com.android.systemui.res.R
+
+/**
+ * A class shared between [StackScrollAlgorithm] and [StackStateAnimator] to ensure all heads up
+ * animations use the same animation values.
+ */
+class HeadsUpAnimator(context: Context) {
+ init {
+ NotificationsHunSharedAnimationValues.assertInNewMode()
+ }
+
+ var headsUpAppearHeightBottom: Int = 0
+ var stackTopMargin: Int = 0
+
+ private var headsUpAppearStartAboveScreen = context.fetchHeadsUpAppearStartAboveScreen()
+
+ /**
+ * Returns the Y translation for a heads-up notification animation.
+ *
+ * For an appear animation, the returned Y translation should be the starting value of the
+ * animation. For a disappear animation, the returned Y translation should be the ending value
+ * of the animation.
+ */
+ fun getHeadsUpYTranslation(isHeadsUpFromBottom: Boolean): Int {
+ NotificationsHunSharedAnimationValues.assertInNewMode()
+
+ if (isHeadsUpFromBottom) {
+ // start from or end at the bottom of the screen
+ return headsUpAppearHeightBottom + headsUpAppearStartAboveScreen
+ }
+
+ // start from or end at the top of the screen
+ return -stackTopMargin - headsUpAppearStartAboveScreen
+ }
+
+ /** Should be invoked when resource values may have changed. */
+ fun updateResources(context: Context) {
+ headsUpAppearStartAboveScreen = context.fetchHeadsUpAppearStartAboveScreen()
+ }
+
+ private fun Context.fetchHeadsUpAppearStartAboveScreen(): Int {
+ return this.resources.getDimensionPixelSize(R.dimen.heads_up_appear_y_above_screen)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt
new file mode 100644
index 000000000000..ca9d498c0e7f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/headsup/NotificationsHunSharedAnimationValues.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.headsup
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the notifications hun shared animation values flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object NotificationsHunSharedAnimationValues {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_HUN_SHARED_ANIMATION_VALUES
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.notificationsHunSharedAnimationValues()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This will throw an exception if
+ * the flag is not enabled to ensure that the refactor author catches issues in testing.
+ * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
+ */
+ @JvmStatic
+ inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
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 d9ff01b46488..4390f1b16ffd 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
@@ -117,8 +117,10 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember
import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix;
import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator;
import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil;
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -233,6 +235,8 @@ public class NotificationStackScrollLayout
private String mLastInitViewDumpString;
private long mLastInitViewElapsedRealtime;
+ @Nullable
+ private final HeadsUpAnimator mHeadsUpAnimator;
/**
* The algorithm which calculates the properties for our children
*/
@@ -668,8 +672,13 @@ public class NotificationStackScrollLayout
mExpandHelper.setEventSource(this);
mExpandHelper.setScrollAdapter(mScrollAdapter);
- mStackScrollAlgorithm = createStackScrollAlgorithm(context);
- mStateAnimator = new StackStateAnimator(context, this);
+ if (NotificationsHunSharedAnimationValues.isEnabled()) {
+ mHeadsUpAnimator = new HeadsUpAnimator(context);
+ } else {
+ mHeadsUpAnimator = null;
+ }
+ mStackScrollAlgorithm = new StackScrollAlgorithm(context, this, mHeadsUpAnimator);
+ mStateAnimator = new StackStateAnimator(context, this, mHeadsUpAnimator);
setOutlineProvider(mOutlineProvider);
// We could set this whenever we 'requestChildUpdate' much like the viewTreeObserver, but
@@ -3588,10 +3597,6 @@ public class NotificationStackScrollLayout
mGoToFullShadeNeedsAnimation = false;
}
- protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
- return new StackScrollAlgorithm(context, this);
- }
-
/**
* @return Whether a y coordinate is inside the content.
*/
@@ -5117,9 +5122,16 @@ public class NotificationStackScrollLayout
public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
SceneContainerFlag.assertInLegacyMode();
mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
- mStackScrollAlgorithm.setHeadsUpAppearHeightBottom(height);
- mStateAnimator.setHeadsUpAppearHeightBottom(height);
- mStateAnimator.setStackTopMargin(mAmbientState.getStackTopMargin());
+
+ if (NotificationsHunSharedAnimationValues.isEnabled()) {
+ mHeadsUpAnimator.setHeadsUpAppearHeightBottom(height);
+ mHeadsUpAnimator.setStackTopMargin(mAmbientState.getStackTopMargin());
+ } else {
+ mStackScrollAlgorithm.setHeadsUpAppearHeightBottom(height);
+ mStateAnimator.setHeadsUpAppearHeightBottom(height);
+ mStateAnimator.setStackTopMargin(mAmbientState.getStackTopMargin());
+ }
+
requestChildrenUpdate();
}
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 948497c822f0..4effb76c6570 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
@@ -36,6 +36,8 @@ import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator;
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -57,6 +59,9 @@ public class StackScrollAlgorithm {
private static final String TAG = "StackScrollAlgorithm";
private static final SourceType STACK_SCROLL_ALGO = SourceType.from("StackScrollAlgorithm");
private final ViewGroup mHostView;
+ @Nullable
+ private final HeadsUpAnimator mHeadsUpAnimator;
+
private float mPaddingBetweenElements;
private float mGapHeight;
private float mGapHeightOnLockscreen;
@@ -79,8 +84,12 @@ public class StackScrollAlgorithm {
private int mHeadsUpAppearHeightBottom;
private int mHeadsUpCyclingPadding;
- public StackScrollAlgorithm(Context context, ViewGroup hostView) {
+ public StackScrollAlgorithm(
+ Context context,
+ ViewGroup hostView,
+ @Nullable HeadsUpAnimator headsUpAnimator) {
mHostView = hostView;
+ mHeadsUpAnimator = headsUpAnimator;
initView(context);
}
@@ -112,6 +121,9 @@ public class StackScrollAlgorithm {
mQuickQsOffsetHeight = SystemBarUtils.getQuickQsOffsetHeight(context);
mSmallCornerRadius = res.getDimension(R.dimen.notification_corner_radius_small);
mLargeCornerRadius = res.getDimension(R.dimen.notification_corner_radius);
+ if (NotificationsHunSharedAnimationValues.isEnabled()) {
+ mHeadsUpAnimator.updateResources(context);
+ }
}
/**
@@ -251,6 +263,7 @@ public class StackScrollAlgorithm {
}
public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
+ NotificationsHunSharedAnimationValues.assertInLegacyMode();
mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
}
@@ -1042,14 +1055,22 @@ public class StackScrollAlgorithm {
childState.setYTranslation(inSpaceTranslation + extraTranslation);
cyclingInHunHeight = -1;
} else if (!ambientState.isDozing()) {
- if (shouldHunAppearFromBottom(ambientState, childState)) {
- // move to the bottom of the screen
- childState.setYTranslation(
- mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen);
+ boolean shouldHunAppearFromBottom =
+ shouldHunAppearFromBottom(ambientState, childState);
+ if (NotificationsHunSharedAnimationValues.isEnabled()) {
+ int yTranslation =
+ mHeadsUpAnimator.getHeadsUpYTranslation(shouldHunAppearFromBottom);
+ childState.setYTranslation(yTranslation);
} else {
- // move to the top of the screen
- childState.setYTranslation(-ambientState.getStackTopMargin()
- - mHeadsUpAppearStartAboveScreen);
+ if (shouldHunAppearFromBottom) {
+ // move to the bottom of the screen
+ childState.setYTranslation(
+ mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen);
+ } else {
+ // move to the top of the screen
+ childState.setYTranslation(-ambientState.getStackTopMargin()
+ - mHeadsUpAppearStartAboveScreen);
+ }
}
} else {
// Make sure row yTranslation is at maximum the HUN yTranslation,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 4da418efa241..9a5cf9ceed53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -27,6 +27,7 @@ import static com.android.systemui.statusbar.notification.stack.NotificationStac
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.Nullable;
import android.content.Context;
import android.util.Property;
import android.view.View;
@@ -39,6 +40,8 @@ import com.android.systemui.res.R;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpAnimator;
+import com.android.systemui.statusbar.notification.headsup.NotificationsHunSharedAnimationValues;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
@@ -83,6 +86,9 @@ public class StackStateAnimator {
private final ExpandableViewState mTmpState = new ExpandableViewState();
private final AnimationProperties mAnimationProperties;
public NotificationStackScrollLayout mHostLayout;
+ @Nullable
+ private final HeadsUpAnimator mHeadsUpAnimator;
+
private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
new ArrayList<>();
private ArrayList<View> mNewAddChildren = new ArrayList<>();
@@ -104,8 +110,12 @@ public class StackStateAnimator {
private NotificationShelf mShelf;
private StackStateLogger mLogger;
- public StackStateAnimator(Context context, NotificationStackScrollLayout hostLayout) {
+ public StackStateAnimator(
+ Context context,
+ NotificationStackScrollLayout hostLayout,
+ @Nullable HeadsUpAnimator headsUpAnimator) {
mHostLayout = hostLayout;
+ mHeadsUpAnimator = headsUpAnimator;
initView(context);
mAnimationProperties = new AnimationProperties() {
@@ -543,7 +553,6 @@ public class StackStateAnimator {
mHeadsUpAppearChildren.add(changingView);
mTmpState.copyFrom(changingView.getViewState());
- // translate the HUN in from the top, or the bottom of the screen
mTmpState.setYTranslation(getHeadsUpYTranslationStart(event.headsUpFromBottom));
// set the height and the initial position
mTmpState.applyToView(changingView);
@@ -728,6 +737,10 @@ public class StackStateAnimator {
}
private float getHeadsUpYTranslationStart(boolean headsUpFromBottom) {
+ if (NotificationsHunSharedAnimationValues.isEnabled()) {
+ return mHeadsUpAnimator.getHeadsUpYTranslation(headsUpFromBottom);
+ }
+
if (headsUpFromBottom) {
// start from the bottom of the screen
return mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen;
@@ -814,10 +827,12 @@ public class StackStateAnimator {
}
public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
+ NotificationsHunSharedAnimationValues.assertInLegacyMode();
mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
}
public void setStackTopMargin(int stackTopMargin) {
+ NotificationsHunSharedAnimationValues.assertInLegacyMode();
mStackTopMargin = stackTopMargin;
}