From 0fae7128d48db5b4beb00871274add53fd81af40 Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Tue, 24 Jan 2023 10:58:15 -0500 Subject: [PATCH] Clear listeners after animation Also, only call `updateVisibility` when the alpha changes between non-zero and zero Test: atest LargeScreenShadeHeaderControllerTest Bug: 265104777 Change-Id: Ie108ba84608a8c54322df0900b0d11d5a63f87e1 --- .../shade/LargeScreenShadeHeaderController.kt | 27 ++++++++++++++-- .../LargeScreenShadeHeaderControllerTest.kt | 31 ++++++++++++++++++- 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt index 88676371bd6b4..86e78a97910b0 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade +import android.animation.Animator import android.annotation.IdRes import android.app.StatusBarManager import android.content.res.Configuration @@ -45,7 +46,6 @@ import com.android.systemui.qs.HeaderPrivacyIconsController import com.android.systemui.qs.carrier.QSCarrierGroup import com.android.systemui.qs.carrier.QSCarrierGroupController import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.HEADER_TRANSITION_ID -import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QS_HEADER_CONSTRAINT import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider @@ -176,9 +176,13 @@ class LargeScreenShadeHeaderController @Inject constructor( var shadeExpandedFraction = -1f set(value) { if (field != value) { + val oldAlpha = header.alpha header.alpha = ShadeInterpolation.getContentAlpha(value) field = value - updateVisibility() + if ((oldAlpha == 0f && header.alpha > 0f) || + (oldAlpha > 0f && header.alpha == 0f)) { + updateVisibility() + } } } @@ -335,9 +339,28 @@ class LargeScreenShadeHeaderController @Inject constructor( .setUpdateListener { updateVisibility() } + .setListener(endAnimationListener) .start() } + private val endAnimationListener = object : Animator.AnimatorListener { + override fun onAnimationCancel(animation: Animator?) { + clearListeners() + } + + override fun onAnimationEnd(animation: Animator?) { + clearListeners() + } + + override fun onAnimationRepeat(animation: Animator?) {} + + override fun onAnimationStart(animation: Animator?) {} + + private fun clearListeners() { + header.animate().setListener(null).setUpdateListener(null) + } + } + private fun loadConstraints() { if (header is MotionLayout) { // Use resources.getXml instead of passing the resource id due to bug b/205018300 diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt index b568122d3fed0..2bf2a81fe13ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.shade +import android.animation.Animator import android.animation.ValueAnimator import android.app.StatusBarManager import android.content.Context @@ -45,8 +46,8 @@ import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.Mockito.verifyZeroInteractions -import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit +import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) @@ -292,6 +293,34 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() { assertThat(viewVisibility).isEqualTo(View.INVISIBLE) } + @Test + fun animatorListenersClearedAtEnd() { + val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF) + whenever(view.animate()).thenReturn(animator) + + mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, 0L) + val listenerCaptor = argumentCaptor() + verify(animator).setListener(capture(listenerCaptor)) + + listenerCaptor.value.onAnimationEnd(mock()) + verify(animator).setListener(null) + verify(animator).setUpdateListener(null) + } + + @Test + fun animatorListenersClearedOnCancel() { + val animator = mock(ViewPropertyAnimator::class.java, Answers.RETURNS_SELF) + whenever(view.animate()).thenReturn(animator) + + mLargeScreenShadeHeaderController.startCustomizingAnimation(show = true, 0L) + val listenerCaptor = argumentCaptor() + verify(animator).setListener(capture(listenerCaptor)) + + listenerCaptor.value.onAnimationCancel(mock()) + verify(animator).setListener(null) + verify(animator).setUpdateListener(null) + } + @Test fun demoMode_attachDemoMode() { val cb = argumentCaptor()