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

Commit 002ab534 authored by Juan Sebastian Martinez's avatar Juan Sebastian Martinez Committed by Android (Google) Code Review
Browse files

Merge "Reversing QS long-press effect if no activity launch occurs." into main

parents 2f4c61b2 a9bb0db2
Loading
Loading
Loading
Loading
+21 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ 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.ActivityTransitionAnimator
import com.android.systemui.classifier.falsingManager
import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.kosmos.testScope
@@ -52,6 +53,7 @@ class QSLongPressEffectTest : SysuiTestCase() {
    private val vibratorHelper = kosmos.vibratorHelper
    private val qsTile = kosmos.qsTileFactory.createTile("Test Tile")
    @Mock private lateinit var callback: QSLongPressEffect.Callback
    @Mock private lateinit var controller: ActivityTransitionAnimator.Controller

    private val effectDuration = 400
    private val lowTickDuration = 12
@@ -218,8 +220,9 @@ class QSLongPressEffectTest : SysuiTestCase() {
            // GIVEN that the animation completes
            longPressEffect.handleAnimationComplete()

            // THEN the effect ends in the idle state.
            // THEN the effect ends in the idle state and the reversed callback is used.
            assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
            verify(callback, times(1)).onEffectFinishedReversing()
        }

    @Test
@@ -348,6 +351,23 @@ class QSLongPressEffectTest : SysuiTestCase() {
        assertThat(clickState).isEqualTo(QSLongPressEffect.State.IDLE)
    }

    @Test
    fun onLongClickTransitionCancelled_whileInLongClickState_reversesEffect() =
        testWhileInState(QSLongPressEffect.State.LONG_CLICKED) {
            // GIVEN a transition controller delegate
            val delegate = longPressEffect.createTransitionControllerDelegate(controller)

            // WHEN the activity launch animation is cancelled
            val newOccludedState = false
            delegate.onTransitionAnimationCancelled(newOccludedState)

            // THEN the effect reverses and ends in RUNNING_BACKWARDS_FROM_CANCEL
            assertThat(longPressEffect.state)
                .isEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_CANCEL)
            verify(callback, times(1)).onReverseAnimator(false)
            verify(controller).onTransitionAnimationCancelled(newOccludedState)
        }

    private fun testWithScope(initialize: Boolean = true, test: suspend TestScope.() -> Unit) =
        with(kosmos) {
            testScope.runTest {
+62 −5
Original line number Diff line number Diff line
@@ -16,9 +16,15 @@

package com.android.systemui.haptics.qs

import android.content.ComponentName
import android.os.VibrationEffect
import android.service.quicksettings.Tile
import android.view.View
import androidx.annotation.VisibleForTesting
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.DelegateTransitionAnimatorController
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
@@ -58,6 +64,7 @@ constructor(
    /** The [QSTile] and [Expandable] used to perform a long-click and click actions */
    var qsTile: QSTile? = null
    var expandable: Expandable? = null
        private set

    /** Haptic effects */
    private val durations =
@@ -125,9 +132,11 @@ constructor(
    }

    fun handleAnimationStart() {
        if (state == State.TIMEOUT_WAIT) {
            vibrate(longPressHint)
            setState(State.RUNNING_FORWARD)
        }
    }

    /** This function is called both when an animator completes or gets cancelled */
    fun handleAnimationComplete() {
@@ -147,7 +156,10 @@ constructor(
                setState(getStateForClick())
                qsTile?.click(expandable)
            }
            State.RUNNING_BACKWARDS_FROM_CANCEL -> setState(State.IDLE)
            State.RUNNING_BACKWARDS_FROM_CANCEL -> {
                callback?.onEffectFinishedReversing()
                setState(State.IDLE)
            }
            else -> {}
        }
    }
@@ -222,13 +234,58 @@ constructor(

    fun resetState() = setState(State.IDLE)

    fun createExpandableFromView(view: View) {
        expandable =
            object : Expandable {
                override fun activityTransitionController(
                    launchCujType: Int?,
                    cookie: ActivityTransitionAnimator.TransitionCookie?,
                    component: ComponentName?,
                    returnCujType: Int?,
                ): ActivityTransitionAnimator.Controller? {
                    val delegatedController =
                        ActivityTransitionAnimator.Controller.fromView(
                            view,
                            launchCujType,
                            cookie,
                            component,
                            returnCujType,
                        )
                    return delegatedController?.let { createTransitionControllerDelegate(it) }
                }

                override fun dialogTransitionController(
                    cuj: DialogCuj?,
                ): DialogTransitionAnimator.Controller? =
                    DialogTransitionAnimator.Controller.fromView(view, cuj)
            }
    }

    @VisibleForTesting
    fun createTransitionControllerDelegate(
        controller: ActivityTransitionAnimator.Controller
    ): DelegateTransitionAnimatorController {
        val delegated =
            object : DelegateTransitionAnimatorController(controller) {
                override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
                    if (state == State.LONG_CLICKED) {
                        setState(State.RUNNING_BACKWARDS_FROM_CANCEL)
                        callback?.onReverseAnimator(false)
                    }
                    delegate.onTransitionAnimationCancelled(newKeyguardOccludedState)
                }
            }
        return delegated
    }

    enum class State {
        IDLE, /* The effect is idle waiting for touch input */
        TIMEOUT_WAIT, /* The effect is waiting for a tap timeout period */
        RUNNING_FORWARD, /* The effect is running normally */
        /* The effect was interrupted by an ACTION_UP and is now running backwards */
        RUNNING_BACKWARDS_FROM_UP,
        /* The effect was interrupted by an ACTION_CANCEL and is now running backwards */
        /* The effect was cancelled by an ACTION_CANCEL or a shade collapse and is now running
        backwards */
        RUNNING_BACKWARDS_FROM_CANCEL,
        CLICKED, /* The effect has ended with a click */
        LONG_CLICKED, /* The effect has ended with a long-click */
@@ -247,7 +304,7 @@ constructor(
        fun onStartAnimator()

        /** Reverse the effect animator */
        fun onReverseAnimator()
        fun onReverseAnimator(playHaptics: Boolean = true)

        /** Cancel the effect animator */
        fun onCancelAnimator()
+7 −13
Original line number Diff line number Diff line
@@ -107,8 +107,10 @@ constructor(
        set(value) {
            if (field == value) return
            field = value
            if (longPressEffect?.state != QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_CANCEL) {
                updateHeight()
            }
        }

    override var squishinessFraction: Float = 1f
        set(value) {
@@ -381,14 +383,6 @@ constructor(
    }

    private fun updateHeight() {
        // TODO(b/332900989): Find a more robust way of resetting the tile if not reset by the
        //  launch animation.
        if (!haveLongPressPropertiesBeenReset && longPressEffect != null) {
            // The launch animation of a long-press effect did not reset the long-press effect so
            // we must do it here
            resetLongPressEffectProperties()
            longPressEffect.resetState()
        }
        val actualHeight =
            if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
                heightOverride
@@ -417,17 +411,17 @@ constructor(
    }

    override fun init(tile: QSTile) {
        val expandable = Expandable.fromView(this)
        if (longPressEffect != null) {
            isHapticFeedbackEnabled = false
            longPressEffect.qsTile = tile
            longPressEffect.expandable = expandable
            longPressEffect.createExpandableFromView(this)
            initLongPressEffectCallback()
            init(
                { _: View -> longPressEffect.onTileClick() },
                null, // Haptics and long-clicks will be handled by the [QSLongPressEffect]
            )
        } else {
            val expandable = Expandable.fromView(this)
            init(
                { _: View? -> tile.click(expandable) },
                { _: View? ->
@@ -475,10 +469,10 @@ constructor(
                    }
                }

                override fun onReverseAnimator() {
                override fun onReverseAnimator(playHaptics: Boolean) {
                    longPressEffectAnimator?.let {
                        val pausedProgress = it.animatedFraction
                        longPressEffect?.playReverseHaptics(pausedProgress)
                        if (playHaptics) longPressEffect?.playReverseHaptics(pausedProgress)
                        it.reverse()
                    }
                }