diff options
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; } |