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

Commit 8874ab95 authored by Nick Chameyev's avatar Nick Chameyev
Browse files

[Unfold transition] Update latency tracker to listen for animation start

We are going to introduce potential delay before
starting the animation, so updating the latency
tracker to track the end of the unfold display switch
only when the animation is started.

Bug: 258214245
Test: atest com.android.systemui.unfold.UnfoldLatencyTrackerTest
Test: manual unfold with/without animations => check that metric is reported
Change-Id: If3017719631f115579f16493136cce0d0e5e2f80
parent c514109b
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.systemui.keyguard

import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
import com.android.systemui.util.traceSection
import javax.inject.Inject
import javax.inject.Singleton

@@ -39,14 +40,22 @@ class LifecycleScreenStatusProvider @Inject constructor(screenLifecycle: ScreenL
    }

    override fun onScreenTurnedOn() {
        traceSection("$TRACE_TAG#onScreenTurnedOn") {
            listeners.forEach(ScreenListener::onScreenTurnedOn)
        }
    }

    override fun onScreenTurningOff() {
        traceSection("$TRACE_TAG#onScreenTurningOff") {
            listeners.forEach(ScreenListener::onScreenTurningOff)
        }
    }

    override fun onScreenTurningOn() {
        traceSection("$TRACE_TAG#onScreenTurningOn") {
            listeners.forEach(ScreenListener::onScreenTurningOn)
        }
    }
}

private const val TRACE_TAG = "LifecycleScreenStatusProvider"
 No newline at end of file
+78 −6
Original line number Diff line number Diff line
@@ -16,12 +16,18 @@

package com.android.systemui.unfold

import android.content.ContentResolver
import android.content.Context
import android.hardware.devicestate.DeviceStateManager
import android.util.Log
import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.keyguard.ScreenLifecycle
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled
import com.android.systemui.util.Compile
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject

