Merge changes from topic "cancel_unfold_anim_on_rotation" into tm-qpr-dev

* changes:
  Cancel unfold animation when rotation changes
  Create RotationChangeProvider in sysui
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
index d7a0b47..3efdc5a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
@@ -17,6 +17,7 @@
 
 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 @@
      * 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
     }
 
     /**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
index ec938b2..aca9907 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
@@ -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 @@
  */
 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()
-        }
-
-        onRotationChanged(context.display.rotation)
+        rotationChangeProvider.addCallback(rotationListener)
+        rotationListener.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 @@
     }
 
     override fun destroy() {
-        try {
-            windowManagerInterface.removeRotationWatcher(rotationWatcher)
-        } catch (e: RemoteException) {
-            e.rethrowFromSystemServer()
-        }
-
+        rotationChangeProvider.removeCallback(rotationListener)
         scopedUnfoldTransitionProgressProvider.destroy()
     }
 
@@ -76,10 +64,4 @@
     override fun removeCallback(listener: TransitionProgressListener) {
         scopedUnfoldTransitionProgressProvider.removeCallback(listener)
     }
-
-    private inner class RotationWatcher : IRotationWatcher.Stub() {
-        override fun onRotationChanged(rotation: Int) {
-            this@NaturalRotationUnfoldProgressProvider.onRotationChanged(rotation)
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index fc20ac2..6ed3a09 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -26,8 +26,6 @@
 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.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 @@
     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 @@
     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 @@
                 .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 @@
         }
     }
 
-    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
@@ -253,6 +254,7 @@
                     root?.relayout(getLayoutParams())
                 }
             }
+        }
     }
 
     private inner class FoldListener :
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
index eea6ac0..59ad24a 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -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 @@
     @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
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
index 7e07040..e18dd3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
@@ -25,16 +25,21 @@
 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 @@
     @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 @@
                 screenOnStatusProvider,
                 foldProvider,
                 activityTypeProvider,
+                rotationChangeProvider,
                 context.mainExecutor,
                 handler
             )
@@ -92,6 +104,8 @@
             })
         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 @@
         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)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
new file mode 100644
index 0000000..85cfef7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.unfold.updates
+
+import android.testing.AndroidTestingRunner
+import android.view.IRotationWatcher
+import android.view.IWindowManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class RotationChangeProviderTest : SysuiTestCase() {
+
+    private lateinit var rotationChangeProvider: RotationChangeProvider
+
+    @Mock lateinit var windowManagerInterface: IWindowManager
+    @Mock lateinit var listener: RotationListener
+    @Captor lateinit var rotationWatcher: ArgumentCaptor<IRotationWatcher>
+    private val fakeExecutor = FakeExecutor(FakeSystemClock())
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        rotationChangeProvider =
+            RotationChangeProvider(windowManagerInterface, context, fakeExecutor)
+        rotationChangeProvider.addCallback(listener)
+        fakeExecutor.runAllReady()
+        verify(windowManagerInterface).watchRotation(rotationWatcher.capture(), anyInt())
+    }
+
+    @Test
+    fun onRotationChanged_rotationUpdated_listenerReceivesIt() {
+        sendRotationUpdate(42)
+
+        verify(listener).onRotationChanged(42)
+    }
+
+    @Test
+    fun onRotationChanged_subscribersRemoved_noRotationChangeReceived() {
+        sendRotationUpdate(42)
+        verify(listener).onRotationChanged(42)
+
+        rotationChangeProvider.removeCallback(listener)
+        fakeExecutor.runAllReady()
+        sendRotationUpdate(43)
+
+        verify(windowManagerInterface).removeRotationWatcher(any())
+        verifyNoMoreInteractions(listener)
+    }
+
+    private fun sendRotationUpdate(newRotation: Int) {
+        rotationWatcher.value.onRotationChanged(newRotation)
+        fakeExecutor.runAllReady()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
index b2cedbf..a25469b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
@@ -16,18 +16,19 @@
 package com.android.systemui.unfold.util
 
 import android.testing.AndroidTestingRunner
-import android.view.IRotationWatcher
-import android.view.IWindowManager
 import android.view.Surface
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.unfold.TestUnfoldTransitionProvider
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-import com.android.systemui.util.mockito.any
+import com.android.systemui.unfold.updates.RotationChangeProvider
+import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
+import com.android.systemui.util.mockito.capture
 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.clearInvocations
 import org.mockito.Mockito.never
@@ -38,32 +39,26 @@
 @SmallTest
 class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
 
-    @Mock
-    lateinit var windowManager: IWindowManager
+    @Mock lateinit var rotationChangeProvider: RotationChangeProvider
 
     private val sourceProvider = TestUnfoldTransitionProvider()
 
-    @Mock
-    lateinit var transitionListener: TransitionProgressListener
+    @Mock lateinit var transitionListener: TransitionProgressListener
+
+    @Captor private lateinit var rotationListenerCaptor: ArgumentCaptor<RotationListener>
 
     lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
 
-    private val rotationWatcherCaptor =
-        ArgumentCaptor.forClass(IRotationWatcher.Stub::class.java)
-
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        progressProvider = NaturalRotationUnfoldProgressProvider(
-            context,
-            windowManager,
-            sourceProvider
-        )
+        progressProvider =
+            NaturalRotationUnfoldProgressProvider(context, rotationChangeProvider, sourceProvider)
 
         progressProvider.init()
 
-        verify(windowManager).watchRotation(rotationWatcherCaptor.capture(), any())
+        verify(rotationChangeProvider).addCallback(capture(rotationListenerCaptor))
 
         progressProvider.addCallback(transitionListener)
     }
