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

Commit 0def5f98 authored by Nicolo' Mazzucato's avatar Nicolo' Mazzucato
Browse files

Cancel unfold animation when rotation changes

In DeviceFoldStateProvider.kt, FOLD_UPDATE_FINISH_HALF_OPEN is emitted if the rotation changes while the animation is in progress.

 This targets the case when the device goes to tabletop mode during the transition.

+ Using RotationChangeProvider to fix problems when the new orientation change was received in a background thread unexpectedly. This fixes: (1) wrong launcher icons movement after the rotation, and (2) wrong dark overlay scrim after rotation.

+ Minor formattig fixes by ktfmt

Bug: 241743859
Test: DeviceFoldStateProviderTest && NaturalRotationUnfoldProgressProviderTest && manual stress testing
Change-Id: I0ff6a823709e0891a7bcd13528a0e160d6f39c98
parent 683cfc81
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.systemui.shared.animation

import android.graphics.Point
import android.view.Surface
import android.view.Surface.Rotation
import android.view.View
import android.view.WindowManager
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
@@ -58,14 +59,14 @@ class UnfoldMoveFromCenterAnimator @JvmOverloads constructor(
     * Updates display properties in order to calculate the initial position for the views
     * Must be called before [registerViewForAnimation]
     */
    fun updateDisplayProperties() {
    @JvmOverloads
    fun updateDisplayProperties(@Rotation rotation: Int = windowManager.defaultDisplay.rotation) {
        windowManager.defaultDisplay.getSize(screenSize)

        // Simple implementation to get current fold orientation,
        // this might not be correct on all devices
        // TODO: use JetPack WindowManager library to get the fold orientation
        isVerticalFold = windowManager.defaultDisplay.rotation == Surface.ROTATION_0 ||
            windowManager.defaultDisplay.rotation == Surface.ROTATION_180
        isVerticalFold = rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
    }

    /**
+7 −25
Original line number Diff line number Diff line
@@ -15,12 +15,11 @@
package com.android.systemui.unfold.util

import android.content.Context
import android.os.RemoteException
import android.view.IRotationWatcher
import android.view.IWindowManager
import android.view.Surface
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener

/**
 * [UnfoldTransitionProgressProvider] that emits transition progress only when the display has
@@ -29,27 +28,21 @@ import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionPr
 */
class NaturalRotationUnfoldProgressProvider(
    private val context: Context,
    private val windowManagerInterface: IWindowManager,
    private val rotationChangeProvider: RotationChangeProvider,
    unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider
) : UnfoldTransitionProgressProvider {

    private val scopedUnfoldTransitionProgressProvider =
        ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
    private val rotationWatcher = RotationWatcher()

    private var isNaturalRotation: Boolean = false

    fun init() {
        try {
            windowManagerInterface.watchRotation(rotationWatcher, context.display.displayId)
        } catch (e: RemoteException) {
            throw e.rethrowFromSystemServer()
        rotationChangeProvider.addCallback(rotationListener)
        rotationListener.onRotationChanged(context.display.rotation)
    }

        onRotationChanged(context.display.rotation)
    }

    private fun onRotationChanged(rotation: Int) {
    private val rotationListener = RotationListener { rotation ->
        val isNewRotationNatural =
            rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180

@@ -60,12 +53,7 @@ class NaturalRotationUnfoldProgressProvider(
    }

    override fun destroy() {
        try {
            windowManagerInterface.removeRotationWatcher(rotationWatcher)
        } catch (e: RemoteException) {
            e.rethrowFromSystemServer()
        }

        rotationChangeProvider.removeCallback(rotationListener)
        scopedUnfoldTransitionProgressProvider.destroy()
    }

@@ -76,10 +64,4 @@ class NaturalRotationUnfoldProgressProvider(
    override fun removeCallback(listener: TransitionProgressListener) {
        scopedUnfoldTransitionProgressProvider.removeCallback(listener)
    }

    private inner class RotationWatcher : IRotationWatcher.Stub() {
        override fun onRotationChanged(rotation: Int) {
            this@NaturalRotationUnfoldProgressProvider.onRotationChanged(rotation)
        }
    }
}
+9 −7
Original line number Diff line number Diff line
@@ -26,8 +26,6 @@ import android.os.Trace
import android.view.Choreographer
import android.view.Display
import android.view.DisplayInfo
import android.view.IRotationWatcher
import android.view.IWindowManager
import android.view.Surface
import android.view.SurfaceControl
import android.view.SurfaceControlViewHost
@@ -40,6 +38,7 @@ import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.LinearLightRevealEffect
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.util.traceSection
import com.android.wm.shell.displayareahelper.DisplayAreaHelper
import java.util.Optional
@@ -58,7 +57,7 @@ constructor(
    private val displayAreaHelper: Optional<DisplayAreaHelper>,
    @Main private val executor: Executor,
    @UiBackground private val backgroundExecutor: Executor,
    private val windowManagerInterface: IWindowManager
    private val rotationChangeProvider: RotationChangeProvider,
) {

    private val transitionListener = TransitionListener()
@@ -78,7 +77,7 @@ constructor(
    fun init() {
        deviceStateManager.registerCallback(executor, FoldListener())
        unfoldTransitionProgressProvider.addCallback(transitionListener)
        windowManagerInterface.watchRotation(rotationWatcher, context.display.displayId)
        rotationChangeProvider.addCallback(rotationWatcher)

        val containerBuilder =
            SurfaceControl.Builder(SurfaceSession())
@@ -86,7 +85,9 @@ constructor(
                .setName("unfold-overlay-container")

        displayAreaHelper.get().attachToRootDisplayArea(
                Display.DEFAULT_DISPLAY, containerBuilder) { builder ->
            Display.DEFAULT_DISPLAY,
            containerBuilder
        ) { builder ->
            executor.execute {
                overlayContainer = builder.build()

@@ -244,8 +245,8 @@ constructor(
        }
    }

    private inner class RotationWatcher : IRotationWatcher.Stub() {
        override fun onRotationChanged(newRotation: Int) =
    private inner class RotationWatcher : RotationChangeProvider.RotationListener {
        override fun onRotationChanged(newRotation: Int) {
            traceSection("UnfoldLightRevealOverlayAnimation#onRotationChanged") {
                if (currentRotation != newRotation) {
                    currentRotation = newRotation
@@ -254,6 +255,7 @@ constructor(
                }
            }
        }
    }

    private inner class FoldListener :
        FoldStateListener(
+3 −3
Original line number Diff line number Diff line
@@ -17,11 +17,11 @@
package com.android.systemui.unfold

import android.content.Context
import android.view.IWindowManager
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.system.SystemUnfoldSharedModule
import com.android.systemui.unfold.updates.FoldStateProvider
import com.android.systemui.unfold.updates.RotationChangeProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -65,11 +65,11 @@ class UnfoldTransitionModule {
    @Singleton
    fun provideNaturalRotationProgressProvider(
        context: Context,
        windowManager: IWindowManager,
        rotationChangeProvider: RotationChangeProvider,
        unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider>
    ): Optional<NaturalRotationUnfoldProgressProvider> =
        unfoldTransitionProgressProvider.map { provider ->
            NaturalRotationUnfoldProgressProvider(context, windowManager, provider)
            NaturalRotationUnfoldProgressProvider(context, rotationChangeProvider, provider)
        }

    @Provides
+35 −0
Original line number Diff line number Diff line
@@ -25,16 +25,21 @@ import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider
import com.android.systemui.unfold.updates.FoldProvider.FoldCallback
import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations

@@ -48,6 +53,12 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
    @Mock
    private lateinit var handler: Handler

    @Mock
    private lateinit var rotationChangeProvider: RotationChangeProvider

    @Captor
    private lateinit var rotationListener: ArgumentCaptor<RotationListener>

    private val foldProvider = TestFoldProvider()
    private val screenOnStatusProvider = TestScreenOnStatusProvider()
    private val testHingeAngleProvider = TestHingeAngleProvider()
@@ -76,6 +87,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
                screenOnStatusProvider,
                foldProvider,
                activityTypeProvider,
                rotationChangeProvider,
                context.mainExecutor,
                handler
            )
@@ -92,6 +104,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
            })
        foldStateProvider.start()

        verify(rotationChangeProvider).addCallback(capture(rotationListener))

        whenever(handler.postDelayed(any<Runnable>(), any())).then { invocationOnMock ->
            scheduledRunnable = invocationOnMock.getArgument<Runnable>(0)
            scheduledRunnableDelay = invocationOnMock.getArgument<Long>(1)
@@ -372,6 +386,27 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
        assertThat(testHingeAngleProvider.isStarted).isFalse()
    }

    @Test
    fun onRotationChanged_whileInProgress_cancelled() {
        setFoldState(folded = false)
        assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_OPENING)

        rotationListener.value.onRotationChanged(1)

        assertThat(foldUpdates).containsExactly(
            FOLD_UPDATE_START_OPENING, FOLD_UPDATE_FINISH_HALF_OPEN)
    }

    @Test
    fun onRotationChanged_whileNotInProgress_noUpdates() {
        setFoldState(folded = true)
        assertThat(foldUpdates).containsExactly(FOLD_UPDATE_FINISH_CLOSED)

        rotationListener.value.onRotationChanged(1)

        assertThat(foldUpdates).containsExactly(FOLD_UPDATE_FINISH_CLOSED)
    }

    private fun setupForegroundActivityType(isHomeActivity: Boolean?) {
        whenever(activityTypeProvider.isHomeActivity).thenReturn(isHomeActivity)
    }
Loading