Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 73cb12ad authored by András Kurucz's avatar András Kurucz
Browse files

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
parent 382cf794
Loading
Loading
Loading
Loading
+11 −5
Original line number Diff line number Diff line
@@ -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)
+13 −5
Original line number Diff line number Diff line
@@ -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)
+83 −0
Original line number Diff line number Diff line
/*
 * 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()
    }
}
+100 −0
Original line number Diff line number Diff line
/*
 * 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()
    }
}