@@ -127,6 +122,6 @@
     }
 
     private fun onRotationChanged(rotation: Int) {
-        rotationWatcherCaptor.value.onRotationChanged(rotation)
+        rotationListenerCaptor.value.onRotationChanged(rotation)
     }
 }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
index a5ec0a4..5a868a4 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -20,10 +20,12 @@
 import android.content.Context
 import android.hardware.SensorManager
 import android.os.Handler
+import android.view.IWindowManager
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.dagger.UnfoldBackground
 import com.android.systemui.unfold.dagger.UnfoldMain
 import com.android.systemui.unfold.updates.FoldProvider
+import com.android.systemui.unfold.updates.RotationChangeProvider
 import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
 import com.android.systemui.unfold.util.CurrentActivityTypeProvider
 import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
@@ -39,11 +41,11 @@
  *
  * This component is meant to be used for places that don't use dagger. By providing those
  * parameters to the factory, all dagger objects are correctly instantiated. See
- * [createUnfoldTransitionProgressProvider] for an example.
+ * [createUnfoldSharedComponent] for an example.
  */
 @Singleton
 @Component(modules = [UnfoldSharedModule::class])
-internal interface UnfoldSharedComponent {
+interface UnfoldSharedComponent {
 
     @Component.Factory
     interface Factory {
@@ -58,9 +60,11 @@
             @BindsInstance @UnfoldMain executor: Executor,
             @BindsInstance @UnfoldBackground backgroundExecutor: Executor,
             @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
+            @BindsInstance windowManager: IWindowManager,
             @BindsInstance contentResolver: ContentResolver = context.contentResolver
         ): UnfoldSharedComponent
     }
 
     val unfoldTransitionProvider: Optional<UnfoldTransitionProgressProvider>
+    val rotationChangeProvider: RotationChangeProvider
 }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 402dd84..a1ed178 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -20,6 +20,7 @@
 import android.content.Context
 import android.hardware.SensorManager
 import android.os.Handler
+import android.view.IWindowManager
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.updates.FoldProvider
 import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
@@ -27,14 +28,15 @@
 import java.util.concurrent.Executor
 
 /**
- * Factory for [UnfoldTransitionProgressProvider].
+ * Factory for [UnfoldSharedComponent].
  *
- * This is needed as Launcher has to create the object manually. If dagger is available, this object
- * is provided in [UnfoldSharedModule].
+ * This wraps the autogenerated factory (for discoverability), and is needed as Launcher has to
+ * create the object manually. If dagger is available, this object is provided in
+ * [UnfoldSharedModule].
  *
  * This should **never** be called from sysui, as the object is already provided in that process.
  */
