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

Commit c81a0cc5 authored by Yalan Yiue's avatar Yalan Yiue Committed by Android (Google) Code Review
Browse files

Merge "Add a new jank enum for SysUI fold animation" into main

parents 6602c300 98c47859
Loading
Loading
Loading
Loading
+14 −2
Original line number Diff line number Diff line
@@ -147,8 +147,16 @@ public class Cuj {
     */
    public static final int CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW = 104;

    /**
     * Track fade-in animation when in SystemUI process fold
     *
     * <p>Tracking starts after the screen turns on and finish when the animation is over {@link
     * com.android.systemui.unfold.FoldLightRevealOverlayAnimation#playFoldLightRevealOverlayAnimation} for the span
     */
    public static final int CUJ_FOLD_ANIM = 105;

    // When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE.
    @VisibleForTesting static final int LAST_CUJ = CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW;
    @VisibleForTesting static final int LAST_CUJ = CUJ_FOLD_ANIM;

    /** @hide */
    @IntDef({
@@ -244,7 +252,8 @@ public class Cuj {
            CUJ_LAUNCHER_WIDGET_BOTTOM_SHEET_CLOSE_BACK,
            CUJ_LAUNCHER_PRIVATE_SPACE_LOCK,
            CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK,
            CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW
            CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW,
            CUJ_FOLD_ANIM
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface CujType {}
@@ -351,6 +360,7 @@ public class Cuj {
        CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_PRIVATE_SPACE_LOCK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_PRIVATE_SPACE_LOCK;
        CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_PRIVATE_SPACE_UNLOCK;
        CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_MAXIMIZE_WINDOW;
        CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_FOLD_ANIM] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__FOLD_ANIM;
    }

    private Cuj() {
@@ -555,6 +565,8 @@ public class Cuj {
                return "LAUNCHER_PRIVATE_SPACE_UNLOCK";
            case CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW:
                return "DESKTOP_MODE_MAXIMIZE_WINDOW";
            case CUJ_FOLD_ANIM:
                return "FOLD_ANIM";
        }
        return "UNKNOWN";
    }
+21 −7
Original line number Diff line number Diff line
@@ -22,9 +22,12 @@ import android.animation.ValueAnimator
import android.annotation.BinderThread
import android.os.SystemProperties
import android.util.Log
import android.view.View
import android.view.animation.DecelerateInterpolator
import com.android.app.tracing.TraceUtils.traceAsync
import com.android.internal.foldables.FoldLockSettingAvailabilityProvider
import com.android.internal.jank.Cuj.CUJ_FOLD_ANIM
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DeviceStateRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
@@ -65,7 +68,8 @@ constructor(
    @Background private val applicationScope: CoroutineScope,
    private val animationStatusRepository: AnimationStatusRepository,
    private val controllerFactory: FullscreenLightRevealAnimationController.Factory,
    private val foldLockSettingAvailabilityProvider: FoldLockSettingAvailabilityProvider
    private val foldLockSettingAvailabilityProvider: FoldLockSettingAvailabilityProvider,
    private val interactionJankMonitor: InteractionJankMonitor
) : FullscreenLightRevealAnimation {

    private val revealProgressValueAnimator: ValueAnimator =
@@ -149,7 +153,8 @@ constructor(
    private suspend fun waitForGoToSleep() =
        traceAsync(TAG, "waitForGoToSleep()") { powerInteractor.isAsleep.filter { it }.first() }

    private suspend fun playFoldLightRevealOverlayAnimation() {
    private suspend fun playFoldLightRevealOverlayAnimation() =
        trackCuj(CUJ_FOLD_ANIM, controller.scrimView) {
            revealProgressValueAnimator.duration = ANIMATION_DURATION
            revealProgressValueAnimator.interpolator = DecelerateInterpolator()
            revealProgressValueAnimator.addUpdateListener { animation ->
@@ -158,6 +163,15 @@ constructor(
            revealProgressValueAnimator.startAndAwaitCompletion()
        }

    private suspend fun trackCuj(cuj: Int, view: View?, block: suspend () -> Unit) {
        view?.let { interactionJankMonitor.begin(it, cuj) }
        try {
            block()
        } finally {
            if (view != null) interactionJankMonitor.end(cuj)
        }
    }

    private suspend fun ValueAnimator.startAndAwaitCompletion(): Unit =
        suspendCancellableCoroutine { continuation ->
            val listener =
+4 −1
Original line number Diff line number Diff line
@@ -81,7 +81,10 @@ constructor(

    private var currentRotation: Int = context.display.rotation
    private var root: SurfaceControlViewHost? = null
    private var scrimView: LightRevealScrim? = null

    /** The scrim view that is used to reveal the screen. */
    var scrimView: LightRevealScrim? = null
        private set

    private val rotationWatcher = RotationWatcher()
    private val internalDisplayInfos: List<DisplayInfo> =
+64 −20
Original line number Diff line number Diff line
@@ -22,7 +22,10 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.foldables.FoldLockSettingAvailabilityProvider
import com.android.internal.jank.Cuj.CUJ_FOLD_ANIM
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
import com.android.systemui.display.data.repository.fakeDeviceStateRepository
import com.android.systemui.kosmos.Kosmos
@@ -32,27 +35,34 @@ import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.se
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setScreenPowerState
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.power.shared.model.ScreenPowerState
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.util.animation.data.repository.fakeAnimationStatusRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.atLeast
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner::class)
@OptIn(ExperimentalCoroutinesApi::class)
class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
    @get:Rule val animatorTestRule = AnimatorTestRule(this)

    private val kosmos = Kosmos()
    private val testScope: TestScope = kosmos.testScope
    private val fakeDeviceStateRepository = kosmos.fakeDeviceStateRepository
@@ -63,6 +73,8 @@ class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
    private val mockFoldLockSettingAvailabilityProvider =
        mock<FoldLockSettingAvailabilityProvider>()
    private val onOverlayReady = mock<Runnable>()
    private val mockJankMonitor = mock<InteractionJankMonitor>()
    private val mockScrimView = mock<LightRevealScrim>()
    private lateinit var foldLightRevealAnimation: FoldLightRevealOverlayAnimation

    @Before
@@ -71,6 +83,8 @@ class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
        whenever(mockFoldLockSettingAvailabilityProvider.isFoldLockBehaviorAvailable)
            .thenReturn(true)
        fakeAnimationStatusRepository.onAnimationStatusChanged(true)
        whenever(mockFullScreenController.scrimView).thenReturn(mockScrimView)
        whenever(mockJankMonitor.begin(any(), eq(CUJ_FOLD_ANIM))).thenReturn(true)

        foldLightRevealAnimation =
            FoldLightRevealOverlayAnimation(
@@ -80,7 +94,8 @@ class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
                testScope.backgroundScope,
                fakeAnimationStatusRepository,
                mockControllerFactory,
                mockFoldLockSettingAvailabilityProvider
                mockFoldLockSettingAvailabilityProvider,
                mockJankMonitor
            )
        foldLightRevealAnimation.init()
    }
@@ -99,9 +114,9 @@ class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
        testScope.runTest {
            foldDeviceToScreenOff()
            emitLastWakefulnessEventStartingToSleep()
            advanceTimeBy(SHORT_DELAY_MS)
            advanceTime(SHORT_DELAY_MS)
            turnScreenOn()
            advanceTimeBy(ANIMATION_DURATION)
            advanceTime(ANIMATION_DURATION)

            verifyFoldAnimationDidNotPlay()
        }
@@ -111,8 +126,8 @@ class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
        testScope.runTest {
            foldDeviceToScreenOff()
            emitLastWakefulnessEventStartingToSleep()
            advanceTimeBy(SHORT_DELAY_MS)
            advanceTimeBy(ANIMATION_DURATION)
            advanceTime(SHORT_DELAY_MS)
            advanceTime(ANIMATION_DURATION)

            verifyFoldAnimationDidNotPlay()
        }
@@ -122,10 +137,10 @@ class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
        testScope.runTest {
            foldDeviceToScreenOff()
            foldLightRevealAnimation.onScreenTurningOn {}
            advanceTimeBy(WAIT_FOR_ANIMATION_TIMEOUT_MS)
            advanceTime(WAIT_FOR_ANIMATION_TIMEOUT_MS)
            powerInteractor.setScreenPowerState(ScreenPowerState.SCREEN_ON)
            advanceTimeBy(SHORT_DELAY_MS)
            advanceTimeBy(ANIMATION_DURATION)
            advanceTime(SHORT_DELAY_MS)
            advanceTime(ANIMATION_DURATION)

            verifyFoldAnimationDidNotPlay()
        }
@@ -135,10 +150,12 @@ class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
        testScope.runTest {
            foldDeviceToScreenOff()
            foldLightRevealAnimation.onScreenTurningOn {}
            advanceTimeBy(SHORT_DELAY_MS)
            advanceTimeBy(ANIMATION_DURATION)
            advanceTime(SHORT_DELAY_MS)
            clearInvocations(mockFullScreenController)

            // Unfold the device
            fakeDeviceStateRepository.emit(DeviceState.HALF_FOLDED)
            advanceTimeBy(SHORT_DELAY_MS)
            advanceTime(SHORT_DELAY_MS)
            powerInteractor.setScreenPowerState(ScreenPowerState.SCREEN_ON)

            verifyOverlayWasRemoved()
@@ -149,11 +166,37 @@ class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
        testScope.runTest {
            foldDeviceToScreenOff()
            turnScreenOn()
            advanceTimeBy(ANIMATION_DURATION)
            clearInvocations(mockFullScreenController)
            advanceTime(ANIMATION_DURATION)

            verifyOverlayWasRemoved()
        }

    @Test
    fun foldToScreenOn_jankCujIsStarted() =
        testScope.runTest {
            foldDeviceToScreenOff()
            turnScreenOn()
            // Cuj has started but not ended
            verify(mockJankMonitor, times(1)).begin(any(), eq(CUJ_FOLD_ANIM))
            verify(mockJankMonitor, never()).end(eq(CUJ_FOLD_ANIM))
        }

    @Test
    fun foldToScreenOn_animationFinished_jankCujIsFinished() =
        testScope.runTest {
            foldDeviceToScreenOff()
            turnScreenOn()

            advanceTime(ANIMATION_DURATION)
            verify(mockJankMonitor, times(1)).end(eq(CUJ_FOLD_ANIM))
        }

    private fun TestScope.advanceTime(timeMs: Long) {
        animatorTestRule.advanceTimeBy(timeMs)
        advanceTimeBy(timeMs)
    }

    @Test
    fun unfold_immediatelyRunRunnable() =
        testScope.runTest {
@@ -165,18 +208,18 @@ class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
    private suspend fun TestScope.foldDeviceToScreenOff() {
        fakeDeviceStateRepository.emit(DeviceState.HALF_FOLDED)
        powerInteractor.setScreenPowerState(ScreenPowerState.SCREEN_ON)
        advanceTimeBy(SHORT_DELAY_MS)
        advanceTime(SHORT_DELAY_MS)
        fakeDeviceStateRepository.emit(DeviceState.FOLDED)
        advanceTimeBy(SHORT_DELAY_MS)
        advanceTime(SHORT_DELAY_MS)
        powerInteractor.setScreenPowerState(ScreenPowerState.SCREEN_OFF)
        advanceTimeBy(SHORT_DELAY_MS)
        advanceTime(SHORT_DELAY_MS)
    }

    private fun TestScope.turnScreenOn() {
        foldLightRevealAnimation.onScreenTurningOn {}
        advanceTimeBy(SHORT_DELAY_MS)
        advanceTime(SHORT_DELAY_MS)
        powerInteractor.setScreenPowerState(ScreenPowerState.SCREEN_ON)
        advanceTimeBy(SHORT_DELAY_MS)
        advanceTime(SHORT_DELAY_MS)
    }

    private fun emitLastWakefulnessEventStartingToSleep() =
@@ -195,6 +238,7 @@ class FoldLightRevealOverlayAnimationTest : SysuiTestCase() {
        const val WAIT_FOR_ANIMATION_TIMEOUT_MS = 2000L
        val ANIMATION_DURATION: Long
            get() = SystemProperties.getLong("persist.fold_animation_duration", 200L)

        const val SHORT_DELAY_MS = 50L
    }
}