Add AOD fold animation

When folding the device and entering AOD, keyguard
should fade in from the side and not the top as is
the default.

Bug: 346325723
Test: atest GoneToAodTransitionViewModelTest
Flag: com.android.systemui.migrate_clocks_to_blueprint
Change-Id: Ie754bf839c7121a3bd5a85a878c379021804e47c
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index 519bb6e..63d06a4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -55,6 +55,8 @@
 
     @Mock private lateinit var burnInInteractor: BurnInInteractor
     @Mock private lateinit var goneToAodTransitionViewModel: GoneToAodTransitionViewModel
+    @Mock
+    private lateinit var lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel
     @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var clockController: ClockController
 
     private val kosmos = testKosmos()
@@ -76,7 +78,12 @@
         kosmos.burnInInteractor = burnInInteractor
         whenever(goneToAodTransitionViewModel.enterFromTopTranslationY(anyInt()))
             .thenReturn(emptyFlow())
+        whenever(goneToAodTransitionViewModel.enterFromSideTranslationX(anyInt()))
+            .thenReturn(emptyFlow())
+        whenever(lockscreenToAodTransitionViewModel.enterFromSideTranslationX(anyInt()))
+            .thenReturn(emptyFlow())
         kosmos.goneToAodTransitionViewModel = goneToAodTransitionViewModel
+        kosmos.lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel
         kosmos.fakeKeyguardClockRepository.setCurrentClock(clockController)
 
         underTest = kosmos.aodBurnInViewModel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
similarity index 71%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
index 716c40d..bab466a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
@@ -28,6 +28,9 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.ui.StateToValue
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.powerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
 import com.android.systemui.testKosmos
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
@@ -47,10 +50,18 @@
     private val underTest = kosmos.goneToAodTransitionViewModel
     private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
     private val biometricSettingsRepository = kosmos.biometricSettingsRepository
+    private val powerRepository = kosmos.powerRepository
 
     @Test
-    fun enterFromTopTranslationY() =
+    fun enterFromTopTranslationY_whenNotOnFold() =
         testScope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.STARTING_TO_SLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.POWER_BUTTON,
+                powerButtonLaunchGestureTriggered = false
+            )
+
             val pixels = -100f
             val enterFromTopTranslationY by
                 collectLastValue(underTest.enterFromTopTranslationY(pixels.toInt()))
@@ -88,6 +99,80 @@
         }
 
     @Test