-fun createUnfoldTransitionProgressProvider(
+fun createUnfoldSharedComponent(
     context: Context,
     config: UnfoldTransitionConfig,
     screenStatusProvider: ScreenStatusProvider,
@@ -44,8 +46,9 @@
     mainHandler: Handler,
     mainExecutor: Executor,
     backgroundExecutor: Executor,
-    tracingTagPrefix: String
-): UnfoldTransitionProgressProvider =
+    tracingTagPrefix: String,
+    windowManager: IWindowManager,
+): UnfoldSharedComponent =
     DaggerUnfoldSharedComponent.factory()
         .create(
             context,
@@ -57,9 +60,6 @@
             mainHandler,
             mainExecutor,
             backgroundExecutor,
-            tracingTagPrefix)
-        .unfoldTransitionProvider
-        .orElse(null)
-        ?: throw IllegalStateException(
-            "Trying to create " +
-                "UnfoldTransitionProgressProvider when the transition is disabled")
+            tracingTagPrefix,
+            windowManager,
+        )
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
index d54481c..7117aaf 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
@@ -26,7 +26,8 @@
  *
  * onTransitionProgress callback could be called on each frame.
  *
- * Use [createUnfoldTransitionProgressProvider] to create instances of this interface
+ * Use [createUnfoldSharedComponent] to create instances of this interface when dagger is not
+ * available.
  */
 interface UnfoldTransitionProgressProvider : CallbackController<TransitionProgressListener> {
 
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 19cfc80..07473b3 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.unfold.dagger.UnfoldMain
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
+import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener
 import com.android.systemui.unfold.updates.hinge.FULLY_CLOSED_DEGREES
 import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES
 import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
@@ -40,22 +41,24 @@
     private val screenStatusProvider: ScreenStatusProvider,
     private val foldProvider: FoldProvider,
     private val activityTypeProvider: CurrentActivityTypeProvider,
+    private val rotationChangeProvider: RotationChangeProvider,
     @UnfoldMain private val mainExecutor: Executor,
     @UnfoldMain private val handler: Handler
 ) : FoldStateProvider {
 
     private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
 
-    @FoldUpdate
-    private var lastFoldUpdate: Int? = null
+    @FoldUpdate private var lastFoldUpdate: Int? = null
 
-    @FloatRange(from = 0.0, to = 180.0)
-    private var lastHingeAngle: Float = 0f
+    @FloatRange(from = 0.0, to = 180.0) private var lastHingeAngle: Float = 0f
 
     private val hingeAngleListener = HingeAngleListener()
     private val screenListener = ScreenStatusListener()
     private val foldStateListener = FoldStateListener()
-    private val timeoutRunnable = TimeoutRunnable()
+    private val timeoutRunnable = Runnable { cancelAnimation() }
+    private val rotationListener = RotationListener {
+        if (isTransitionInProgress) cancelAnimation()
+    }
 
     /**
      * Time after which [FOLD_UPDATE_FINISH_HALF_OPEN] is emitted following a
@@ -72,6 +75,7 @@
         foldProvider.registerCallback(foldStateListener, mainExecutor)
         screenStatusProvider.addCallback(screenListener)
         hingeAngleProvider.addCallback(hingeAngleListener)
+        rotationChangeProvider.addCallback(rotationListener)
     }
 
     override fun stop() {
@@ -79,6 +83,7 @@
         foldProvider.unregisterCallback(foldStateListener)
         hingeAngleProvider.removeCallback(hingeAngleListener)
         hingeAngleProvider.stop()
+        rotationChangeProvider.removeCallback(rotationListener)
     }
 
     override fun addCallback(listener: FoldUpdatesListener) {
@@ -90,14 +95,15 @@
     }
 
     override val isFinishedOpening: Boolean
-        get() = !isFolded &&
+        get() =
+            !isFolded &&
                 (lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN ||
-                        lastFoldUpdate == FOLD_UPDATE_FINISH_HALF_OPEN)
+                    lastFoldUpdate == FOLD_UPDATE_FINISH_HALF_OPEN)
 
     private val isTransitionInProgress: Boolean
         get() =
             lastFoldUpdate == FOLD_UPDATE_START_OPENING ||
-                    lastFoldUpdate == FOLD_UPDATE_START_CLOSING
+                lastFoldUpdate == FOLD_UPDATE_START_CLOSING
 
     private fun onHingeAngle(angle: Float) {
         if (DEBUG) {
@@ -168,7 +174,7 @@
 
     private fun notifyFoldUpdate(@FoldUpdate update: Int) {
         if (DEBUG) {
-            Log.d(TAG, stateToString(update))
+            Log.d(TAG, update.name())
         }
         outputListeners.forEach { it.onFoldUpdate(update) }
         lastFoldUpdate = update
@@ -185,6 +191,8 @@
         handler.removeCallbacks(timeoutRunnable)
     }
 
+    private fun cancelAnimation(): Unit = notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
+
     private inner class ScreenStatusListener : ScreenStatusProvider.ScreenListener {
 
         override fun onScreenTurnedOn() {
@@ -225,16 +233,10 @@
             onHingeAngle(angle)
         }
     }
-
-    private inner class TimeoutRunnable : Runnable {
-        override fun run() {
-            notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN)
-        }
-    }
 }
 
-private fun stateToString(@FoldUpdate update: Int): String {
-    return when (update) {
+fun @receiver:FoldUpdate Int.name() =
+    when (this) {
         FOLD_UPDATE_START_OPENING -> "START_OPENING"
         FOLD_UPDATE_START_CLOSING -> "START_CLOSING"
         FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> "UNFOLDED_SCREEN_AVAILABLE"
@@ -243,15 +245,12 @@
         FOLD_UPDATE_FINISH_CLOSED -> "FINISH_CLOSED"
         else -> "UNKNOWN"
     }
-}
 
 private const val TAG = "DeviceFoldProvider"
 private const val DEBUG = false
 
 /** Threshold after which we consider the device fully unfolded. */
-@VisibleForTesting
-const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
+@VisibleForTesting const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
 
 /** Fold animation on top of apps only when the angle exceeds this threshold. */
-@VisibleForTesting
-const val START_CLOSING_ON_APPS_THRESHOLD_DEGREES = 60
+@VisibleForTesting const val START_CLOSING_ON_APPS_THRESHOLD_DEGREES = 60
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
new file mode 100644
index 0000000..0cf8224
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.unfold.updates
+
+import android.content.Context
+import android.os.RemoteException
+import android.view.IRotationWatcher
+import android.view.IWindowManager
+import android.view.Surface.Rotation
+import com.android.systemui.unfold.dagger.UnfoldMain
+import com.android.systemui.unfold.util.CallbackController
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * Allows to subscribe to rotation changes.
+ *
+ * This is needed as rotation updates from [IWindowManager] are received in a binder thread, while
+ * most of the times we want them in the main one. Updates are provided for the display associated
+ * to [context].
+ */
+class RotationChangeProvider
+@Inject
+constructor(
+    private val windowManagerInterface: IWindowManager,
+    private val context: Context,
+    @UnfoldMain private val mainExecutor: Executor,
+) : CallbackController<RotationChangeProvider.RotationListener> {
+
+    private val listeners = mutableListOf<RotationListener>()
+
+    private val rotationWatcher = RotationWatcher()
+
+    override fun addCallback(listener: RotationListener) {
+        mainExecutor.execute {
+            if (listeners.isEmpty()) {
+                subscribeToRotation()
+            }
+            listeners += listener
+        }
+    }
+
+    override fun removeCallback(listener: RotationListener) {
+        mainExecutor.execute {
+            listeners -= listener
+            if (listeners.isEmpty()) {
+                unsubscribeToRotation()
+            }
+        }
+    }
+
+    private fun subscribeToRotation() {
+        try {
+            windowManagerInterface.watchRotation(rotationWatcher, context.displayId)
+        } catch (e: RemoteException) {
+            throw e.rethrowFromSystemServer()
+        }
+    }
+
+    private fun unsubscribeToRotation() {
+        try {
+            windowManagerInterface.removeRotationWatcher(rotationWatcher)
+        } catch (e: RemoteException) {
+            throw e.rethrowFromSystemServer()
+        }
+    }
+
+    /** Gets notified of rotation changes. */
+    fun interface RotationListener {
+        /** Called once rotation changes. */
+        fun onRotationChanged(@Rotation newRotation: Int)
+    }
+
+    private inner class RotationWatcher : IRotationWatcher.Stub() {
+        override fun onRotationChanged(rotation: Int) {
+            mainExecutor.execute { listeners.forEach { it.onRotationChanged(rotation) } }
+        }
+    }
+}