From 73cb12adc32c184a6b8423e8731e804036cddcea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20Kurucz?= Date: Mon, 29 Aug 2022 11:45:17 +0000 Subject: [PATCH] Fix missing Notification collapse animation on LS When we start to expand a Notification on the LS but we abort it halfway, the Notification should animate back to the collapsed state. This animation was missing, because we tried to access the "ExpandableView#actualHeight" property via reflection. Test: create a makepush build and observe the animation on the LS Test: atest DragDownHelperTest Test: atest PulseExpansionHandlerTest Fixes: 237084760 Change-Id: I3798dec1ab016d89b61e87a3192199ea16181472 --- .../LockscreenShadeTransitionController.kt | 16 ++- .../statusbar/PulseExpansionHandler.kt | 18 +++- .../systemui/statusbar/DragDownHelperTest.kt | 83 +++++++++++++++ .../statusbar/PulseExpansionHandlerTest.kt | 100 ++++++++++++++++++ 4 files changed, 207 insertions(+), 10 deletions(-) create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 74d8f1beaec1b..d3343dfdcd192 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -2,7 +2,6 @@ package com.android.systemui.statusbar import android.animation.Animator import android.animation.AnimatorListenerAdapter -import android.animation.ObjectAnimator import android.animation.ValueAnimator import android.content.Context import android.content.res.Configuration @@ -880,15 +879,22 @@ class DragDownHelper( child.actualHeight = (child.collapsedHeight + rubberband).toInt() } - private fun cancelChildExpansion(child: ExpandableView) { + @VisibleForTesting + fun cancelChildExpansion( + child: ExpandableView, + animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS + ) { if (child.actualHeight == child.collapsedHeight) { expandCallback.setUserLockedChild(child, false) return } - val anim = ObjectAnimator.ofInt(child, "actualHeight", - child.actualHeight, child.collapsedHeight) + val anim = ValueAnimator.ofInt(child.actualHeight, child.collapsedHeight) anim.interpolator = Interpolators.FAST_OUT_SLOW_IN - anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS + anim.duration = animationDuration + anim.addUpdateListener { animation: ValueAnimator -> + // don't use reflection, because the `actualHeight` field may be obfuscated + child.actualHeight = animation.animatedValue as Int + } anim.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { expandCallback.setUserLockedChild(child, false) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index 6302ef11cfe79..bbff0466cf50c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -18,7 +18,7 @@ package com.android.systemui.statusbar import android.animation.Animator import android.animation.AnimatorListenerAdapter -import android.animation.ObjectAnimator +import android.animation.ValueAnimator import android.content.Context import android.content.res.Configuration import android.os.PowerManager @@ -28,6 +28,7 @@ import android.util.IndentingPrintWriter import android.view.MotionEvent import android.view.VelocityTracker import android.view.ViewConfiguration +import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.Gefingerpoken import com.android.systemui.R @@ -276,15 +277,22 @@ constructor( } } - private fun reset(child: ExpandableView) { + @VisibleForTesting + fun reset( + child: ExpandableView, + animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS.toLong() + ) { if (child.actualHeight == child.collapsedHeight) { setUserLocked(child, false) return } - val anim = ObjectAnimator.ofInt(child, "actualHeight", - child.actualHeight, child.collapsedHeight) + val anim = ValueAnimator.ofInt(child.actualHeight, child.collapsedHeight) anim.interpolator = Interpolators.FAST_OUT_SLOW_IN - anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS.toLong() + anim.duration = animationDuration + anim.addUpdateListener { animation: ValueAnimator -> + // don't use reflection, because the `actualHeight` field may be obfuscated + child.actualHeight = animation.animatedValue as Int + } anim.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { setUserLocked(child, false) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt new file mode 100644 index 0000000000000..d925d0acd8d8a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022 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 + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.ExpandHelper +import com.android.systemui.SysuiTestCase +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.statusbar.notification.row.ExpandableView +import com.android.systemui.util.mockito.mock +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.atLeast +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever + +@SmallTest +@TestableLooper.RunWithLooper +@RunWith(AndroidTestingRunner::class) +class DragDownHelperTest : SysuiTestCase() { + + private lateinit var dragDownHelper: DragDownHelper + + private val collapsedHeight = 300 + private val falsingManager: FalsingManager = mock() + private val falsingCollector: FalsingCollector = mock() + private val dragDownloadCallback: LockscreenShadeTransitionController = mock() + private val expandableView: ExpandableView = mock() + private val expandCallback: ExpandHelper.Callback = mock() + + @Before + fun setUp() { + whenever(expandableView.collapsedHeight).thenReturn(collapsedHeight) + + dragDownHelper = DragDownHelper( + falsingManager, + falsingCollector, + dragDownloadCallback, + mContext + ).also { + it.expandCallback = expandCallback + } + } + + @Test + fun cancelChildExpansion_updateHeight() { + whenever(expandableView.actualHeight).thenReturn(500) + + dragDownHelper.cancelChildExpansion(expandableView, animationDuration = 0) + + verify(expandableView, atLeast(1)).actualHeight = collapsedHeight + } + + @Test + fun cancelChildExpansion_dontUpdateHeight() { + whenever(expandableView.actualHeight).thenReturn(collapsedHeight) + + dragDownHelper.cancelChildExpansion(expandableView, animationDuration = 0) + + verify(expandableView, never()).actualHeight = anyInt() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt new file mode 100644 index 0000000000000..44cbe51a30ac0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022 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 + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.dump.DumpManager +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator +import com.android.systemui.statusbar.notification.row.ExpandableView +import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager +import com.android.systemui.statusbar.phone.HeadsUpManagerPhone +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.util.mockito.mock +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.atLeast +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever + +@SmallTest +@TestableLooper.RunWithLooper +@RunWith(AndroidTestingRunner::class) +class PulseExpansionHandlerTest : SysuiTestCase() { + + private lateinit var pulseExpansionHandler: PulseExpansionHandler + + private val collapsedHeight = 300 + private val wakeUpCoordinator: NotificationWakeUpCoordinator = mock() + private val bypassController: KeyguardBypassController = mock() + private val headsUpManager: HeadsUpManagerPhone = mock() + private val roundnessManager: NotificationRoundnessManager = mock() + private val configurationController: ConfigurationController = mock() + private val statusBarStateController: StatusBarStateController = mock() + private val falsingManager: FalsingManager = mock() + private val lockscreenShadeTransitionController: LockscreenShadeTransitionController = mock() + private val falsingCollector: FalsingCollector = mock() + private val dumpManager: DumpManager = mock() + private val expandableView: ExpandableView = mock() + + @Before + fun setUp() { + whenever(expandableView.collapsedHeight).thenReturn(collapsedHeight) + + pulseExpansionHandler = PulseExpansionHandler( + mContext, + wakeUpCoordinator, + bypassController, + headsUpManager, + roundnessManager, + configurationController, + statusBarStateController, + falsingManager, + lockscreenShadeTransitionController, + falsingCollector, + dumpManager + ) + } + + @Test + fun resetChild_updateHeight() { + whenever(expandableView.actualHeight).thenReturn(500) + + pulseExpansionHandler.reset(expandableView, animationDuration = 0) + + verify(expandableView, atLeast(1)).actualHeight = collapsedHeight + } + + @Test + fun resetChild_dontUpdateHeight() { + whenever(expandableView.actualHeight).thenReturn(collapsedHeight) + + pulseExpansionHandler.reset(expandableView, animationDuration = 0) + + verify(expandableView, never()).actualHeight = anyInt() + } +}