+    fun enterFromTopTranslationY_whenOnFold_emitsNothing() =
+        testScope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.STARTING_TO_SLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.FOLD,
+                powerButtonLaunchGestureTriggered = false
+            )
+
+            val pixels = -100f
+            val enterFromTopTranslationY by
+                collectLastValue(underTest.enterFromTopTranslationY(pixels.toInt()))
+            runCurrent()
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(enterFromTopTranslationY).isNull()
+
+            repository.sendTransitionStep(step(.55f))
+            assertThat(enterFromTopTranslationY).isNull()
+
+            repository.sendTransitionStep(step(.85f))
+            assertThat(enterFromTopTranslationY).isNull()
+
+            repository.sendTransitionStep(step(1f))
+            assertThat(enterFromTopTranslationY).isNull()
+        }
+
+    @Test
+    fun enterFromSideTranslationX_onFold() =
+        testScope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.STARTING_TO_SLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.FOLD,
+                powerButtonLaunchGestureTriggered = false
+            )
+
+            val pixels = -100f
+            val enterFromSideTranslationX by
+                collectLastValue(underTest.enterFromSideTranslationX(pixels.toInt()))
+            runCurrent()
+
+            // The animation should only start > .4f way through
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(enterFromSideTranslationX)
+                .isEqualTo(
+                    StateToValue(
+                        from = KeyguardState.GONE,
+                        to = KeyguardState.AOD,
+                        transitionState = TransitionState.STARTED,
+                        value = pixels
+                    )
+                )
+
+            repository.sendTransitionStep(step(.55f))
+            assertThat(enterFromSideTranslationX!!.value ?: -1f).isIn(Range.closed(pixels, 0f))
+
+            repository.sendTransitionStep(step(.85f))
+            assertThat(enterFromSideTranslationX!!.value ?: -1f).isIn(Range.closed(pixels, 0f))
+
+            // At the end, the translation should be complete and set to zero
+            repository.sendTransitionStep(step(1f))
+            assertThat(enterFromSideTranslationX)
+                .isEqualTo(
+                    StateToValue(
+                        from = KeyguardState.GONE,
+                        to = KeyguardState.AOD,
+                        transitionState = TransitionState.RUNNING,
+                        value = 0f
+                    )
+                )
+        }
+
+    @Test
     fun enterFromTopAnimationAlpha() =
         testScope.runTest {
             val enterFromTopAnimationAlpha by collectLastValue(underTest.enterFromTopAnimationAlpha)
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index cf91326..40bdc3e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -810,6 +810,8 @@
     <dimen name="keyguard_smartspace_top_offset">12dp</dimen>
     <!-- The amount to translate lockscreen elements on the GONE->AOD transition -->
     <dimen name="keyguard_enter_from_top_translation_y">-100dp</dimen>
+    <!-- The amount to translate lockscreen elements on the GONE->AOD transition, on device fold -->
+    <dimen name="keyguard_enter_from_side_translation_x">-100dp</dimen>
 
     <dimen name="notification_scrim_corner_radius">32dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index f30eef0..35a2d58 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason.FOLD
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.data.repository.ShadeRepository
@@ -368,7 +369,12 @@
                     // being delayed in KeyguardViewMediator
                     KeyguardState.DREAMING -> TO_DREAMING_DURATION + 100.milliseconds
                     KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
-                    KeyguardState.AOD -> TO_AOD_DURATION
+                    KeyguardState.AOD ->
+                        if (powerInteractor.detailedWakefulness.value.lastSleepReason == FOLD) {
+                            TO_AOD_FOLD_DURATION
+                        } else {
+                            TO_AOD_DURATION
+                        }
                     KeyguardState.DOZING -> TO_DOZING_DURATION
                     KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> TO_DREAMING_HOSTED_DURATION
                     KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION
@@ -385,6 +391,7 @@
         val TO_DREAMING_HOSTED_DURATION = 933.milliseconds
         val TO_OCCLUDED_DURATION = 450.milliseconds
         val TO_AOD_DURATION = 500.milliseconds
+        val TO_AOD_FOLD_DURATION = 1100.milliseconds
         val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
         val TO_GONE_DURATION = 633.milliseconds
         val TO_GLANCEABLE_HUB_DURATION = 1.seconds
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/ToAodFoldTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/ToAodFoldTransitionInteractor.kt
index 6729246..21b9e53 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/ToAodFoldTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/ToAodFoldTransitionInteractor.kt
@@ -16,30 +16,17 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
-import android.animation.ValueAnimator
 import android.view.ViewGroup
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.TransitionInfo
-import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.shade.NotificationPanelViewController
 import com.android.systemui.shade.ShadeFoldAnimator
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
 
 @SysUISingleton
 class ToAodFoldTransitionInteractor
 @Inject
 constructor(
     private val keyguardClockInteractor: KeyguardClockInteractor,
-    private val transitionInteractor: KeyguardTransitionInteractor,
-    private val transitionRepository: KeyguardTransitionRepository,
-    @Application private val mainScope: CoroutineScope,
-    @Main private val mainDispatcher: CoroutineDispatcher,
 ) {
     private var parentAnimator: NotificationPanelViewController.ShadeFoldAnimatorImpl? = null
 
@@ -50,7 +37,6 @@
                 get() = throw NotImplementedError("Deprecated. Do not call.")
 
             override fun prepareFoldToAodAnimation() {
-                forceToAod()
                 parentAnimator?.prepareFoldToAodAnimation()
             }
 
@@ -78,21 +64,6 @@
             parentAnimator as? NotificationPanelViewController.ShadeFoldAnimatorImpl?
     }
 
-    /** Forces the keyguard into AOD or Doze */
-    private fun forceToAod() {
-        mainScope.launch(mainDispatcher) {
-            transitionRepository.startTransition(
-                TransitionInfo(
-                    "$TAG (Fold transition triggered)",
-                    transitionInteractor.getCurrentState(),
-                    transitionInteractor.asleepKeyguardState.value,
-                    ValueAnimator().apply { duration = 0 },
-                    TransitionModeOnCanceled.LAST_VALUE,
-                )
-            )
-        }
-    }
-
     companion object {
         private val TAG = ToAodFoldTransitionInteractor::class.simpleName!!
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index d9a6d64..62b4782 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -55,6 +55,7 @@
     private val keyguardInteractor: KeyguardInteractor,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
+    private val lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
     private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
     private val occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
     private val keyguardClockViewModel: KeyguardClockViewModel,
@@ -74,13 +75,30 @@
                 burnInParams
             }
         return configurationInteractor
-            .dimensionPixelSize(R.dimen.keyguard_enter_from_top_translation_y)
-            .flatMapLatest { enterFromTopAmount ->
+            .dimensionPixelSize(
+                setOf(
+                    R.dimen.keyguard_enter_from_top_translation_y,
+                    R.dimen.keyguard_enter_from_side_translation_x,
+                )
+            )
+            .flatMapLatest { dimens ->
                 combine(
                     keyguardInteractor.keyguardTranslationY.onStart { emit(0f) },
                     burnIn(params).onStart { emit(BurnInModel()) },
                     goneToAodTransitionViewModel
-                        .enterFromTopTranslationY(enterFromTopAmount)
+                        .enterFromTopTranslationY(
+                            dimens[R.dimen.keyguard_enter_from_top_translation_y]!!
+                        )
+                        .onStart { emit(StateToValue()) },
+                    goneToAodTransitionViewModel
+                        .enterFromSideTranslationX(
+                            dimens[R.dimen.keyguard_enter_from_side_translation_x]!!
+                        )
+                        .onStart { emit(StateToValue()) },
+                    lockscreenToAodTransitionViewModel
+                        .enterFromSideTranslationX(
+                            dimens[R.dimen.keyguard_enter_from_side_translation_x]!!
+                        )
                         .onStart { emit(StateToValue()) },
                     occludedToLockscreenTransitionViewModel.lockscreenTranslationY.onStart {
                         emit(0f)
@@ -88,21 +106,31 @@
                     aodToLockscreenTransitionViewModel.translationY(params.translationY).onStart {
                         emit(StateToValue())
                     },
-                ) {
-                    keyguardTranslationY,
-                    burnInModel,
-                    goneToAod,
-                    occludedToLockscreen,
-                    aodToLockscreen ->
+                ) { flows ->
+                    val keyguardTranslationY = flows[0] as Float
+                    val burnInModel = flows[1] as BurnInModel
+                    val goneToAodTranslationY = flows[2] as StateToValue
+                    val goneToAodTranslationX = flows[3] as StateToValue
+                    val lockscreenToAodTranslationX = flows[4] as StateToValue
+                    val occludedToLockscreen = flows[5] as Float
+                    val aodToLockscreen = flows[6] as StateToValue
+
                     val translationY =
                         if (aodToLockscreen.transitionState.isTransitioning()) {
                             aodToLockscreen.value ?: 0f
-                        } else if (goneToAod.transitionState.isTransitioning()) {
-                            (goneToAod.value ?: 0f) + burnInModel.translationY
+                        } else if (goneToAodTranslationY.transitionState.isTransitioning()) {
+                            (goneToAodTranslationY.value ?: 0f) + burnInModel.translationY
                         } else {
                             burnInModel.translationY + occludedToLockscreen + keyguardTranslationY
                         }
-                    burnInModel.copy(translationY = translationY.toInt())
+                    val translationX =
+                        burnInModel.translationX +
+                            (goneToAodTranslationX.value ?: 0f) +
+                            (lockscreenToAodTranslationX.value ?: 0f)
+                    burnInModel.copy(
+                        translationX = translationX.toInt(),
+                        translationY = translationY.toInt(),
+                    )
                 }
             }
             .distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
index 74f7d75..2bc8e51 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -26,13 +26,17 @@
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.StateToValue
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason.FOLD
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.transform
 
 /** Breaks down GONE->AOD transition into discrete steps for corresponding views to consume. */
 @ExperimentalCoroutinesApi
@@ -41,6 +45,7 @@
 @Inject
 constructor(
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+    private val powerInteractor: PowerInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
 
@@ -56,13 +61,38 @@
 
     /** y-translation from the top of the screen for AOD */
     fun enterFromTopTranslationY(translatePx: Int): Flow<StateToValue> {
-        return transitionAnimation.sharedFlowWithState(
-            startTime = 600.milliseconds,
-            duration = 500.milliseconds,
-            onStep = { translatePx + it * -translatePx },
-            onFinish = { 0f },
-            interpolator = EMPHASIZED_DECELERATE,
-        )
+        return transitionAnimation
+            .sharedFlowWithState(
+                startTime = 600.milliseconds,
+                duration = 500.milliseconds,
+                onStep = { translatePx + it * -translatePx },
+                onFinish = { 0f },
+                interpolator = EMPHASIZED_DECELERATE,
+            )
+            .sample(powerInteractor.detailedWakefulness, ::Pair)
+            .transform { (stateToValue, wakefulness) ->
+                if (wakefulness.lastSleepReason != FOLD) {
+                    emit(stateToValue)
+                }
+            }
+    }
+
+    /** x-translation from the side of the screen for fold animation */
+    fun enterFromSideTranslationX(translatePx: Int): Flow<StateToValue> {
+        return transitionAnimation
+            .sharedFlowWithState(
+                startTime = 500.milliseconds,
+                duration = 600.milliseconds,
+                onStep = { translatePx + it * -translatePx },
+                onFinish = { 0f },
+                interpolator = EMPHASIZED_DECELERATE,
+            )
+            .sample(powerInteractor.detailedWakefulness, ::Pair)
+            .transform { (stateToValue, wakefulness) ->
+                if (wakefulness.lastSleepReason == FOLD) {
+                    emit(stateToValue)
+                }
+            }
     }
 
     val notificationAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index aefff7d..d7ac976 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -251,6 +251,7 @@
                         goneToDreamingTransitionViewModel.lockscreenAlpha,
                         goneToLockscreenTransitionViewModel.lockscreenAlpha,
                         lockscreenToAodTransitionViewModel.lockscreenAlpha(viewState),
+                        lockscreenToAodTransitionViewModel.lockscreenAlphaOnFold,
                         lockscreenToDozingTransitionViewModel.lockscreenAlpha,
                         lockscreenToDreamingTransitionViewModel.lockscreenAlpha,
                         lockscreenToGlanceableHubTransitionViewModel.keyguardAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
index 8b5b347..5408428 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import android.util.MathUtils
+import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
@@ -24,12 +25,17 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.StateToValue
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason.FOLD
+import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.transform
 
 /**
  * Breaks down LOCKSCREEN->AOD transition into discrete steps for corresponding views to consume.
@@ -40,6 +46,7 @@
 @Inject
 constructor(
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+    private val powerInteractor: PowerInteractor,
     shadeDependentFlows: ShadeDependentFlows,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
@@ -50,6 +57,12 @@
             edge = Edge.create(from = LOCKSCREEN, to = AOD),
         )
 
+    private val transitionAnimationOnFold =
+        animationFlow.setup(
+            duration = FromLockscreenTransitionInteractor.TO_AOD_FOLD_DURATION,
+            edge = Edge.create(from = LOCKSCREEN, to = AOD),
+        )
+
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
         shadeDependentFlows.transitionFlow(
             flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
@@ -71,11 +84,64 @@
 
     fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
         var startAlpha = 1f
-        return transitionAnimation.sharedFlow(
-            duration = 500.milliseconds,
-            onStart = { startAlpha = viewState.alpha() },
-            onStep = { MathUtils.lerp(startAlpha, 1f, it) },
-        )
+        return transitionAnimation
+            .sharedFlow(
+                duration = 500.milliseconds,
+                onStart = { startAlpha = viewState.alpha() },
+                onStep = { MathUtils.lerp(startAlpha, 1f, it) },
+            )
+            .sample(powerInteractor.detailedWakefulness, ::Pair)
+            .transform { (alpha, wakefulness) ->
+                if (wakefulness.lastSleepReason != FOLD) {
+                    emit(alpha)
+                }
+            }
+    }
+
+    val lockscreenAlphaOnFold: Flow<Float> =
+        transitionAnimationOnFold
+            .sharedFlow(
+                startTime = 600.milliseconds,
+                duration = 500.milliseconds,
+                onStep = { it },
+            )
+            .sample(powerInteractor.detailedWakefulness, ::Pair)
+            .transform { (alpha, wakefulness) ->
+                if (wakefulness.lastSleepReason == FOLD) {
+                    emit(alpha)
+                }
+            }
+
+    val notificationAlphaOnFold: Flow<Float> =
+        transitionAnimationOnFold
+            .sharedFlow(
+                duration = 1100.milliseconds,
+                onStep = { 0f },
+                onFinish = { 1f },
+            )
+            .sample(powerInteractor.detailedWakefulness, ::Pair)
+            .transform { (alpha, wakefulness) ->
+                if (wakefulness.lastSleepReason == FOLD) {
+                    emit(alpha)
+                }
+            }
+
+    /** x-translation from the side of the screen for fold animation */
+    fun enterFromSideTranslationX(translatePx: Int): Flow<StateToValue> {
+        return transitionAnimationOnFold
+            .sharedFlowWithState(
+                startTime = 600.milliseconds,
+                duration = 500.milliseconds,
+                onStep = { translatePx + it * -translatePx },
+                onFinish = { 0f },
+                interpolator = EMPHASIZED_DECELERATE,
+            )
+            .sample(powerInteractor.detailedWakefulness, ::Pair)
+            .transform { (stateToValue, wakefulness) ->
+                if (wakefulness.lastSleepReason == FOLD) {
+                    emit(stateToValue)
+                }
+            }
     }
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt
index 6f168d4..6cf668c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelKosmos.kt
@@ -33,6 +33,7 @@
         keyguardInteractor = keyguardInteractor,
         keyguardTransitionInteractor = keyguardTransitionInteractor,
         goneToAodTransitionViewModel = goneToAodTransitionViewModel,
+        lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel,
         aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
         occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
         keyguardClockViewModel = keyguardClockViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
index 19e4241..8549a30 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
@@ -22,11 +22,13 @@
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.power.domain.interactor.powerInteractor
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 var Kosmos.goneToAodTransitionViewModel by Fixture {
     GoneToAodTransitionViewModel(
         deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+        powerInteractor = powerInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
index 07b4cd4..f45e33b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
@@ -22,11 +22,13 @@
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.power.domain.interactor.powerInteractor
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-val Kosmos.lockscreenToAodTransitionViewModel by Fixture {
+var Kosmos.lockscreenToAodTransitionViewModel by Fixture {
     LockscreenToAodTransitionViewModel(
         deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+        powerInteractor = powerInteractor,
         shadeDependentFlows = shadeDependentFlows,
         animationFlow = keyguardTransitionAnimationFlow,
     )