@@ -41,17 +47,19 @@ class UnfoldLatencyTracker
constructor(
    private val latencyTracker: LatencyTracker,
    private val deviceStateManager: DeviceStateManager,
    private val transitionProgressProvider: Optional<UnfoldTransitionProgressProvider>,
    @UiBackground private val uiBgExecutor: Executor,
    private val context: Context,
    private val contentResolver: ContentResolver,
    private val screenLifecycle: ScreenLifecycle
) : ScreenLifecycle.Observer {
) : ScreenLifecycle.Observer, TransitionProgressListener {

    private var folded: Boolean? = null
    private var isTransitionEnabled: Boolean? = null
    private val foldStateListener = FoldStateListener(context)
    private val isFoldable: Boolean
        get() =
            context
                .resources
            context.resources
                .getIntArray(com.android.internal.R.array.config_foldedDeviceStates)
                .isNotEmpty()

@@ -62,6 +70,11 @@ constructor(
        }
        deviceStateManager.registerCallback(uiBgExecutor, foldStateListener)
        screenLifecycle.addObserver(this)
        if (transitionProgressProvider.isPresent) {
            // Might not be present if the device is not a foldable device or unfold transition
            // is disabled in the device configuration
            transitionProgressProvider.get().addCallback(this)
        }
    }

    /**
@@ -71,16 +84,72 @@ constructor(
     * end action event only if we previously received a fold state.
     */
    override fun onScreenTurnedOn() {
        if (folded == false) {
        if (DEBUG) {
            Log.d(
                TAG,
                "onScreenTurnedOn: folded = $folded, isTransitionEnabled = $isTransitionEnabled"
            )
        }

        // We use onScreenTurnedOn event to finish tracking only if we are not playing
        // the unfold animation (e.g. it could be disabled because of battery saver).
        // When animation is enabled finishing of the tracking will be done in onTransitionStarted.
        if (folded == false && isTransitionEnabled == false) {
            latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD)

            if (DEBUG) {
                Log.d(TAG, "onScreenTurnedOn: ending ACTION_SWITCH_DISPLAY_UNFOLD")
            }
        }
    }

    /**
     * This callback is used to end the metric when the unfold animation is enabled because it could
     * add an additional delay to synchronize with launcher.
     */
    override fun onTransitionStarted() {
        if (DEBUG) {
            Log.d(
                TAG,
                "onTransitionStarted: folded = $folded, isTransitionEnabled = $isTransitionEnabled"
            )
        }

        if (folded == false && isTransitionEnabled == true) {
            latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD)

            if (DEBUG) {
                Log.d(TAG, "onTransitionStarted: ending ACTION_SWITCH_DISPLAY_UNFOLD")
            }
        }
    }

    private fun onFoldEvent(folded: Boolean) {
        if (this.folded != folded) {
        val oldFolded = this.folded

        if (oldFolded != folded) {
            this.folded = folded
            if (!folded) { // unfolding started

            if (DEBUG) {
                Log.d(TAG, "Received onFoldEvent = $folded")
            }

            // Do not start tracking when oldFolded is null, this means that this is the first
            // onFoldEvent after booting the device or starting SystemUI and not actual folding or
            // unfolding the device.
            if (oldFolded != null && !folded) {
                // Unfolding started
                latencyTracker.onActionStart(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD)
                isTransitionEnabled =
                    transitionProgressProvider.isPresent && contentResolver.areAnimationsEnabled()

                if (DEBUG) {
                    Log.d(
                        TAG,
                        "Starting ACTION_SWITCH_DISPLAY_UNFOLD, " +
                            "isTransitionEnabled = $isTransitionEnabled"
                    )
                }
            }
        }
    }
@@ -88,3 +157,6 @@ constructor(
    private inner class FoldStateListener(context: Context) :
        DeviceStateManager.FoldStateListener(context, { onFoldEvent(it) })
}

private const val TAG = "UnfoldLatencyTracker"
private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE)
+91 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.unfold

import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
import android.provider.Settings
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
@@ -32,9 +33,11 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
import java.util.Optional

@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -59,14 +62,18 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() {

    private lateinit var unfoldLatencyTracker: UnfoldLatencyTracker

    private val transitionProgressProvider = TestUnfoldTransitionProvider()

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
        unfoldLatencyTracker = UnfoldLatencyTracker(
            latencyTracker,
            deviceStateManager,
            Optional.of(transitionProgressProvider),
            context.mainExecutor,
            context,
            context.contentResolver,
            screenLifecycle
        ).apply { init() }
        deviceStates = FoldableTestUtils.findDeviceStates(context)
@@ -76,22 +83,88 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() {
    }

    @Test
    fun unfold_eventPropagated() {
    fun unfold_startedFolded_animationsDisabled_eventPropagatedOnScreenTurnedOnEvent() {
        setAnimationsEnabled(false)
        sendFoldEvent(folded = true)
        sendFoldEvent(folded = false)

        sendScreenTurnedOnEvent()

        verify(latencyTracker).onActionStart(any())
        verify(latencyTracker).onActionEnd(any())
    }

    @Test
    fun unfold_startedFolded_animationsEnabledOnScreenTurnedOn_eventNotFinished() {
        setAnimationsEnabled(true)
        sendFoldEvent(folded = true)
        sendFoldEvent(folded = false)

        sendScreenTurnedOnEvent()

        verify(latencyTracker).onActionStart(any())
        verify(latencyTracker, never()).onActionEnd(any())
    }

    @Test
    fun unfold_firstFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventNotPropagated() {
        setAnimationsEnabled(true)
        sendFoldEvent(folded = false)

        sendScreenTurnedOnEvent()
        transitionProgressProvider.onTransitionStarted()

        verifyNoMoreInteractions(latencyTracker)
    }

    @Test
    fun unfold_secondFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() {
        setAnimationsEnabled(true)
        sendFoldEvent(folded = true)
        sendFoldEvent(folded = false)

        sendScreenTurnedOnEvent()
        transitionProgressProvider.onTransitionStarted()

        verify(latencyTracker).onActionStart(any())
        verify(latencyTracker).onActionEnd(any())
    }

    @Test
    fun unfold_unfoldFoldUnfoldAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() {
        setAnimationsEnabled(true)
        sendFoldEvent(folded = false)
        sendFoldEvent(folded = true)
        sendFoldEvent(folded = false)

        sendScreenTurnedOnEvent()
        transitionProgressProvider.onTransitionStarted()

        verify(latencyTracker).onActionStart(any())
        verify(latencyTracker).onActionEnd(any())
    }

    @Test
    fun fold_eventNotPropagated() {
    fun fold_animationsDisabled_screenTurnedOn_eventNotPropagated() {
        setAnimationsEnabled(false)
        sendFoldEvent(folded = true)

        sendScreenTurnedOnEvent() // outer display on.

        verifyNoMoreInteractions(latencyTracker)
    }

    @Test
    fun fold_animationsEnabled_screenTurnedOn_eventNotPropagated() {
        setAnimationsEnabled(true)
        sendFoldEvent(folded = true)

        sendScreenTurnedOnEvent() // outer display on.
        transitionProgressProvider.onTransitionStarted()

        verifyNoMoreInteractions(latencyTracker)
    }

    @Test
    fun onScreenTurnedOn_stateNeverSet_eventNotPropagated() {
        sendScreenTurnedOnEvent()
@@ -107,4 +180,20 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() {
    private fun sendScreenTurnedOnEvent() {
        screenLifecycleCaptor.value.onScreenTurnedOn()
    }

    private fun setAnimationsEnabled(enabled: Boolean) {
        val durationScale =
            if (enabled) {
                1f
            } else {
                0f
            }

        // It uses [TestableSettingsProvider] and it will be cleared after the test
        Settings.Global.putString(
            context.contentResolver,
            Settings.Global.ANIMATOR_DURATION_SCALE,
            durationScale.toString()
        )
    }
}
 No newline at end of file
+4 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
package com.android.systemui.unfold.progress

import android.os.Trace
import android.os.Trace.TRACE_TAG_APP
import android.util.Log
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.FloatPropertyCompat
@@ -157,7 +158,10 @@ class PhysicsBasedUnfoldTransitionProgressProvider(
    }

    private fun onStartTransition() {
        Trace.beginSection( "$TAG#onStartTransition")
        listeners.forEach { it.onTransitionStarted() }
        Trace.endSection()

        isTransitionRunning = true

        if (DEBUG) {