Merge "Add data layer for the DeviceEntryIcon" into main
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 0e9f8b1..80fd516 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -294,8 +294,6 @@
         "tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt",
         "tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt",
         "tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt",
-        "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt",
-        "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt",
         "tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt",
         "tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt",
         // Keyguard helper
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
index 6082fb9..8ad32b4 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/FingerprintSensorType.kt
@@ -25,7 +25,11 @@
     UDFPS_ULTRASONIC,
     UDFPS_OPTICAL,
     POWER_BUTTON,
-    HOME_BUTTON
+    HOME_BUTTON;
+
+    fun isUdfps(): Boolean {
+        return (this == UDFPS_OPTICAL) || (this == UDFPS_ULTRASONIC)
+    }
 }
 
 /** Convert [this] to corresponding [FingerprintSensorType] */
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index c3f3529..298811b 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.scene.shared.model.SceneModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.collectLatest
@@ -46,6 +47,7 @@
  * Device entry occurs when the user successfully dismisses (or bypasses) the lockscreen, regardless
  * of the authentication method used.
  */
+@ExperimentalCoroutinesApi
 @SysUISingleton
 class DeviceEntryInteractor
 @Inject
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt
new file mode 100644
index 0000000..72b9da6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 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.deviceentry.domain.interactor
+
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/** Encapsulates business logic for device entry under-display fingerprint state changes. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class DeviceEntryUdfpsInteractor
+@Inject
+constructor(
+    // TODO (b/309655554): create & use interactors for these repositories
+    fingerprintPropertyRepository: FingerprintPropertyRepository,
+    fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+    biometricSettingsRepository: BiometricSettingsRepository,
+) {
+    /** Whether the device supports an under display fingerprint sensor. */
+    val isUdfpsSupported: Flow<Boolean> =
+        fingerprintPropertyRepository.sensorType.map { it.isUdfps() }
+
+    /** Whether the under-display fingerprint sensor is enrolled and enabled for device entry. */
+    val isUdfpsEnrolledAndEnabled: Flow<Boolean> =
+        combine(isUdfpsSupported, biometricSettingsRepository.isFingerprintEnrolledAndEnabled) {
+            udfps,
+            fpEnrolledAndEnabled ->
+            udfps && fpEnrolledAndEnabled
+        }
+    /** Whether the under display fingerprint sensor is currently running. */
+    val isListeningForUdfps =
+        isUdfpsSupported.flatMapLatest { isUdfps ->
+            if (isUdfps) {
+                fingerprintAuthRepository.isRunning
+            } else {
+                flowOf(false)
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 8b93b17..331d892 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -55,6 +55,7 @@
 import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger;
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl;
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransitionModule;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.navigationbar.NavigationModeController;
@@ -94,6 +95,7 @@
         KeyguardStatusViewComponent.class,
         KeyguardUserSwitcherComponent.class},
         includes = {
+            DeviceEntryIconTransitionModule.class,
             FalsingModule.class,
             KeyguardDataQuickAffordanceModule.class,
             KeyguardRepositoryModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
index 8bf2bc3..cc1cf91 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractor.kt
@@ -50,14 +50,18 @@
     private val configurationRepository: ConfigurationRepository,
     private val keyguardInteractor: KeyguardInteractor,
 ) {
-    val udfpsBurnInXOffset: StateFlow<Int> =
+    val deviceEntryIconXOffset: StateFlow<Int> =
         burnInOffsetDefinedInPixels(R.dimen.udfps_burn_in_offset_x, isXAxis = true)
-    val udfpsBurnInYOffset: StateFlow<Int> =
+    val deviceEntryIconYOffset: StateFlow<Int> =
         burnInOffsetDefinedInPixels(R.dimen.udfps_burn_in_offset_y, isXAxis = false)
-    val udfpsBurnInProgress: StateFlow<Float> =
+    val udfpsProgress: StateFlow<Float> =
         keyguardInteractor.dozeTimeTick
             .mapLatest { burnInHelperWrapper.burnInProgressOffset() }
-            .stateIn(scope, SharingStarted.Lazily, burnInHelperWrapper.burnInProgressOffset())
+            .stateIn(
+                scope,
+                SharingStarted.WhileSubscribed(),
+                burnInHelperWrapper.burnInProgressOffset()
+            )
 
     val keyguardBurnIn: Flow<BurnInModel> =
         combine(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index a331a66..8584401 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -113,7 +113,9 @@
     }
 
     companion object {
-        val TO_LOCKSCREEN_DURATION = 500.milliseconds
         private val DEFAULT_DURATION = 500.milliseconds
+        val TO_LOCKSCREEN_DURATION = 500.milliseconds
+        val TO_GONE_DURATION = DEFAULT_DURATION
+        val TO_OCCLUDED_DURATION = DEFAULT_DURATION
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index e9719e7..eca7088 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -26,11 +26,11 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.util.kotlin.Utils.Companion.toTriple
 import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
-import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
 
 @SysUISingleton
 class FromDozingTransitionInteractor
@@ -97,5 +97,6 @@
 
     companion object {
         private val DEFAULT_DURATION = 500.milliseconds
+        val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index eace0c7..bd73d60 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -138,6 +138,6 @@
     companion object {
         private val DEFAULT_DURATION = 500.milliseconds
         val TO_DREAMING_DURATION = 933.milliseconds
-        val TO_AOD_DURATION = 1100.milliseconds
+        val TO_AOD_DURATION = 1300.milliseconds
     }
 }
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 ea40ba0..152d217 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
@@ -391,5 +391,7 @@
         val TO_DREAMING_DURATION = 933.milliseconds
         val TO_OCCLUDED_DURATION = 450.milliseconds
         val TO_AOD_DURATION = 500.milliseconds
+        val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
+        val TO_GONE_DURATION = DEFAULT_DURATION
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index dec38b5..6a8555c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -142,9 +142,7 @@
                     ::toTriple
                 )
                 .collect { (isAsleep, lastStartedStep, isAodAvailable) ->
-                    if (
-                        lastStartedStep.to == KeyguardState.OCCLUDED && isAsleep
-                    ) {
+                    if (lastStartedStep.to == KeyguardState.OCCLUDED && isAsleep) {
                         startTransitionTo(
                             if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING
                         )
@@ -187,5 +185,6 @@
     companion object {
         private val DEFAULT_DURATION = 500.milliseconds
         val TO_LOCKSCREEN_DURATION = 933.milliseconds
+        val TO_AOD_DURATION = DEFAULT_DURATION
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 24b6661..5f246e1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -255,5 +255,7 @@
         private val DEFAULT_DURATION = 300.milliseconds
         val TO_GONE_DURATION = 500.milliseconds
         val TO_GONE_SHORT_DURATION = 200.milliseconds
+        val TO_AOD_DURATION = DEFAULT_DURATION
+        val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
index c0308e6..f5cd767 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
@@ -51,14 +51,14 @@
     val scaleForResolution = configRepo.scaleForResolution
 
     /** Burn-in offsets for the UDFPS view to mitigate burn-in on AOD. */
-    val burnInOffsets: Flow<BurnInOffsets> =
+    val burnInOffsets: Flow<Offsets> =
         combine(
             keyguardInteractor.dozeAmount,
-            burnInInteractor.udfpsBurnInXOffset,
-            burnInInteractor.udfpsBurnInYOffset,
-            burnInInteractor.udfpsBurnInProgress
+            burnInInteractor.deviceEntryIconXOffset,
+            burnInInteractor.deviceEntryIconYOffset,
+            burnInInteractor.udfpsProgress
         ) { dozeAmount, fullyDozingBurnInX, fullyDozingBurnInY, fullyDozingBurnInProgress ->
-            BurnInOffsets(
+            Offsets(
                 intEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInX),
                 intEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInY),
                 floatEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInProgress),
@@ -86,8 +86,8 @@
             .onStart { emit(0f) }
 }
 
-data class BurnInOffsets(
-    val burnInXOffset: Int, // current x burn in offset based on the aodTransitionAmount
-    val burnInYOffset: Int, // current y burn in offset based on the aodTransitionAmount
-    val burnInProgress: Float, // current progress based on the aodTransitionAmount
+data class Offsets(
+    val x: Int, // current x burn in offset based on the aodTransitionAmount
+    val y: Int, // current y burn in offset based on the aodTransitionAmount
+    val progress: Float, // current progress based on the aodTransitionAmount
 )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index 9d7477c..d5ad7ab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -105,4 +105,9 @@
             }
             .filterNotNull()
     }
+
+    /** Immediately (after 1ms) emits the given value for every step of the KeyguardTransition. */
+    fun immediatelyTransitionTo(value: Float): Flow<Float> {
+        return createFlow(duration = 1.milliseconds, onStep = { value }, onFinish = { value })
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index e82ea7f..a8b28bc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -24,6 +24,8 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.systemui.common.ui.view.LongPressHandlingView
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
 import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.FalsingManager
@@ -34,19 +36,24 @@
 object DeviceEntryIconViewBinder {
 
     /**
-     * Updates UI for the device entry icon view (lock, unlock and fingerprint icons) and its
-     * background.
+     * Updates UI for:
+     * - device entry containing view (parent view for the below views)
+     *     - long-press handling view (transparent, no UI)
+     *     - foreground icon view (lock/unlock/fingerprint)
+     *     - background view (optional)
      */
     @SuppressLint("ClickableViewAccessibility")
     @JvmStatic
     fun bind(
         view: DeviceEntryIconView,
         viewModel: DeviceEntryIconViewModel,
+        fgViewModel: DeviceEntryForegroundViewModel,
+        bgViewModel: DeviceEntryBackgroundViewModel,
         falsingManager: FalsingManager,
     ) {
-        val iconView = view.iconView
-        val bgView = view.bgView
         val longPressHandlingView = view.longPressHandlingView
+        val fgIconView = view.iconView
+        val bgView = view.bgView
         longPressHandlingView.listener =
             object : LongPressHandlingView.Listener {
                 override fun onLongPressDetected(view: View, x: Int, y: Int) {
@@ -56,37 +63,12 @@
                     viewModel.onLongPress()
                 }
             }
+
         view.repeatWhenAttached {
-            repeatOnLifecycle(Lifecycle.State.STARTED) {
-                launch {
-                    viewModel.iconViewModel.collect { iconViewModel ->
-                        iconView.setImageState(
-                            view.getIconState(iconViewModel.type, iconViewModel.useAodVariant),
-                            /* merge */ false
-                        )
-                        iconView.imageTintList = ColorStateList.valueOf(iconViewModel.tint)
-                        iconView.alpha = iconViewModel.alpha
-                        iconView.setPadding(
-                            iconViewModel.padding,
-                            iconViewModel.padding,
-                            iconViewModel.padding,
-                            iconViewModel.padding,
-                        )
-                    }
-                }
-                launch {
-                    viewModel.backgroundViewModel.collect { bgViewModel ->
-                        bgView.alpha = bgViewModel.alpha
-                        bgView.imageTintList = ColorStateList.valueOf(bgViewModel.tint)
-                    }
-                }
-                launch {
-                    viewModel.burnInViewModel.collect { burnInViewModel ->
-                        view.translationX = burnInViewModel.x.toFloat()
-                        view.translationY = burnInViewModel.y.toFloat()
-                        view.aodFpDrawable.progress = burnInViewModel.progress
-                    }
-                }
+            // Repeat on CREATED so that the view will always observe the entire
+            // GONE => AOD transition (even though the view may not be visible until the middle
+            // of the transition.
+            repeatOnLifecycle(Lifecycle.State.CREATED) {
                 launch {
                     viewModel.isLongPressEnabled.collect { isEnabled ->
                         longPressHandlingView.setLongPressHandlingEnabled(isEnabled)
@@ -97,6 +79,55 @@
                         view.accessibilityHintType = hint
                     }
                 }
+                launch {
+                    viewModel.useBackgroundProtection.collect { useBackgroundProtection ->
+                        if (useBackgroundProtection) {
+                            bgView.visibility = View.VISIBLE
+                        } else {
+                            bgView.visibility = View.GONE
+                        }
+                    }
+                }
+                launch {
+                    viewModel.burnInOffsets.collect { burnInOffsets ->
+                        view.translationX = burnInOffsets.x.toFloat()
+                        view.translationY = burnInOffsets.y.toFloat()
+                        view.aodFpDrawable.progress = burnInOffsets.progress
+                    }
+                }
+
+                launch { viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha } }
+            }
+        }
+
+        fgIconView.repeatWhenAttached {
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch {
+                    fgViewModel.viewModel.collect { viewModel ->
+                        fgIconView.setImageState(
+                            view.getIconState(viewModel.type, viewModel.useAodVariant),
+                            /* merge */ false
+                        )
+                        fgIconView.imageTintList = ColorStateList.valueOf(viewModel.tint)
+                        fgIconView.setPadding(
+                            viewModel.padding,
+                            viewModel.padding,
+                            viewModel.padding,
+                            viewModel.padding,
+                        )
+                    }
+                }
+            }
+        }
+
+        bgView.repeatWhenAttached {
+            repeatOnLifecycle(Lifecycle.State.CREATED) {
+                launch {
+                    bgViewModel.viewModel.collect { bgViewModel ->
+                        bgView.alpha = bgViewModel.alpha
+                        bgView.imageTintList = ColorStateList.valueOf(bgViewModel.tint)
+                    }
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
index 9872d97..52d87d3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
@@ -42,9 +42,9 @@
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 launch {
                     viewModel.burnInOffsets.collect { burnInOffsets ->
-                        view.progress = burnInOffsets.burnInProgress
-                        view.translationX = burnInOffsets.burnInXOffset.toFloat()
-                        view.translationY = burnInOffsets.burnInYOffset.toFloat()
+                        view.progress = burnInOffsets.progress
+                        view.translationX = burnInOffsets.x.toFloat()
+                        view.translationY = burnInOffsets.y.toFloat()
                     }
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
index bab04f2..d4621e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
@@ -59,8 +59,8 @@
 
                 launch {
                     viewModel.burnInOffsets.collect { burnInOffsets ->
-                        view.translationX = burnInOffsets.burnInXOffset.toFloat()
-                        view.translationY = burnInOffsets.burnInYOffset.toFloat()
+                        view.translationX = burnInOffsets.x.toFloat()
+                        view.translationY = burnInOffsets.y.toFloat()
                     }
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransition.kt
new file mode 100644
index 0000000..b58a80f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransition.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.transitions
+
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Each DeviceEntryIconTransition is responsible for updating the given parameters for the current
+ * keyguard transition.
+ * *
+ * MUST list implementing classes in dagger module [DeviceEntryIconTransitionModule].
+ */
+interface DeviceEntryIconTransition {
+    val deviceEntryParentViewAlpha: Flow<Float>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
new file mode 100644
index 0000000..9d557bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.transitions
+
+import com.android.systemui.keyguard.ui.viewmodel.AodToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AodToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DozingToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludedToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+@Module
+abstract class DeviceEntryIconTransitionModule {
+    @Binds
+    @IntoSet
+    abstract fun aodToLockscreen(
+        impl: AodToLockscreenTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun aodToGone(impl: AodToGoneTransitionViewModel): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun dozingToLockscreen(
+        impl: DozingToLockscreenTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun dreamingToLockscreen(
+        impl: DreamingToLockscreenTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun lockscreenToAod(
+        impl: LockscreenToAodTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun lockscreenToDreaming(
+        impl: LockscreenToDreamingTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun lockscreenToOccluded(
+        impl: LockscreenToOccludedTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun lockscreenToPrimaryBouncer(
+        impl: LockscreenToPrimaryBouncerTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun lockscreenToGone(
+        impl: LockscreenToGoneTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun goneToAod(impl: GoneToAodTransitionViewModel): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun occludedToAod(impl: OccludedToAodTransitionViewModel): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun occludedToLockscreen(
+        impl: OccludedToLockscreenTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun primaryBouncerToAod(
+        impl: PrimaryBouncerToAodTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun primaryBouncerToLockscreen(
+        impl: PrimaryBouncerToLockscreenTransitionViewModel
+    ): DeviceEntryIconTransition
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
index c9e3954..af1d0df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -40,7 +40,10 @@
     attrs: AttributeSet?,
     defStyleAttrs: Int = 0,
 ) : FrameLayout(context, attrs, defStyleAttrs) {
-    val longPressHandlingView: LongPressHandlingView = LongPressHandlingView(context, attrs)
+    val longPressHandlingView: LongPressHandlingView =
+        LongPressHandlingView(context, attrs) {
+            context.resources.getInteger(R.integer.config_lockIconLongPress).toLong()
+        }
     val iconView: ImageView = ImageView(context, attrs).apply { id = R.id.device_entry_icon_fg }
     val bgView: ImageView = ImageView(context, attrs).apply { id = R.id.device_entry_icon_bg }
     val aodFpDrawable: LottieDrawable = LottieDrawable()
@@ -105,7 +108,7 @@
         // FINGERPRINT
         animatedIconDrawable.addState(
             getIconState(IconType.FINGERPRINT, false),
-            context.getDrawable(R.drawable.ic_kg_fingerprint)!!,
+            context.getDrawable(R.drawable.ic_fingerprint)!!,
             R.id.locked_fp,
         )
 
@@ -220,7 +223,7 @@
         val lp = longPressHandlingView.layoutParams as LayoutParams
         lp.height = ViewGroup.LayoutParams.MATCH_PARENT
         lp.width = ViewGroup.LayoutParams.MATCH_PARENT
-        longPressHandlingView.setLayoutParams(lp)
+        longPressHandlingView.layoutParams = lp
     }
 
     private fun addIconImageView() {
@@ -231,7 +234,7 @@
         lp.height = ViewGroup.LayoutParams.MATCH_PARENT
         lp.width = ViewGroup.LayoutParams.MATCH_PARENT
         lp.gravity = Gravity.CENTER
-        iconView.setLayoutParams(lp)
+        iconView.layoutParams = lp
     }
 
     private fun addBgImageView() {
@@ -240,7 +243,7 @@
         val lp = bgView.layoutParams as LayoutParams
         lp.height = ViewGroup.LayoutParams.MATCH_PARENT
         lp.width = ViewGroup.LayoutParams.MATCH_PARENT
-        bgView.setLayoutParams(lp)
+        bgView.layoutParams = lp
     }
 
     fun getIconState(icon: IconType, aod: Boolean): IntArray {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
index ace970a..13ea8ff 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSection.kt
@@ -36,6 +36,8 @@
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
 import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
@@ -56,12 +58,15 @@
     private val featureFlags: FeatureFlags,
     private val lockIconViewController: Lazy<LockIconViewController>,
     private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>,
+    private val deviceEntryForegroundViewModel: Lazy<DeviceEntryForegroundViewModel>,
+    private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
     private val falsingManager: Lazy<FalsingManager>,
 ) : KeyguardSection() {
     private val deviceEntryIconViewId = R.id.device_entry_icon_view
 
     override fun addViews(constraintLayout: ConstraintLayout) {
-        if (!keyguardBottomAreaRefactor() &&
+        if (
+            !keyguardBottomAreaRefactor() &&
                 !featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)
         ) {
             return
@@ -87,6 +92,8 @@
                 DeviceEntryIconViewBinder.bind(
                     it,
                     deviceEntryIconViewModel.get(),
+                    deviceEntryForegroundViewModel.get(),
+                    deviceEntryBackgroundViewModel.get(),
                     falsingManager.get(),
                 )
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
new file mode 100644
index 0000000..4d2af0c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+/** Breaks down AOD->GONE transition into discrete steps for corresponding views to consume. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class AodToGoneTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+) : DeviceEntryIconTransition {
+
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = FromAodTransitionInteractor.TO_GONE_DURATION,
+            transitionFlow = interactor.transition(KeyguardState.AOD, KeyguardState.GONE),
+        )
+
+    override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
index 024707a..14de01b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -17,22 +17,29 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 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
 
 /**
  * Breaks down AOD->LOCKSCREEN transition into discrete steps for corresponding views to consume.
  */
+@ExperimentalCoroutinesApi
 @SysUISingleton
 class AodToLockscreenTransitionViewModel
 @Inject
 constructor(
-    private val interactor: KeyguardTransitionInteractor,
-) {
+    interactor: KeyguardTransitionInteractor,
+    deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
 
     private val transitionAnimation =
         KeyguardTransitionAnimationFlow(
@@ -47,4 +54,21 @@
             onStart = { 1f },
             onStep = { 1f },
         )
+
+    val deviceEntryBackgroundViewAlpha: Flow<Float> =
+        deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps ->
+            if (isUdfps) {
+                // fade in
+                transitionAnimation.createFlow(
+                    duration = 250.milliseconds,
+                    onStep = { it },
+                    onFinish = { 1f },
+                )
+            } else {
+                // background view isn't visible, so return an empty flow
+                emptyFlow()
+            }
+        }
+
+    override val deviceEntryParentViewAlpha: Flow<Float> = lockscreenAlpha
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
new file mode 100644
index 0000000..06661d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+
+/** Breaks down AOD->OCCLUDED transition into discrete steps for corresponding views to consume. */
+@SysUISingleton
+class AodToOccludedTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+) : DeviceEntryIconTransition {
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
+            transitionFlow = interactor.transition(KeyguardState.AOD, KeyguardState.OCCLUDED),
+        )
+
+    override val deviceEntryParentViewAlpha = transitionAnimation.immediatelyTransitionTo(0f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
new file mode 100644
index 0000000..3e8bbb3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import android.content.Context
+import com.android.settingslib.Utils
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
+
+/** Models the UI state for the device entry icon background view. */
+@ExperimentalCoroutinesApi
+class DeviceEntryBackgroundViewModel
+@Inject
+constructor(
+    val context: Context,
+    configurationRepository: ConfigurationRepository, // TODO (b/309655554): create & use interactor
+    lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
+    aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
+    goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
+    primaryBouncerToAodTransitionViewModel: PrimaryBouncerToAodTransitionViewModel,
+    occludedToAodTransitionViewModel: OccludedToAodTransitionViewModel,
+    occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
+    dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
+) {
+    private val color: Flow<Int> =
+        configurationRepository.onAnyConfigurationChange
+            .map {
+                Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorSurface)
+            }
+            .onStart {
+                emit(
+                    Utils.getColorAttrDefaultColor(
+                        context,
+                        com.android.internal.R.attr.colorSurface
+                    )
+                )
+            }
+    private val alpha: Flow<Float> =
+        setOf(
+                lockscreenToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
+                aodToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
+                goneToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
+                primaryBouncerToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
+                occludedToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
+                occludedToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
+                dreamingToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
+            )
+            .merge()
+
+    val viewModel: Flow<BackgroundViewModel> =
+        combine(color, alpha) { color, alpha ->
+            BackgroundViewModel(
+                alpha = alpha,
+                tint = color,
+            )
+        }
+
+    data class BackgroundViewModel(
+        val alpha: Float,
+        val tint: Int,
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
new file mode 100644
index 0000000..99529a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import android.content.Context
+import com.android.settingslib.Utils
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+
+/** Models the UI state for the device entry icon foreground view (displayed icon). */
+@ExperimentalCoroutinesApi
+class DeviceEntryForegroundViewModel
+@Inject
+constructor(
+    val context: Context,
+    configurationRepository: ConfigurationRepository, // TODO (b/309655554): create & use interactor
+    deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+    transitionInteractor: KeyguardTransitionInteractor,
+    deviceEntryIconViewModel: DeviceEntryIconViewModel,
+) {
+    private val isShowingAod: Flow<Boolean> =
+        transitionInteractor.startedKeyguardState.map { keyguardState ->
+            keyguardState == KeyguardState.AOD
+        }
+    private val color: Flow<Int> =
+        configurationRepository.onAnyConfigurationChange
+            .map { Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary) }
+            .onStart {
+                emit(Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary))
+            }
+    private val useAodIconVariant: Flow<Boolean> =
+        combine(isShowingAod, deviceEntryUdfpsInteractor.isUdfpsSupported) {
+                isTransitionToAod,
+                isUdfps ->
+                isTransitionToAod && isUdfps
+            }
+            .distinctUntilChanged()
+    private val padding: Flow<Int> =
+        configurationRepository.scaleForResolution.map { scale ->
+            (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
+                .roundToInt()
+        }
+
+    val viewModel: Flow<ForegroundIconViewModel> =
+        combine(
+            deviceEntryIconViewModel.iconType,
+            useAodIconVariant,
+            color,
+            padding,
+        ) { iconType, useAodVariant, color, padding ->
+            ForegroundIconViewModel(
+                type = iconType,
+                useAodVariant = useAodVariant,
+                tint = color,
+                padding = padding,
+            )
+        }
+
+    data class ForegroundIconViewModel(
+        val type: DeviceEntryIconView.IconType,
+        val useAodVariant: Boolean,
+        val tint: Int,
+        val padding: Int,
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index 842dde3..5b5a103 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -12,57 +12,202 @@
  * 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.keyguard.ui.viewmodel
 
-import android.graphics.Color
+import android.animation.FloatEvaluator
+import android.animation.IntEvaluator
+import com.android.keyguard.KeyguardViewController
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
 
+/** Models the UI state for the containing device entry icon & long-press handling view. */
 @ExperimentalCoroutinesApi
-class DeviceEntryIconViewModel @Inject constructor() {
-    // TODO: b/305234447 update these states from the data layer
-    val iconViewModel: Flow<IconViewModel> =
-        flowOf(
-            IconViewModel(
-                type = DeviceEntryIconView.IconType.LOCK,
-                useAodVariant = false,
-                tint = Color.WHITE,
-                alpha = 1f,
-                padding = 48,
+class DeviceEntryIconViewModel
+@Inject
+constructor(
+    transitions: Set<@JvmSuppressWildcards DeviceEntryIconTransition>,
+    burnInInteractor: BurnInInteractor,
+    shadeInteractor: ShadeInteractor,
+    deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+    transitionInteractor: KeyguardTransitionInteractor,
+    val keyguardInteractor: KeyguardInteractor,
+    val viewModel: AodToLockscreenTransitionViewModel,
+    val shadeDependentFlows: ShadeDependentFlows,
+    private val sceneContainerFlags: SceneContainerFlags,
+    private val keyguardViewController: Lazy<KeyguardViewController>,
+    private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
+    udfpsInteractor: DeviceEntryUdfpsInteractor,
+    private val deviceEntryInteractor: DeviceEntryInteractor,
+) {
+    private val intEvaluator = IntEvaluator()
+    private val floatEvaluator = FloatEvaluator()
+    private val toAodFromState: Flow<KeyguardState> =
+        transitionInteractor.transitionStepsToState(KeyguardState.AOD).map { it.from }
+    private val showingAlternateBouncer: Flow<Boolean> =
+        transitionInteractor.startedKeyguardState.map { keyguardState ->
+            keyguardState == KeyguardState.ALTERNATE_BOUNCER
+        }
+    private val qsProgress: Flow<Float> = shadeInteractor.qsExpansion.onStart { emit(0f) }
+    private val shadeExpansion: Flow<Float> = shadeInteractor.shadeExpansion.onStart { emit(0f) }
+    private val transitionAlpha: Flow<Float> =
+        transitions.map { it.deviceEntryParentViewAlpha }.merge()
+    private val alphaMultiplierFromShadeExpansion: Flow<Float> =
+        combine(
+            showingAlternateBouncer,
+            shadeExpansion,
+            qsProgress,
+        ) { showingAltBouncer, shadeExpansion, qsProgress ->
+            val interpolatedQsProgress = (qsProgress * 2).coerceIn(0f, 1f)
+            if (showingAltBouncer) {
+                1f
+            } else {
+                (1f - shadeExpansion) * (1f - interpolatedQsProgress)
+            }
+        }
+    // Burn-in offsets in AOD
+    private val nonAnimatedBurnInOffsets: Flow<BurnInOffsets> =
+        combine(
+            burnInInteractor.deviceEntryIconXOffset,
+            burnInInteractor.deviceEntryIconYOffset,
+            burnInInteractor.udfpsProgress
+        ) { fullyDozingBurnInX, fullyDozingBurnInY, fullyDozingBurnInProgress ->
+            BurnInOffsets(
+                fullyDozingBurnInX,
+                fullyDozingBurnInY,
+                fullyDozingBurnInProgress,
             )
-        )
-    val backgroundViewModel: Flow<BackgroundViewModel> =
-        flowOf(BackgroundViewModel(alpha = 1f, tint = Color.GRAY))
-    val burnInViewModel: Flow<BurnInViewModel> = flowOf(BurnInViewModel(0, 0, 0f))
-    val isLongPressEnabled: Flow<Boolean> = flowOf(true)
+        }
+    // Burn-in offsets that animate based on the transition amount to AOD
+    private val animatedBurnInOffsets: Flow<BurnInOffsets> =
+        combine(
+            nonAnimatedBurnInOffsets,
+            transitionInteractor.transitionStepsToState(KeyguardState.AOD)
+        ) { burnInOffsets, transitionStepsToAod ->
+            val dozeAmount = transitionStepsToAod.value
+            BurnInOffsets(
+                intEvaluator.evaluate(dozeAmount, 0, burnInOffsets.x),
+                intEvaluator.evaluate(dozeAmount, 0, burnInOffsets.y),
+                floatEvaluator.evaluate(dozeAmount, 0, burnInOffsets.progress)
+            )
+        }
+
+    val deviceEntryViewAlpha: Flow<Float> =
+        combine(
+            transitionAlpha,
+            alphaMultiplierFromShadeExpansion,
+        ) { alpha, alphaMultiplier ->
+            alpha * alphaMultiplier
+        }
+    val useBackgroundProtection: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported
+    val burnInOffsets: Flow<BurnInOffsets> =
+        deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolled ->
+            if (udfpsEnrolled) {
+                toAodFromState.flatMapLatest { fromState ->
+                    when (fromState) {
+                        KeyguardState.AOD,
+                        KeyguardState.GONE,
+                        KeyguardState.OCCLUDED,
+                        KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+                        KeyguardState.OFF,
+                        KeyguardState.DOZING,
+                        KeyguardState.DREAMING,
+                        KeyguardState.PRIMARY_BOUNCER -> nonAnimatedBurnInOffsets
+                        KeyguardState.ALTERNATE_BOUNCER -> animatedBurnInOffsets
+                        KeyguardState.LOCKSCREEN ->
+                            shadeDependentFlows.transitionFlow(
+                                flowWhenShadeIsExpanded = nonAnimatedBurnInOffsets,
+                                flowWhenShadeIsNotExpanded = animatedBurnInOffsets,
+                            )
+                    }
+                }
+            } else {
+                // If UDFPS isn't enrolled, we don't show any UI on AOD so there's no need
+                // to use burn in offsets at all
+                flowOf(BurnInOffsets(x = 0, y = 0, progress = 0f))
+            }
+        }
+    val iconType: Flow<DeviceEntryIconView.IconType> =
+        combine(
+            udfpsInteractor.isListeningForUdfps,
+            deviceEntryInteractor.isUnlocked,
+        ) { isListeningForUdfps, isUnlocked ->
+            if (isUnlocked) {
+                DeviceEntryIconView.IconType.UNLOCK
+            } else {
+                if (isListeningForUdfps) {
+                    DeviceEntryIconView.IconType.FINGERPRINT
+                } else {
+                    DeviceEntryIconView.IconType.LOCK
+                }
+            }
+        }
+    val isLongPressEnabled: Flow<Boolean> =
+        combine(
+            iconType,
+            deviceEntryUdfpsInteractor.isUdfpsSupported,
+        ) { deviceEntryStatus, isUdfps ->
+            when (deviceEntryStatus) {
+                DeviceEntryIconView.IconType.LOCK -> isUdfps
+                DeviceEntryIconView.IconType.UNLOCK -> true
+                DeviceEntryIconView.IconType.FINGERPRINT -> false
+            }
+        }
     val accessibilityDelegateHint: Flow<DeviceEntryIconView.AccessibilityHintType> =
-        flowOf(DeviceEntryIconView.AccessibilityHintType.NONE)
+        combine(iconType, isLongPressEnabled) { deviceEntryStatus, longPressEnabled ->
+            if (longPressEnabled) {
+                deviceEntryStatus.toAccessibilityHintType()
+            } else {
+                DeviceEntryIconView.AccessibilityHintType.NONE
+            }
+        }
 
     fun onLongPress() {
-        // TODO() vibrate & perform action based on current lock/unlock state
+        deviceEntryHapticsInteractor.vibrateSuccess()
+
+        // TODO (b/309804148): play auth ripple via an interactor
+
+        if (sceneContainerFlags.isEnabled()) {
+            deviceEntryInteractor.attemptDeviceEntry()
+        } else {
+            keyguardViewController.get().showPrimaryBouncer(/* scrim */ true)
+        }
     }
-    data class BurnInViewModel(
-        val x: Int, // current x burn in offset based on the aodTransitionAmount
-        val y: Int, // current y burn in offset based on the aodTransitionAmount
-        val progress: Float, // current progress based on the aodTransitionAmount
-    )
 
-    class IconViewModel(
-        val type: DeviceEntryIconView.IconType,
-        val useAodVariant: Boolean,
-        val tint: Int,
-        val alpha: Float,
-        val padding: Int,
-    )
-
-    class BackgroundViewModel(
-        val alpha: Float,
-        val tint: Int,
-    )
+    private fun DeviceEntryIconView.IconType.toAccessibilityHintType():
+        DeviceEntryIconView.AccessibilityHintType {
+        return when (this) {
+            DeviceEntryIconView.IconType.LOCK ->
+                DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE
+            DeviceEntryIconView.IconType.UNLOCK -> DeviceEntryIconView.AccessibilityHintType.ENTER
+            DeviceEntryIconView.IconType.FINGERPRINT ->
+                DeviceEntryIconView.AccessibilityHintType.NONE
+        }
+    }
 }
+
+data class BurnInOffsets(
+    val x: Int, // current x burn in offset based on the aodTransitionAmount
+    val y: Int, // current y burn in offset based on the aodTransitionAmount
+    val progress: Float, // current progress based on the aodTransitionAmount
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
new file mode 100644
index 0000000..27fb8a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down DOZING->LOCKSCREEN transition into discrete steps for corresponding views to consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class DozingToLockscreenTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+) : DeviceEntryIconTransition {
+    private val transitionAnimation: KeyguardTransitionAnimationFlow =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = FromDozingTransitionInteractor.TO_LOCKSCREEN_DURATION,
+            transitionFlow = interactor.dozingToLockscreenTransition,
+        )
+
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(1f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index e24d326..a3b8b85 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -18,27 +18,34 @@
 
 import com.android.app.animation.Interpolators.EMPHASIZED
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 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.filter
+import kotlinx.coroutines.flow.flatMapLatest
 
 /**
  * Breaks down DREAMING->LOCKSCREEN transition into discrete steps for corresponding views to
  * consume.
  */
+@ExperimentalCoroutinesApi
 @SysUISingleton
 class DreamingToLockscreenTransitionViewModel
 @Inject
 constructor(
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
-    private val fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
-) {
+    private val fromDreamingTransitionInteractor: FromDreamingTransitionInteractor,
+    private val deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
     fun startTransition() = fromDreamingTransitionInteractor.startToLockscreenTransition()
 
     private val transitionAnimation =
@@ -88,4 +95,15 @@
             duration = 250.milliseconds,
             onStep = { it },
         )
+
+    val deviceEntryBackgroundViewAlpha =
+        deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps ->
+            if (isUdfps) {
+                // immediately show; will fade in with deviceEntryParentViewAlpha
+                transitionAnimation.immediatelyTransitionTo(1f)
+            } else {
+                emptyFlow()
+            }
+        }
+    override val deviceEntryParentViewAlpha = lockscreenAlpha
 }
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 601dbcc..62b2281 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
@@ -18,20 +18,27 @@
 
 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.FromGoneTransitionInteractor.Companion.TO_AOD_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 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
 
 /** Breaks down GONE->AOD transition into discrete steps for corresponding views to consume. */
+@ExperimentalCoroutinesApi
 @SysUISingleton
 class GoneToAodTransitionViewModel
 @Inject
 constructor(
-    private val interactor: KeyguardTransitionInteractor,
-) {
+    interactor: KeyguardTransitionInteractor,
+    deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
 
     private val transitionAnimation =
         KeyguardTransitionAnimationFlow(
@@ -60,4 +67,21 @@
             onStart = { 0f },
             onStep = { it },
         )
+    val deviceEntryBackgroundViewAlpha: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0f)
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolled ->
+            if (udfpsEnrolled) {
+                // fade in at the end of the transition to give time for FP to start running
+                // and avoid a flicker of the unlocked icon
+                transitionAnimation.createFlow(
+                    startTime = 1100.milliseconds,
+                    duration = 200.milliseconds,
+                    onStep = { it },
+                    onFinish = { 1f },
+                )
+            } else {
+                emptyFlow()
+            }
+        }
 }
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
new file mode 100644
index 0000000..2bf12e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+
+/**
+ * Breaks down LOCKSCREEN->AOD transition into discrete steps for corresponding views to consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class LockscreenToAodTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+    deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+    shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition {
+
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = FromLockscreenTransitionInteractor.TO_AOD_DURATION,
+            transitionFlow = interactor.lockscreenToAodTransition,
+        )
+
+    val deviceEntryBackgroundViewAlpha: Flow<Float> =
+        shadeDependentFlows.transitionFlow(
+            flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
+            flowWhenShadeIsNotExpanded =
+                transitionAnimation.createFlow(
+                    duration = 300.milliseconds,
+                    onStep = { 1 - it },
+                    onFinish = { 0f },
+                ),
+        )
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
+            isUdfpsEnrolledAndEnabled ->
+            if (isUdfpsEnrolledAndEnabled) {
+                shadeDependentFlows.transitionFlow(
+                    flowWhenShadeIsExpanded = // fade in
+                    transitionAnimation.createFlow(
+                            duration = 300.milliseconds,
+                            onStep = { it },
+                            onFinish = { 1f },
+                        ),
+                    flowWhenShadeIsNotExpanded = transitionAnimation.immediatelyTransitionTo(1f),
+                )
+            } else {
+                shadeDependentFlows.transitionFlow(
+                    flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
+                    flowWhenShadeIsNotExpanded = // fade out
+                    transitionAnimation.createFlow(
+                            duration = 200.milliseconds,
+                            onStep = { 1f - it },
+                            onFinish = { 0f },
+                        ),
+                )
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
index a3ae67d..5229613 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.flow.Flow
@@ -34,7 +35,8 @@
 @Inject
 constructor(
     interactor: KeyguardTransitionInteractor,
-) {
+    shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition {
     private val transitionAnimation =
         KeyguardTransitionAnimationFlow(
             transitionDuration = TO_DREAMING_DURATION,
@@ -60,6 +62,12 @@
             onStep = { 1f - it },
         )
 
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        shadeDependentFlows.transitionFlow(
+            flowWhenShadeIsNotExpanded = lockscreenAlpha,
+            flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
+        )
+
     companion object {
         @JvmField val DREAMING_ANIMATION_DURATION_MS = TO_DREAMING_DURATION.inWholeMilliseconds
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
new file mode 100644
index 0000000..59e5aa8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down LOCKSCREEN->GONE transition into discrete steps for corresponding views to consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class LockscreenToGoneTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+) : DeviceEntryIconTransition {
+
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = FromLockscreenTransitionInteractor.TO_GONE_DURATION,
+            transitionFlow = interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.GONE),
+        )
+
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
index d3ea89c..d49bc49 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.flow.Flow
@@ -33,8 +34,9 @@
 class LockscreenToOccludedTransitionViewModel
 @Inject
 constructor(
-    private val interactor: KeyguardTransitionInteractor,
-) {
+    interactor: KeyguardTransitionInteractor,
+    shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition {
     private val transitionAnimation =
         KeyguardTransitionAnimationFlow(
             transitionDuration = TO_OCCLUDED_DURATION,
@@ -59,4 +61,10 @@
             interpolator = EMPHASIZED_ACCELERATE,
         )
     }
+
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        shadeDependentFlows.transitionFlow(
+            flowWhenShadeIsNotExpanded = lockscreenAlpha,
+            flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
new file mode 100644
index 0000000..f04b67a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down LOCKSCREEN->PRIMARY BOUNCER transition into discrete steps for corresponding views to
+ * consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class LockscreenToPrimaryBouncerTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+    shadeDependentFlows: ShadeDependentFlows,
+) : DeviceEntryIconTransition {
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+            transitionFlow =
+                interactor.transition(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER),
+        )
+
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        shadeDependentFlows.transitionFlow(
+            flowWhenShadeIsNotExpanded =
+                transitionAnimation.createFlow(
+                    duration = 250.milliseconds,
+                    onStep = { 1f - it },
+                    onFinish = { 0f }
+                ),
+            flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f)
+        )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
new file mode 100644
index 0000000..f7cff9b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+
+/** Breaks down OCCLUDED->AOD transition into discrete steps for corresponding views to consume. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class OccludedToAodTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+    deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = FromOccludedTransitionInteractor.TO_AOD_DURATION,
+            transitionFlow = interactor.transition(KeyguardState.OCCLUDED, KeyguardState.AOD),
+        )
+
+    val deviceEntryBackgroundViewAlpha: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0f)
+
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolledAndEnabled
+            ->
+            if (udfpsEnrolledAndEnabled) {
+                transitionAnimation.immediatelyTransitionTo(1f)
+            } else {
+                emptyFlow()
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index 6845c55..0bdc85d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -18,23 +18,30 @@
 
 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.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 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
 
 /**
  * Breaks down OCCLUDED->LOCKSCREEN transition into discrete steps for corresponding views to
  * consume.
  */
+@ExperimentalCoroutinesApi
 @SysUISingleton
 class OccludedToLockscreenTransitionViewModel
 @Inject
 constructor(
-    private val interactor: KeyguardTransitionInteractor,
-) {
+    interactor: KeyguardTransitionInteractor,
+    deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor
+) : DeviceEntryIconTransition {
     private val transitionAnimation =
         KeyguardTransitionAnimationFlow(
             transitionDuration = TO_LOCKSCREEN_DURATION,
@@ -58,4 +65,16 @@
             duration = 250.milliseconds,
             onStep = { it },
         )
+
+    val deviceEntryBackgroundViewAlpha: Flow<Float> =
+        deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
+            isUdfpsEnrolledAndEnabled ->
+            if (isUdfpsEnrolledAndEnabled) {
+                transitionAnimation.immediatelyTransitionTo(1f)
+            } else {
+                emptyFlow()
+            }
+        }
+
+    override val deviceEntryParentViewAlpha: Flow<Float> = lockscreenAlpha
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
new file mode 100644
index 0000000..05a6d58
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+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
+
+/**
+ * Breaks down PRIMARY BOUNCER->AOD transition into discrete steps for corresponding views to
+ * consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class PrimaryBouncerToAodTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+    deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
+            transitionFlow =
+                interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.AOD),
+        )
+
+    val deviceEntryBackgroundViewAlpha: Flow<Float> =
+        deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfpsEnrolledAndEnabled ->
+            if (isUdfpsEnrolledAndEnabled) {
+                transitionAnimation.immediatelyTransitionTo(0f)
+            } else {
+                emptyFlow()
+            }
+        }
+
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
+            isUdfpsEnrolledAndEnabled ->
+            if (isUdfpsEnrolledAndEnabled) {
+                transitionAnimation.createFlow(
+                    duration = 300.milliseconds,
+                    onStep = { it },
+                    onFinish = { 1f },
+                )
+            } else {
+                emptyFlow()
+            }
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
new file mode 100644
index 0000000..3cf793a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+
+/**
+ * Breaks down PRIMARY BOUNCER->LOCKSCREEN transition into discrete steps for corresponding views to
+ * consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class PrimaryBouncerToLockscreenTransitionViewModel
+@Inject
+constructor(
+    interactor: KeyguardTransitionInteractor,
+    deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+) : DeviceEntryIconTransition {
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
+            transitionFlow =
+                interactor.transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.LOCKSCREEN),
+        )
+
+    val deviceEntryBackgroundViewAlpha: Flow<Float> =
+        deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { isUdfps ->
+            if (isUdfps) {
+                transitionAnimation.immediatelyTransitionTo(1f)
+            } else {
+                emptyFlow()
+            }
+        }
+
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(1f)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt
new file mode 100644
index 0000000..e45d537
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+/** Helper for flows that depend on the shade expansion */
+class ShadeDependentFlows
+@Inject
+constructor(
+    transitionInteractor: KeyguardTransitionInteractor,
+    shadeInteractor: ShadeInteractor,
+) {
+    /** When the last keyguard state transition started, was the shade fully expanded? */
+    private val lastStartedTransitionHadShadeFullyExpanded: Flow<Boolean> =
+        transitionInteractor.startedKeyguardState.sample(shadeInteractor.isAnyFullyExpanded)
+
+    /**
+     * Decide which flow to use depending on the shade expansion state at the start of the last
+     * keyguard state transition.
+     */
+    fun <T> transitionFlow(
+        flowWhenShadeIsExpanded: Flow<T>,
+        flowWhenShadeIsNotExpanded: Flow<T>,
+    ): Flow<T> {
+        val filteredFlowWhenShadeIsExpanded =
+            flowWhenShadeIsExpanded
+                .sample(lastStartedTransitionHadShadeFullyExpanded, ::Pair)
+                .filter { (_, shadeFullyExpanded) -> shadeFullyExpanded }
+                .map { (valueWhenShadeIsExpanded, _) -> valueWhenShadeIsExpanded }
+        val filteredFlowWhenShadeIsNotExpanded =
+            flowWhenShadeIsNotExpanded
+                .sample(lastStartedTransitionHadShadeFullyExpanded, ::Pair)
+                .filter { (_, shadeFullyExpanded) -> !shadeFullyExpanded }
+                .map { (valueWhenShadeIsNotExpanded, _) -> valueWhenShadeIsNotExpanded }
+        return merge(filteredFlowWhenShadeIsExpanded, filteredFlowWhenShadeIsNotExpanded)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
index c10a463..6e77e13e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
@@ -17,9 +17,9 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import android.content.Context
-import com.android.systemui.res.R
-import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
+import com.android.systemui.keyguard.domain.interactor.Offsets
 import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import com.android.systemui.res.R
 import javax.inject.Inject
 import kotlin.math.roundToInt
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -35,7 +35,7 @@
     val context: Context,
 ) {
     val alpha: Flow<Float> = interactor.dozeAmount
-    val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
+    val burnInOffsets: Flow<Offsets> = interactor.burnInOffsets
     val isVisible: Flow<Boolean> = alpha.map { it != 0f }
 
     // Padding between the fingerprint icon and its bounding box in pixels.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
index 0b1079f..642904d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
@@ -19,9 +19,9 @@
 import android.content.Context
 import androidx.annotation.ColorInt
 import com.android.settingslib.Utils.getColorAttrDefaultColor
-import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.Offsets
 import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -185,7 +185,7 @@
         keyguardInteractor,
     ) {
     val dozeAmount: Flow<Float> = interactor.dozeAmount
-    val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
+    val burnInOffsets: Flow<Offsets> = interactor.burnInOffsets
 
     // Padding between the fingerprint icon and its bounding box in pixels.
     val padding: Flow<Int> =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractorTest.kt
new file mode 100644
index 0000000..e8eda80
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractorTest.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 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.deviceentry.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceEntryUdfpsInteractorTest : SysuiTestCase() {
+    private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+    private lateinit var fingerprintAuthRepository: FakeDeviceEntryFingerprintAuthRepository
+    private lateinit var biometricsRepository: FakeBiometricSettingsRepository
+
+    private lateinit var underTest: DeviceEntryUdfpsInteractor
+
+    @Before
+    fun setUp() {
+        fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+        fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+        biometricsRepository = FakeBiometricSettingsRepository()
+
+        underTest =
+            DeviceEntryUdfpsInteractor(
+                fingerprintPropertyRepository = fingerprintPropertyRepository,
+                fingerprintAuthRepository = fingerprintAuthRepository,
+                biometricSettingsRepository = biometricsRepository,
+            )
+    }
+
+    @Test
+    fun udfpsSupported_rearFp_false() = runTest {
+        val isUdfpsSupported by collectLastValue(underTest.isUdfpsSupported)
+        fingerprintPropertyRepository.supportsRearFps()
+        assertThat(isUdfpsSupported).isFalse()
+    }
+
+    @Test
+    fun udfpsSupoprted() = runTest {
+        val isUdfpsSupported by collectLastValue(underTest.isUdfpsSupported)
+        fingerprintPropertyRepository.supportsUdfps()
+        assertThat(isUdfpsSupported).isTrue()
+    }
+
+    @Test
+    fun udfpsEnrolledAndEnabled() = runTest {
+        val isUdfpsEnrolledAndEnabled by collectLastValue(underTest.isUdfpsEnrolledAndEnabled)
+        fingerprintPropertyRepository.supportsUdfps()
+        biometricsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+        assertThat(isUdfpsEnrolledAndEnabled).isTrue()
+    }
+
+    @Test
+    fun udfpsEnrolledAndEnabled_rearFp_false() = runTest {
+        val isUdfpsEnrolledAndEnabled by collectLastValue(underTest.isUdfpsEnrolledAndEnabled)
+        fingerprintPropertyRepository.supportsRearFps()
+        biometricsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+        assertThat(isUdfpsEnrolledAndEnabled).isFalse()
+    }
+
+    @Test
+    fun udfpsEnrolledAndEnabled_notEnrolledOrEnabled_false() = runTest {
+        val isUdfpsEnrolledAndEnabled by collectLastValue(underTest.isUdfpsEnrolledAndEnabled)
+        fingerprintPropertyRepository.supportsUdfps()
+        biometricsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+        assertThat(isUdfpsEnrolledAndEnabled).isFalse()
+    }
+
+    @Test
+    fun isListeningForUdfps() = runTest {
+        val isListeningForUdfps by collectLastValue(underTest.isListeningForUdfps)
+        fingerprintPropertyRepository.supportsUdfps()
+        fingerprintAuthRepository.setIsRunning(true)
+        assertThat(isListeningForUdfps).isTrue()
+    }
+
+    @Test
+    fun isListeningForUdfps_rearFp_false() = runTest {
+        val isListeningForUdfps by collectLastValue(underTest.isListeningForUdfps)
+        fingerprintPropertyRepository.supportsRearFps()
+        fingerprintAuthRepository.setIsRunning(true)
+        assertThat(isListeningForUdfps).isFalse()
+    }
+
+    @Test
+    fun isListeningForUdfps_notRunning_false() = runTest {
+        val isListeningForUdfps by collectLastValue(underTest.isListeningForUdfps)
+        fingerprintPropertyRepository.supportsUdfps()
+        fingerprintAuthRepository.setIsRunning(false)
+        assertThat(isListeningForUdfps).isFalse()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
index 5eab2fc..df52265 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BurnInInteractorTest.kt
@@ -84,8 +84,8 @@
     @Test
     fun udfpsBurnInOffset_updatesOnResolutionScaleChange() =
         testScope.runTest {
-            val udfpsBurnInOffsetX by collectLastValue(underTest.udfpsBurnInXOffset)
-            val udfpsBurnInOffsetY by collectLastValue(underTest.udfpsBurnInYOffset)
+            val udfpsBurnInOffsetX by collectLastValue(underTest.deviceEntryIconXOffset)
+            val udfpsBurnInOffsetY by collectLastValue(underTest.deviceEntryIconYOffset)
             assertThat(udfpsBurnInOffsetX).isEqualTo(burnInOffset)
             assertThat(udfpsBurnInOffsetY).isEqualTo(burnInOffset)
 
@@ -101,7 +101,7 @@
     @Test
     fun udfpsBurnInProgress_updatesOnDozeTimeTick() =
         testScope.runTest {
-            val udfpsBurnInProgress by collectLastValue(underTest.udfpsBurnInProgress)
+            val udfpsBurnInProgress by collectLastValue(underTest.udfpsProgress)
             assertThat(udfpsBurnInProgress).isEqualTo(burnInProgress)
 
             setBurnInProgress(.88f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index 3442df6..2dfc132 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -123,30 +123,30 @@
             runCurrent()
 
             // THEN burn in offsets are 0
-            assertThat(burnInOffsets?.burnInProgress).isEqualTo(0f)
-            assertThat(burnInOffsets?.burnInYOffset).isEqualTo(0)
-            assertThat(burnInOffsets?.burnInXOffset).isEqualTo(0)
+            assertThat(burnInOffsets?.progress).isEqualTo(0f)
+            assertThat(burnInOffsets?.y).isEqualTo(0)
+            assertThat(burnInOffsets?.x).isEqualTo(0)
 
             // WHEN we're in the middle of the doze amount change
             keyguardRepository.setDozeAmount(.50f)
             runCurrent()
 
             // THEN burn in is updated (between 0 and the full offset)
-            assertThat(burnInOffsets?.burnInProgress).isGreaterThan(0f)
-            assertThat(burnInOffsets?.burnInYOffset).isGreaterThan(0)
-            assertThat(burnInOffsets?.burnInXOffset).isGreaterThan(0)
-            assertThat(burnInOffsets?.burnInProgress).isLessThan(burnInProgress)
-            assertThat(burnInOffsets?.burnInYOffset).isLessThan(burnInYOffset)
-            assertThat(burnInOffsets?.burnInXOffset).isLessThan(burnInXOffset)
+            assertThat(burnInOffsets?.progress).isGreaterThan(0f)
+            assertThat(burnInOffsets?.y).isGreaterThan(0)
+            assertThat(burnInOffsets?.x).isGreaterThan(0)
+            assertThat(burnInOffsets?.progress).isLessThan(burnInProgress)
+            assertThat(burnInOffsets?.y).isLessThan(burnInYOffset)
+            assertThat(burnInOffsets?.x).isLessThan(burnInXOffset)
 
             // WHEN we're fully dozing
             keyguardRepository.setDozeAmount(1f)
             runCurrent()
 
             // THEN burn in offsets are updated to final current values (for the given time)
-            assertThat(burnInOffsets?.burnInProgress).isEqualTo(burnInProgress)
-            assertThat(burnInOffsets?.burnInYOffset).isEqualTo(burnInYOffset)
-            assertThat(burnInOffsets?.burnInXOffset).isEqualTo(burnInXOffset)
+            assertThat(burnInOffsets?.progress).isEqualTo(burnInProgress)
+            assertThat(burnInOffsets?.y).isEqualTo(burnInYOffset)
+            assertThat(burnInOffsets?.x).isEqualTo(burnInXOffset)
         }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
index 71313c8..75bdcdd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntryIconSectionTest.kt
@@ -24,12 +24,14 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.LockIconViewController
+import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
-import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
 import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
@@ -42,6 +44,7 @@
 import org.junit.runners.JUnit4
 import org.mockito.Answers
 import org.mockito.Mock
+import org.mockito.Mockito.mock
 import org.mockito.MockitoAnnotations
 
 @ExperimentalCoroutinesApi
@@ -77,7 +80,9 @@
                 notificationPanelView,
                 featureFlags,
                 { lockIconViewController },
-                { DeviceEntryIconViewModel() },
+                { mock(DeviceEntryIconViewModel::class.java) },
+                { mock(DeviceEntryForegroundViewModel::class.java) },
+                { mock(DeviceEntryBackgroundViewModel::class.java) },
                 { falsingManager },
             )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
new file mode 100644
index 0000000..f282481
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AodToGoneTransitionViewModelTest : SysuiTestCase() {
+    private lateinit var underTest: AodToGoneTransitionViewModel
+    private lateinit var repository: FakeKeyguardTransitionRepository
+
+    @Before
+    fun setUp() {
+        repository = FakeKeyguardTransitionRepository()
+        val interactor =
+            KeyguardTransitionInteractorFactory.create(
+                    scope = TestScope().backgroundScope,
+                    repository = repository,
+                )
+                .keyguardTransitionInteractor
+        underTest = AodToGoneTransitionViewModel(interactor)
+    }
+
+    @Test
+    fun deviceEntryParentViewHides() = runTest {
+        val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        repository.sendTransitionStep(step(0.1f))
+        repository.sendTransitionStep(step(0.3f))
+        repository.sendTransitionStep(step(0.4f))
+        repository.sendTransitionStep(step(0.5f))
+        repository.sendTransitionStep(step(0.6f))
+        repository.sendTransitionStep(step(0.8f))
+        repository.sendTransitionStep(step(1f))
+        deviceEntryParentViewAlpha.forEach { assertThat(it).isEqualTo(0f) }
+    }
+
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.AOD,
+            to = KeyguardState.GONE,
+            value = value,
+            transitionState = state,
+            ownerName = "AodToGoneTransitionViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
new file mode 100644
index 0000000..517149c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AodToLockscreenTransitionViewModelTest : SysuiTestCase() {
+    private lateinit var underTest: AodToLockscreenTransitionViewModel
+    private lateinit var repository: FakeKeyguardTransitionRepository
+    private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+
+    @Before
+    fun setUp() {
+        repository = FakeKeyguardTransitionRepository()
+        fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+        underTest =
+            AodToLockscreenTransitionViewModel(
+                interactor =
+                    KeyguardTransitionInteractorFactory.create(
+                            scope = TestScope().backgroundScope,
+                            repository = repository,
+                        )
+                        .keyguardTransitionInteractor,
+                deviceEntryUdfpsInteractor =
+                    DeviceEntryUdfpsInteractor(
+                        fingerprintPropertyRepository = fingerprintPropertyRepository,
+                        fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+                        biometricSettingsRepository = FakeBiometricSettingsRepository(),
+                    ),
+            )
+    }
+
+    @Test
+    fun deviceEntryParentViewShows() = runTest {
+        val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        repository.sendTransitionStep(step(0.1f))
+        repository.sendTransitionStep(step(0.3f))
+        repository.sendTransitionStep(step(0.5f))
+        repository.sendTransitionStep(step(0.6f))
+        repository.sendTransitionStep(step(1f))
+        deviceEntryParentViewAlpha.forEach { assertThat(it).isEqualTo(1f) }
+    }
+
+    @Test
+    fun deviceEntryBackgroundView_udfps_alphaFadeIn() = runTest {
+        fingerprintPropertyRepository.supportsUdfps()
+        val deviceEntryBackgroundViewAlpha by
+            collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+        // fade in
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+        repository.sendTransitionStep(step(0.1f))
+        assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(.2f)
+
+        repository.sendTransitionStep(step(0.3f))
+        assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(.6f)
+
+        repository.sendTransitionStep(step(0.6f))
+        assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(1f)
+
+        repository.sendTransitionStep(step(1f))
+        assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(1f)
+    }
+
+    @Test
+    fun deviceEntryBackgroundView_rearFp_noUpdates() = runTest {
+        fingerprintPropertyRepository.supportsRearFps()
+        val deviceEntryBackgroundViewAlpha by
+            collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+        // no updates
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertThat(deviceEntryBackgroundViewAlpha).isNull()
+        repository.sendTransitionStep(step(0.1f))
+        assertThat(deviceEntryBackgroundViewAlpha).isNull()
+        repository.sendTransitionStep(step(0.3f))
+        assertThat(deviceEntryBackgroundViewAlpha).isNull()
+        repository.sendTransitionStep(step(0.6f))
+        assertThat(deviceEntryBackgroundViewAlpha).isNull()
+        repository.sendTransitionStep(step(1f))
+        assertThat(deviceEntryBackgroundViewAlpha).isNull()
+    }
+
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.AOD,
+            to = KeyguardState.LOCKSCREEN,
+            value = value,
+            transitionState = state,
+            ownerName = "AodToLockscreenTransitionViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt
new file mode 100644
index 0000000..96f69462
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AodToOccludedTransitionViewModelTest : SysuiTestCase() {
+    private lateinit var underTest: AodToOccludedTransitionViewModel
+    private lateinit var repository: FakeKeyguardTransitionRepository
+
+    @Before
+    fun setUp() {
+        repository = FakeKeyguardTransitionRepository()
+        val interactor =
+            KeyguardTransitionInteractorFactory.create(
+                    scope = TestScope().backgroundScope,
+                    repository = repository,
+                )
+                .keyguardTransitionInteractor
+        underTest = AodToOccludedTransitionViewModel(interactor)
+    }
+
+    @Test
+    fun deviceEntryParentViewHides() = runTest {
+        val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        repository.sendTransitionStep(step(0.1f))
+        repository.sendTransitionStep(step(0.3f))
+        repository.sendTransitionStep(step(0.4f))
+        repository.sendTransitionStep(step(0.5f))
+        repository.sendTransitionStep(step(0.6f))
+        repository.sendTransitionStep(step(0.8f))
+        repository.sendTransitionStep(step(1f))
+        deviceEntryParentViewAlpha.forEach { Truth.assertThat(it).isEqualTo(0f) }
+    }
+
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.AOD,
+            to = KeyguardState.OCCLUDED,
+            value = value,
+            transitionState = state,
+            ownerName = "AodToOccludedTransitionViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
new file mode 100644
index 0000000..5dccc3b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DozingToLockscreenTransitionViewModelTest : SysuiTestCase() {
+    private lateinit var testScope: TestScope
+    private lateinit var underTest: DozingToLockscreenTransitionViewModel
+    private lateinit var repository: FakeKeyguardTransitionRepository
+
+    @Before
+    fun setUp() {
+        repository = FakeKeyguardTransitionRepository()
+        underTest =
+            DozingToLockscreenTransitionViewModel(
+                interactor =
+                    KeyguardTransitionInteractorFactory.create(
+                            scope = TestScope().backgroundScope,
+                            repository = repository,
+                        )
+                        .keyguardTransitionInteractor,
+            )
+    }
+
+    @Test
+    fun deviceEntryParentViewShows() = runTest {
+        val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        repository.sendTransitionStep(step(0.1f))
+        repository.sendTransitionStep(step(0.3f))
+        repository.sendTransitionStep(step(0.5f))
+        repository.sendTransitionStep(step(0.6f))
+        repository.sendTransitionStep(step(1f))
+        deviceEntryParentViewAlpha.forEach { assertThat(it).isEqualTo(1f) }
+    }
+
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.DOZING,
+            to = KeyguardState.LOCKSCREEN,
+            value = value,
+            transitionState = state,
+            ownerName = "DozingToLockscreenTransitionViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
index 6d47aed..fd125e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
@@ -19,6 +19,12 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
@@ -35,6 +41,7 @@
 import com.android.systemui.util.mockito.mock
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.test.TestScope
@@ -44,22 +51,34 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@ExperimentalCoroutinesApi
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
     private lateinit var underTest: DreamingToLockscreenTransitionViewModel
     private lateinit var repository: FakeKeyguardTransitionRepository
+    private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
 
     @Before
     fun setUp() {
         repository = FakeKeyguardTransitionRepository()
+        fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
         val interactor =
             KeyguardTransitionInteractorFactory.create(
                     scope = TestScope().backgroundScope,
                     repository = repository,
                 )
                 .keyguardTransitionInteractor
-        underTest = DreamingToLockscreenTransitionViewModel(interactor, mock())
+        underTest =
+            DreamingToLockscreenTransitionViewModel(
+                interactor,
+                mock(),
+                DeviceEntryUdfpsInteractor(
+                    fingerprintPropertyRepository = fingerprintPropertyRepository,
+                    fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+                    biometricSettingsRepository = FakeBiometricSettingsRepository(),
+                ),
+            )
     }
 
     @Test
@@ -129,6 +148,78 @@
         }
 
     @Test
+    fun deviceEntryParentViewFadeIn() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<Float>()
+
+            val job = underTest.deviceEntryParentViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+            repository.sendTransitionStep(step(0f, STARTED))
+            repository.sendTransitionStep(step(0f))
+            repository.sendTransitionStep(step(0.1f))
+            repository.sendTransitionStep(step(0.2f))
+            repository.sendTransitionStep(step(0.3f))
+            repository.sendTransitionStep(step(1f))
+
+            assertThat(values.size).isEqualTo(4)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+
+            job.cancel()
+        }
+
+    @Test
+    fun deviceEntryBackgroundViewAppear() =
+        runTest(UnconfinedTestDispatcher()) {
+            fingerprintPropertyRepository.setProperties(
+                sensorId = 0,
+                strength = SensorStrength.STRONG,
+                sensorType = FingerprintSensorType.UDFPS_OPTICAL,
+                sensorLocations = emptyMap(),
+            )
+            val values = mutableListOf<Float>()
+
+            val job =
+                underTest.deviceEntryBackgroundViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+            repository.sendTransitionStep(step(0f, STARTED))
+            repository.sendTransitionStep(step(0f))
+            repository.sendTransitionStep(step(0.1f))
+            repository.sendTransitionStep(step(0.2f))
+            repository.sendTransitionStep(step(0.3f))
+            repository.sendTransitionStep(step(1f))
+
+            values.forEach { assertThat(it).isEqualTo(1f) }
+
+            job.cancel()
+        }
+
+    @Test
+    fun deviceEntryBackground_noUdfps_noUpdates() =
+        runTest(UnconfinedTestDispatcher()) {
+            fingerprintPropertyRepository.setProperties(
+                sensorId = 0,
+                strength = SensorStrength.STRONG,
+                sensorType = FingerprintSensorType.REAR,
+                sensorLocations = emptyMap(),
+            )
+            val values = mutableListOf<Float>()
+
+            val job =
+                underTest.deviceEntryBackgroundViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+            repository.sendTransitionStep(step(0f, STARTED))
+            repository.sendTransitionStep(step(0f))
+            repository.sendTransitionStep(step(0.1f))
+            repository.sendTransitionStep(step(0.2f))
+            repository.sendTransitionStep(step(0.3f))
+            repository.sendTransitionStep(step(1f))
+
+            assertThat(values.size).isEqualTo(0) // no updates
+
+            job.cancel()
+        }
+
+    @Test
     fun lockscreenTranslationY() =
         runTest(UnconfinedTestDispatcher()) {
             val values = mutableListOf<Float>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
index 255f4df..c1444a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
@@ -19,7 +19,11 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -27,6 +31,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
@@ -34,11 +39,14 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@ExperimentalCoroutinesApi
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class GoneToAodTransitionViewModelTest : SysuiTestCase() {
     private lateinit var underTest: GoneToAodTransitionViewModel
     private lateinit var repository: FakeKeyguardTransitionRepository
+    private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+    private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
     private lateinit var testScope: TestScope
 
     @Before
@@ -47,13 +55,24 @@
         testScope = TestScope(testDispatcher)
 
         repository = FakeKeyguardTransitionRepository()
-        val interactor =
-            KeyguardTransitionInteractorFactory.create(
-                    scope = testScope.backgroundScope,
-                    repository = repository,
-                )
-                .keyguardTransitionInteractor
-        underTest = GoneToAodTransitionViewModel(interactor)
+        fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+        biometricSettingsRepository = FakeBiometricSettingsRepository()
+
+        underTest =
+            GoneToAodTransitionViewModel(
+                interactor =
+                    KeyguardTransitionInteractorFactory.create(
+                            scope = testScope.backgroundScope,
+                            repository = repository,
+                        )
+                        .keyguardTransitionInteractor,
+                deviceEntryUdfpsInteractor =
+                    DeviceEntryUdfpsInteractor(
+                        fingerprintPropertyRepository = fingerprintPropertyRepository,
+                        fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+                        biometricSettingsRepository = biometricSettingsRepository,
+                    ),
+            )
     }
 
     @Test
@@ -63,11 +82,11 @@
             val enterFromTopTranslationY by
                 collectLastValue(underTest.enterFromTopTranslationY(pixels.toInt()))
 
-            // The animation should only start > halfway through
+            // The animation should only start > .4f way through
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             assertThat(enterFromTopTranslationY).isEqualTo(pixels)
 
-            repository.sendTransitionStep(step(0.5f))
+            repository.sendTransitionStep(step(0.4f))
             assertThat(enterFromTopTranslationY).isEqualTo(pixels)
 
             repository.sendTransitionStep(step(.85f))
@@ -83,11 +102,11 @@
         testScope.runTest {
             val enterFromTopAnimationAlpha by collectLastValue(underTest.enterFromTopAnimationAlpha)
 
-            // The animation should only start > halfway through
+            // The animation should only start > .4f way through
             repository.sendTransitionStep(step(0f, TransitionState.STARTED))
             assertThat(enterFromTopAnimationAlpha).isEqualTo(0f)
 
-            repository.sendTransitionStep(step(0.5f))
+            repository.sendTransitionStep(step(0.4f))
             assertThat(enterFromTopAnimationAlpha).isEqualTo(0f)
 
             repository.sendTransitionStep(step(.85f))
@@ -97,6 +116,98 @@
             assertThat(enterFromTopAnimationAlpha).isEqualTo(1f)
         }
 
+    @Test
+    fun deviceEntryBackgroundViewAlpha() =
+        testScope.runTest {
+            val deviceEntryBackgroundViewAlpha by
+                collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+            // immediately 0f
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(0.4f))
+            assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(.85f))
+            assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(1f))
+            assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+        }
+
+    @Test
+    fun deviceEntryParentViewAlpha_udfpsEnrolled() =
+        testScope.runTest {
+            fingerprintPropertyRepository.supportsUdfps()
+            biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+            val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+            // animation doesn't start until the end
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(deviceEntryParentViewAlpha).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(0.5f))
+            assertThat(deviceEntryParentViewAlpha).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(.95f))
+            assertThat(deviceEntryParentViewAlpha).isIn(Range.closed(.01f, 1f))
+
+            repository.sendTransitionStep(step(1f))
+            assertThat(deviceEntryParentViewAlpha).isIn(Range.closed(.99f, 1f))
+
+            repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+        }
+
+    @Test
+    fun deviceEntryParentViewAlpha_rearFpEnrolled() =
+        testScope.runTest {
+            fingerprintPropertyRepository.supportsRearFps()
+            biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+            val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+            // animation doesn't start until the end
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(deviceEntryParentViewAlpha).isNull()
+
+            repository.sendTransitionStep(step(0.5f))
+            assertThat(deviceEntryParentViewAlpha).isNull()
+
+            repository.sendTransitionStep(step(.95f))
+            assertThat(deviceEntryParentViewAlpha).isNull()
+
+            repository.sendTransitionStep(step(1f))
+            assertThat(deviceEntryParentViewAlpha).isNull()
+
+            repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            assertThat(deviceEntryParentViewAlpha).isNull()
+        }
+
+    @Test
+    fun deviceEntryParentViewAlpha_udfpsNotEnrolled_noUpdates() =
+        testScope.runTest {
+            fingerprintPropertyRepository.supportsUdfps()
+            biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+            val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+            // animation doesn't start until the end
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(deviceEntryParentViewAlpha).isNull()
+
+            repository.sendTransitionStep(step(0.5f))
+            assertThat(deviceEntryParentViewAlpha).isNull()
+
+            repository.sendTransitionStep(step(.95f))
+            assertThat(deviceEntryParentViewAlpha).isNull()
+
+            repository.sendTransitionStep(step(1f))
+            assertThat(deviceEntryParentViewAlpha).isNull()
+
+            repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            assertThat(deviceEntryParentViewAlpha).isNull()
+        }
+
     private fun step(
         value: Float,
         state: TransitionState = TransitionState.RUNNING
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
new file mode 100644
index 0000000..4074851
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.SysUITestComponent
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.collectLastValue
+import com.android.collectValues
+import com.android.runCurrent
+import com.android.runTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
+import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockscreenToAodTransitionViewModelTest : SysuiTestCase() {
+    @SysUISingleton
+    @Component(
+        modules =
+            [
+                SysUITestModule::class,
+                UserDomainLayerModule::class,
+            ]
+    )
+    interface TestComponent : SysUITestComponent<LockscreenToAodTransitionViewModel> {
+        val repository: FakeKeyguardTransitionRepository
+        val deviceEntryRepository: FakeDeviceEntryRepository
+        val keyguardRepository: FakeKeyguardRepository
+        val shadeRepository: FakeShadeRepository
+        val fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+        val biometricSettingsRepository: FakeBiometricSettingsRepository
+
+        @Component.Factory
+        interface Factory {
+            fun create(
+                @BindsInstance test: SysuiTestCase,
+                featureFlags: FakeFeatureFlagsClassicModule,
+                mocks: TestMocksModule,
+            ): TestComponent
+        }
+
+        fun shadeExpanded(expanded: Boolean) {
+            if (expanded) {
+                shadeRepository.setQsExpansion(1f)
+            } else {
+                keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+                shadeRepository.setQsExpansion(0f)
+                shadeRepository.setLockscreenShadeExpansion(0f)
+            }
+        }
+    }
+
+    private val testComponent: TestComponent =
+        DaggerLockscreenToAodTransitionViewModelTest_TestComponent.factory()
+            .create(
+                test = this,
+                featureFlags =
+                    FakeFeatureFlagsClassicModule {
+                        set(FACE_AUTH_REFACTOR, true)
+                        set(FULL_SCREEN_USER_SWITCHER, true)
+                    },
+                mocks = TestMocksModule(),
+            )
+
+    @Test
+    fun backgroundViewAlpha_shadeNotExpanded() =
+        testComponent.runTest {
+            val actual by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+            shadeExpanded(false)
+            runCurrent()
+
+            // fade out
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(actual).isEqualTo(1f)
+
+            repository.sendTransitionStep(step(.3f))
+            assertThat(actual).isIn(Range.closed(.1f, .9f))
+
+            // finish fading out before the end of the full transition
+            repository.sendTransitionStep(step(.7f))
+            assertThat(actual).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            assertThat(actual).isEqualTo(0f)
+        }
+
+    @Test
+    fun backgroundViewAlpha_shadeExpanded() =
+        testComponent.runTest {
+            val actual by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+            shadeExpanded(true)
+            runCurrent()
+
+            // immediately 0f
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(actual).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(.3f))
+            assertThat(actual).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(.7f))
+            assertThat(actual).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            assertThat(actual).isEqualTo(0f)
+        }
+
+    @Test
+    fun deviceEntryParentViewAlpha_udfpsEnrolled_shadeNotExpanded() =
+        testComponent.runTest {
+            val values by collectValues(underTest.deviceEntryParentViewAlpha)
+            fingerprintPropertyRepository.supportsUdfps()
+            biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+            shadeExpanded(false)
+            runCurrent()
+
+            repository.sendTransitionSteps(
+                steps =
+                    listOf(
+                        step(0f, TransitionState.STARTED),
+                        step(.3f),
+                        step(.7f),
+                        step(1f),
+                    ),
+                testScope = testScope,
+            )
+            // immediately 1f
+            values.forEach { assertThat(it).isEqualTo(1f) }
+        }
+
+    @Test
+    fun deviceEntryParentViewAlpha_udfpsEnrolled_shadeExpanded() =
+        testComponent.runTest {
+            val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            fingerprintPropertyRepository.supportsUdfps()
+            biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+            shadeExpanded(true)
+            runCurrent()
+
+            // fade in
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(actual).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(.3f))
+            assertThat(actual).isIn(Range.closed(.1f, .9f))
+
+            // finish fading in before the end of the full transition
+            repository.sendTransitionStep(step(.7f))
+            assertThat(actual).isEqualTo(1f)
+
+            repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            assertThat(actual).isEqualTo(1f)
+        }
+
+    @Test
+    fun deviceEntryParentViewAlpha_rearFp_shadeNotExpanded() =
+        testComponent.runTest {
+            val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            fingerprintPropertyRepository.supportsRearFps()
+            shadeExpanded(false)
+            runCurrent()
+
+            // fade out
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(actual).isEqualTo(1f)
+
+            repository.sendTransitionStep(step(.1f))
+            assertThat(actual).isIn(Range.closed(.1f, .9f))
+
+            // finish fading out before the end of the full transition
+            repository.sendTransitionStep(step(.7f))
+            assertThat(actual).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            assertThat(actual).isEqualTo(0f)
+        }
+
+    @Test
+    fun deviceEntryParentViewAlpha_rearFp_shadeExpanded() =
+        testComponent.runTest {
+            val values by collectValues(underTest.deviceEntryParentViewAlpha)
+            fingerprintPropertyRepository.supportsRearFps()
+            shadeExpanded(true)
+            runCurrent()
+
+            repository.sendTransitionSteps(
+                steps =
+                    listOf(
+                        step(0f, TransitionState.STARTED),
+                        step(.3f),
+                        step(.7f),
+                        step(1f),
+                    ),
+                testScope = testScope,
+            )
+            // immediately 0f
+            values.forEach { assertThat(it).isEqualTo(0f) }
+        }
+
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.AOD,
+            value = value,
+            transitionState = state,
+            ownerName = "LockscreenToAodTransitionViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index 89a1d2b..5c85357 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -18,88 +18,172 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.SysUITestComponent
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.collectLastValue
+import com.android.collectValues
+import com.android.runCurrent
+import com.android.runTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import dagger.BindsInstance
+import dagger.Component
 import kotlinx.coroutines.test.runTest
-import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
-    private lateinit var underTest: LockscreenToDreamingTransitionViewModel
-    private lateinit var repository: FakeKeyguardTransitionRepository
+    @SysUISingleton
+    @Component(
+        modules =
+            [
+                SysUITestModule::class,
+                UserDomainLayerModule::class,
+            ]
+    )
+    interface TestComponent : SysUITestComponent<LockscreenToDreamingTransitionViewModel> {
+        val repository: FakeKeyguardTransitionRepository
+        val keyguardRepository: FakeKeyguardRepository
+        val shadeRepository: FakeShadeRepository
 
-    @Before
-    fun setUp() {
-        repository = FakeKeyguardTransitionRepository()
-        val interactor =
-            KeyguardTransitionInteractorFactory.create(
-                    scope = TestScope().backgroundScope,
-                    repository = repository,
-                )
-                .keyguardTransitionInteractor
-        underTest = LockscreenToDreamingTransitionViewModel(interactor)
+        @Component.Factory
+        interface Factory {
+            fun create(
+                @BindsInstance test: SysuiTestCase,
+                featureFlags: FakeFeatureFlagsClassicModule,
+                mocks: TestMocksModule,
+            ): TestComponent
+        }
+
+        fun shadeExpanded(expanded: Boolean) {
+            if (expanded) {
+                shadeRepository.setQsExpansion(1f)
+            } else {
+                keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+                shadeRepository.setQsExpansion(0f)
+                shadeRepository.setLockscreenShadeExpansion(0f)
+            }
+        }
     }
 
+    private val testComponent: TestComponent =
+        DaggerLockscreenToDreamingTransitionViewModelTest_TestComponent.factory()
+            .create(
+                test = this,
+                featureFlags =
+                    FakeFeatureFlagsClassicModule {
+                        set(Flags.FACE_AUTH_REFACTOR, true)
+                        set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+                    },
+                mocks = TestMocksModule(),
+            )
     @Test
     fun lockscreenFadeOut() =
-        runTest(UnconfinedTestDispatcher()) {
-            val values = mutableListOf<Float>()
-
-            val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
-
-            // Should start running here...
-            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
-            repository.sendTransitionStep(step(0f))
-            repository.sendTransitionStep(step(0.1f))
-            repository.sendTransitionStep(step(0.2f))
-            repository.sendTransitionStep(step(0.3f))
-            // ...up to here
-            repository.sendTransitionStep(step(1f))
+        testComponent.runTest {
+            val values by collectValues(underTest.lockscreenAlpha)
+            repository.sendTransitionSteps(
+                steps =
+                    listOf(
+                        step(0f, TransitionState.STARTED), // Should start running here...
+                        step(0f),
+                        step(.1f),
+                        step(.2f),
+                        step(.3f), // ...up to here
+                        step(1f),
+                    ),
+                testScope = testScope,
+            )
 
             // Only three values should be present, since the dream overlay runs for a small
             // fraction of the overall animation time
             assertThat(values.size).isEqualTo(5)
             values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
-
-            job.cancel()
         }
 
     @Test
     fun lockscreenTranslationY() =
-        runTest(UnconfinedTestDispatcher()) {
-            val values = mutableListOf<Float>()
-
+        testComponent.runTest {
             val pixels = 100
-            val job =
-                underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
+            val values by collectValues(underTest.lockscreenTranslationY(pixels))
 
-            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
-            repository.sendTransitionStep(step(0f))
-            repository.sendTransitionStep(step(0.3f))
-            repository.sendTransitionStep(step(0.5f))
-            repository.sendTransitionStep(step(1f))
-            // And a final reset event on FINISHED
-            repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            repository.sendTransitionSteps(
+                steps =
+                    listOf(
+                        step(0f, TransitionState.STARTED), // Should start running here...
+                        step(0f),
+                        step(.3f),
+                        step(.5f),
+                        step(1f),
+                        step(1f, TransitionState.FINISHED), // Final reset event on FINISHED
+                    ),
+                testScope = testScope,
+            )
 
             assertThat(values.size).isEqualTo(6)
             values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
             // Validate finished value
             assertThat(values[5]).isEqualTo(0f)
+        }
 
-            job.cancel()
+    @Test
+    fun deviceEntryParentViewAlpha_shadeExpanded() =
+        testComponent.runTest {
+            val values by collectValues(underTest.deviceEntryParentViewAlpha)
+            shadeExpanded(true)
+            runCurrent()
+
+            repository.sendTransitionSteps(
+                steps =
+                    listOf(
+                        step(0f, TransitionState.STARTED),
+                        step(0f),
+                        step(.3f),
+                        step(.5f),
+                        step(1f),
+                        step(1f, TransitionState.FINISHED),
+                    ),
+                testScope = testScope,
+            )
+
+            // immediately 0f
+            values.forEach { assertThat(it).isEqualTo(0f) }
+        }
+
+    @Test
+    fun deviceEntryParentViewAlpha_shadeNotExpanded() =
+        testComponent.runTest {
+            val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            shadeExpanded(false)
+            runCurrent()
+
+            // fade out
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(actual).isEqualTo(1f)
+
+            repository.sendTransitionStep(step(.1f))
+            assertThat(actual).isIn(Range.open(.1f, .9f))
+
+            // alpha is 1f before the full transition starts ending
+            repository.sendTransitionStep(step(0.8f))
+            assertThat(actual).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            assertThat(actual).isEqualTo(0f)
         }
 
     private fun step(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
new file mode 100644
index 0000000..1494c92
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockscreenToGoneTransitionViewModelTest : SysuiTestCase() {
+    private lateinit var underTest: LockscreenToGoneTransitionViewModel
+    private lateinit var repository: FakeKeyguardTransitionRepository
+
+    @Before
+    fun setUp() {
+        repository = FakeKeyguardTransitionRepository()
+        val interactor =
+            KeyguardTransitionInteractorFactory.create(
+                    scope = TestScope().backgroundScope,
+                    repository = repository,
+                )
+                .keyguardTransitionInteractor
+        underTest =
+            LockscreenToGoneTransitionViewModel(
+                interactor,
+            )
+    }
+
+    @Test
+    fun deviceEntryParentViewHides() = runTest {
+        val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        repository.sendTransitionStep(step(0.1f))
+        repository.sendTransitionStep(step(0.3f))
+        repository.sendTransitionStep(step(0.4f))
+        repository.sendTransitionStep(step(0.5f))
+        repository.sendTransitionStep(step(0.6f))
+        repository.sendTransitionStep(step(0.8f))
+        repository.sendTransitionStep(step(1f))
+        deviceEntryParentViewAlpha.forEach { assertThat(it).isEqualTo(0f) }
+    }
+
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.GONE,
+            value = value,
+            transitionState = state,
+            ownerName = "LockscreenToGoneTransitionViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index 41f8856..4cbefa3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -18,109 +18,185 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.SysUITestComponent
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.collectLastValue
+import com.android.collectValues
+import com.android.runCurrent
+import com.android.runTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
+import dagger.BindsInstance
+import dagger.Component
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
-    private lateinit var underTest: LockscreenToOccludedTransitionViewModel
-    private lateinit var repository: FakeKeyguardTransitionRepository
+    @SysUISingleton
+    @Component(
+        modules =
+            [
+                SysUITestModule::class,
+                UserDomainLayerModule::class,
+            ]
+    )
+    interface TestComponent : SysUITestComponent<LockscreenToOccludedTransitionViewModel> {
+        val repository: FakeKeyguardTransitionRepository
+        val keyguardRepository: FakeKeyguardRepository
+        val shadeRepository: FakeShadeRepository
 
-    @Before
-    fun setUp() {
-        repository = FakeKeyguardTransitionRepository()
-        val interactor =
-            KeyguardTransitionInteractorFactory.create(
-                    scope = TestScope().backgroundScope,
-                    repository = repository,
-                )
-                .keyguardTransitionInteractor
-        underTest = LockscreenToOccludedTransitionViewModel(interactor)
+        @Component.Factory
+        interface Factory {
+            fun create(
+                @BindsInstance test: SysuiTestCase,
+                featureFlags: FakeFeatureFlagsClassicModule,
+                mocks: TestMocksModule,
+            ): TestComponent
+        }
+
+        fun shadeExpanded(expanded: Boolean) {
+            if (expanded) {
+                shadeRepository.setQsExpansion(1f)
+            } else {
+                keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+                shadeRepository.setQsExpansion(0f)
+                shadeRepository.setLockscreenShadeExpansion(0f)
+            }
+        }
     }
 
+    private val testComponent: TestComponent =
+        DaggerLockscreenToOccludedTransitionViewModelTest_TestComponent.factory()
+            .create(
+                test = this,
+                featureFlags =
+                    FakeFeatureFlagsClassicModule {
+                        set(Flags.FACE_AUTH_REFACTOR, true)
+                        set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+                    },
+                mocks = TestMocksModule(),
+            )
+
     @Test
     fun lockscreenFadeOut() =
-        runTest(UnconfinedTestDispatcher()) {
-            val values = mutableListOf<Float>()
-
-            val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
-
-            // Should start running here...
-            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
-            repository.sendTransitionStep(step(0f))
-            repository.sendTransitionStep(step(0.1f))
-            repository.sendTransitionStep(step(0.4f))
-            repository.sendTransitionStep(step(0.7f))
-            // ...up to here
-            repository.sendTransitionStep(step(1f))
-
+        testComponent.runTest {
+            val values by collectValues(underTest.lockscreenAlpha)
+            repository.sendTransitionSteps(
+                steps =
+                    listOf(
+                        step(0f, TransitionState.STARTED), // Should start running here...
+                        step(0f),
+                        step(.1f),
+                        step(.4f),
+                        step(.7f), // ...up to here
+                        step(1f),
+                    ),
+                testScope = testScope,
+            )
             // Only 3 values should be present, since the dream overlay runs for a small fraction
             // of the overall animation time
             assertThat(values.size).isEqualTo(5)
             values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
-
-            job.cancel()
         }
 
     @Test
     fun lockscreenTranslationY() =
-        runTest(UnconfinedTestDispatcher()) {
-            val values = mutableListOf<Float>()
-
+        testComponent.runTest {
             val pixels = 100
-            val job =
-                underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
-
-            // Should start running here...
-            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
-            repository.sendTransitionStep(step(0f))
-            repository.sendTransitionStep(step(0.3f))
-            repository.sendTransitionStep(step(0.5f))
-            repository.sendTransitionStep(step(1f))
-            // ...up to here
-
+            val values by collectValues(underTest.lockscreenTranslationY(pixels))
+            repository.sendTransitionSteps(
+                steps =
+                    listOf(
+                        step(0f, TransitionState.STARTED), // Should start running here...
+                        step(0f),
+                        step(.3f),
+                        step(.5f),
+                        step(1f), // ...up to here
+                    ),
+                testScope = testScope,
+            )
             assertThat(values.size).isEqualTo(5)
             values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
-
-            job.cancel()
         }
 
     @Test
     fun lockscreenTranslationYIsCanceled() =
-        runTest(UnconfinedTestDispatcher()) {
-            val values = mutableListOf<Float>()
-
+        testComponent.runTest {
             val pixels = 100
-            val job =
-                underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
-
-            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
-            repository.sendTransitionStep(step(0f))
-            repository.sendTransitionStep(step(0.3f))
-            repository.sendTransitionStep(step(0.3f, TransitionState.CANCELED))
-
+            val values by collectValues(underTest.lockscreenTranslationY(pixels))
+            repository.sendTransitionSteps(
+                steps =
+                    listOf(
+                        step(0f, TransitionState.STARTED),
+                        step(0f),
+                        step(.3f),
+                        step(0.3f, TransitionState.CANCELED),
+                    ),
+                testScope = testScope,
+            )
             assertThat(values.size).isEqualTo(4)
             values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }
 
             // Cancel will reset the translation
             assertThat(values[3]).isEqualTo(0)
+        }
 
-            job.cancel()
+    @Test
+    fun deviceEntryParentViewAlpha_shadeExpanded() =
+        testComponent.runTest {
+            val values by collectValues(underTest.deviceEntryParentViewAlpha)
+            shadeExpanded(true)
+            runCurrent()
+
+            // immediately 0f
+            repository.sendTransitionSteps(
+                steps =
+                    listOf(
+                        step(0f, TransitionState.STARTED),
+                        step(.5f),
+                        step(1f, TransitionState.FINISHED)
+                    ),
+                testScope = testScope,
+            )
+
+            values.forEach { assertThat(it).isEqualTo(0f) }
+        }
+
+    @Test
+    fun deviceEntryParentViewAlpha_shadeNotExpanded() =
+        testComponent.runTest {
+            val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            shadeExpanded(false)
+            runCurrent()
+
+            // fade out
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(actual).isEqualTo(1f)
+
+            repository.sendTransitionStep(step(.2f))
+            assertThat(actual).isIn(Range.open(.1f, .9f))
+
+            // alpha is 1f before the full transition starts ending
+            repository.sendTransitionStep(step(0.8f))
+            assertThat(actual).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            assertThat(actual).isEqualTo(0f)
         }
 
     private fun step(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
new file mode 100644
index 0000000..4f56435
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.SysUITestComponent
+import com.android.SysUITestModule
+import com.android.TestMocksModule
+import com.android.collectLastValue
+import com.android.runCurrent
+import com.android.runTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.user.domain.UserDomainLayerModule
+import com.google.common.collect.Range
+import com.google.common.truth.Truth
+import dagger.BindsInstance
+import dagger.Component
+import kotlin.test.Test
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockscreenToPrimaryBouncerTransitionViewModelTest : SysuiTestCase() {
+    @SysUISingleton
+    @Component(
+        modules =
+            [
+                SysUITestModule::class,
+                UserDomainLayerModule::class,
+            ]
+    )
+    interface TestComponent : SysUITestComponent<LockscreenToPrimaryBouncerTransitionViewModel> {
+        val repository: FakeKeyguardTransitionRepository
+        val keyguardRepository: FakeKeyguardRepository
+        val shadeRepository: FakeShadeRepository
+
+        @Component.Factory
+        interface Factory {
+            fun create(
+                @BindsInstance test: SysuiTestCase,
+                featureFlags: FakeFeatureFlagsClassicModule,
+                mocks: TestMocksModule,
+            ): TestComponent
+        }
+
+        fun shadeExpanded(expanded: Boolean) {
+            if (expanded) {
+                shadeRepository.setQsExpansion(1f)
+            } else {
+                keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+                shadeRepository.setQsExpansion(0f)
+                shadeRepository.setLockscreenShadeExpansion(0f)
+            }
+        }
+    }
+
+    private val testComponent: TestComponent =
+        DaggerLockscreenToPrimaryBouncerTransitionViewModelTest_TestComponent.factory()
+            .create(
+                test = this,
+                featureFlags =
+                    FakeFeatureFlagsClassicModule {
+                        set(Flags.FACE_AUTH_REFACTOR, true)
+                        set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+                    },
+                mocks = TestMocksModule(),
+            )
+
+    @Test
+    fun deviceEntryParentViewAlpha_shadeExpanded() =
+        testComponent.runTest {
+            val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            shadeExpanded(true)
+            runCurrent()
+
+            // immediately 0f
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            runCurrent()
+            Truth.assertThat(actual).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(.2f))
+            runCurrent()
+            Truth.assertThat(actual).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(0.8f))
+            runCurrent()
+            Truth.assertThat(actual).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            runCurrent()
+            Truth.assertThat(actual).isEqualTo(0f)
+        }
+
+    @Test
+    fun deviceEntryParentViewAlpha_shadeNotExpanded() =
+        testComponent.runTest {
+            val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
+            shadeExpanded(false)
+            runCurrent()
+
+            // fade out
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            runCurrent()
+            Truth.assertThat(actual).isEqualTo(1f)
+
+            repository.sendTransitionStep(step(.1f))
+            runCurrent()
+            Truth.assertThat(actual).isIn(Range.open(.1f, .9f))
+
+            // alpha is 1f before the full transition starts ending
+            repository.sendTransitionStep(step(0.8f))
+            runCurrent()
+            Truth.assertThat(actual).isEqualTo(0f)
+
+            repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            runCurrent()
+            Truth.assertThat(actual).isEqualTo(0f)
+        }
+
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING,
+    ): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.LOCKSCREEN,
+            to = KeyguardState.PRIMARY_BOUNCER,
+            value = value,
+            transitionState = state,
+            ownerName = "LockscreenToPrimaryBouncerTransitionViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
new file mode 100644
index 0000000..0eb8ff6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class OccludedToAodTransitionViewModelTest : SysuiTestCase() {
+    private lateinit var underTest: OccludedToAodTransitionViewModel
+    private lateinit var repository: FakeKeyguardTransitionRepository
+    private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+    private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+
+    @Before
+    fun setUp() {
+        repository = FakeKeyguardTransitionRepository()
+        fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+        biometricSettingsRepository = FakeBiometricSettingsRepository()
+
+        underTest =
+            OccludedToAodTransitionViewModel(
+                KeyguardTransitionInteractorFactory.create(
+                        scope = TestScope().backgroundScope,
+                        repository = repository,
+                    )
+                    .keyguardTransitionInteractor,
+                DeviceEntryUdfpsInteractor(
+                    fingerprintPropertyRepository = fingerprintPropertyRepository,
+                    fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+                    biometricSettingsRepository = biometricSettingsRepository,
+                ),
+            )
+    }
+
+    @Test
+    fun deviceEntryBackgroundViewAlpha() = runTest {
+        val deviceEntryBackgroundViewAlpha by
+            collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+        // immediately 0f
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+        repository.sendTransitionStep(step(0.4f))
+        assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+        repository.sendTransitionStep(step(.85f))
+        assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+        repository.sendTransitionStep(step(1f))
+        assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+    }
+
+    @Test
+    fun deviceEntryParentViewAlpha_udfpsEnrolled() = runTest {
+        fingerprintPropertyRepository.supportsUdfps()
+        biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+        val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+        // immediately 1f
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+        repository.sendTransitionStep(step(0.5f))
+        assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+        repository.sendTransitionStep(step(.95f))
+        assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+        repository.sendTransitionStep(step(1f))
+        assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+        repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+        assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+    }
+
+    @Test
+    fun deviceEntryParentViewAlpha_rearFpEnrolled_noUpdates() = runTest {
+        fingerprintPropertyRepository.supportsRearFps()
+        biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+        val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+        // no updates
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(0.5f))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(.95f))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(1f))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+    }
+
+    @Test
+    fun deviceEntryParentViewAlpha_udfpsNotEnrolled_noUpdates() = runTest {
+        fingerprintPropertyRepository.supportsUdfps()
+        biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+        val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+        // no updates
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(0.5f))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(.95f))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(1f))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+    }
+
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.OCCLUDED,
+            to = KeyguardState.AOD,
+            value = value,
+            transitionState = state,
+            ownerName = "OccludedToAodTransitionViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index ec95cb8..d077227 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -19,6 +19,10 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -26,6 +30,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.test.TestScope
@@ -35,22 +40,35 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@ExperimentalCoroutinesApi
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() {
     private lateinit var underTest: OccludedToLockscreenTransitionViewModel
     private lateinit var repository: FakeKeyguardTransitionRepository
+    private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+    private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
 
     @Before
     fun setUp() {
         repository = FakeKeyguardTransitionRepository()
-        val interactor =
-            KeyguardTransitionInteractorFactory.create(
-                    scope = TestScope().backgroundScope,
-                    repository = repository,
-                )
-                .keyguardTransitionInteractor
-        underTest = OccludedToLockscreenTransitionViewModel(interactor)
+        fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+        biometricSettingsRepository = FakeBiometricSettingsRepository()
+        underTest =
+            OccludedToLockscreenTransitionViewModel(
+                interactor =
+                    KeyguardTransitionInteractorFactory.create(
+                            scope = TestScope().backgroundScope,
+                            repository = repository,
+                        )
+                        .keyguardTransitionInteractor,
+                deviceEntryUdfpsInteractor =
+                    DeviceEntryUdfpsInteractor(
+                        fingerprintPropertyRepository = fingerprintPropertyRepository,
+                        fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+                        biometricSettingsRepository = biometricSettingsRepository,
+                    ),
+            )
     }
 
     @Test
@@ -113,6 +131,78 @@
             job.cancel()
         }
 
+    @Test
+    fun deviceEntryParentViewFadeIn() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<Float>()
+
+            val job = underTest.deviceEntryParentViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(0.1f))
+            // Should start running here...
+            repository.sendTransitionStep(step(0.3f))
+            repository.sendTransitionStep(step(0.4f))
+            repository.sendTransitionStep(step(0.5f))
+            repository.sendTransitionStep(step(0.6f))
+            // ...up to here
+            repository.sendTransitionStep(step(0.8f))
+            repository.sendTransitionStep(step(1f))
+
+            assertThat(values.size).isEqualTo(5)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+
+            job.cancel()
+        }
+
+    @Test
+    fun deviceEntryBackgroundViewShows() =
+        runTest(UnconfinedTestDispatcher()) {
+            fingerprintPropertyRepository.supportsUdfps()
+            biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+            val values = mutableListOf<Float>()
+
+            val job =
+                underTest.deviceEntryBackgroundViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(0.1f))
+            repository.sendTransitionStep(step(0.3f))
+            repository.sendTransitionStep(step(0.4f))
+            repository.sendTransitionStep(step(0.5f))
+            repository.sendTransitionStep(step(0.6f))
+            repository.sendTransitionStep(step(0.8f))
+            repository.sendTransitionStep(step(1f))
+
+            values.forEach { assertThat(it).isEqualTo(1f) }
+
+            job.cancel()
+        }
+
+    @Test
+    fun deviceEntryBackgroundView_noUdfpsEnrolled_noUpdates() =
+        runTest(UnconfinedTestDispatcher()) {
+            fingerprintPropertyRepository.supportsRearFps()
+            biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+            val values = mutableListOf<Float>()
+
+            val job =
+                underTest.deviceEntryBackgroundViewAlpha.onEach { values.add(it) }.launchIn(this)
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(0.1f))
+            repository.sendTransitionStep(step(0.3f))
+            repository.sendTransitionStep(step(0.4f))
+            repository.sendTransitionStep(step(0.5f))
+            repository.sendTransitionStep(step(0.6f))
+            repository.sendTransitionStep(step(0.8f))
+            repository.sendTransitionStep(step(1f))
+
+            assertThat(values).isEmpty() // no updates
+
+            job.cancel()
+        }
+
     private fun step(
         value: Float,
         state: TransitionState = TransitionState.RUNNING
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
new file mode 100644
index 0000000..350b310
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrimaryBouncerToAodTransitionViewModelTest : SysuiTestCase() {
+    private lateinit var underTest: PrimaryBouncerToAodTransitionViewModel
+    private lateinit var repository: FakeKeyguardTransitionRepository
+    private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+    private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+
+    @Before
+    fun setUp() {
+        repository = FakeKeyguardTransitionRepository()
+        fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+        biometricSettingsRepository = FakeBiometricSettingsRepository()
+        val interactor =
+            KeyguardTransitionInteractorFactory.create(
+                    scope = TestScope().backgroundScope,
+                    repository = repository,
+                )
+                .keyguardTransitionInteractor
+        underTest =
+            PrimaryBouncerToAodTransitionViewModel(
+                interactor,
+                DeviceEntryUdfpsInteractor(
+                    fingerprintPropertyRepository = fingerprintPropertyRepository,
+                    fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+                    biometricSettingsRepository = biometricSettingsRepository,
+                ),
+            )
+    }
+
+    @Test
+    fun deviceEntryBackgroundViewAlpha() = runTest {
+        fingerprintPropertyRepository.supportsUdfps()
+        val deviceEntryBackgroundViewAlpha by
+            collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+        // immediately 0f
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+        repository.sendTransitionStep(step(0.4f))
+        assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+        repository.sendTransitionStep(step(.85f))
+        assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+
+        repository.sendTransitionStep(step(1f))
+        assertThat(deviceEntryBackgroundViewAlpha).isEqualTo(0f)
+    }
+
+    @Test
+    fun deviceEntryParentViewAlpha_udfpsEnrolled_fadeIn() = runTest {
+        fingerprintPropertyRepository.supportsUdfps()
+        biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+        val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+
+        repository.sendTransitionStep(step(0.5f))
+        repository.sendTransitionStep(step(.75f))
+        repository.sendTransitionStep(step(1f))
+
+        repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+        assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+    }
+
+    @Test
+    fun deviceEntryParentViewAlpha_rearFpEnrolled_noUpdates() = runTest {
+        fingerprintPropertyRepository.supportsRearFps()
+        biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+        val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+        // animation doesn't start until the end
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(0.5f))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(.95f))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(1f))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+    }
+
+    @Test
+    fun deviceEntryParentViewAlpha_udfpsNotEnrolled_noUpdates() = runTest {
+        fingerprintPropertyRepository.supportsUdfps()
+        biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+        val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(0.5f))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(.75f))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(1f))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+        assertThat(deviceEntryParentViewAlpha).isNull()
+    }
+
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.PRIMARY_BOUNCER,
+            to = KeyguardState.AOD,
+            value = value,
+            transitionState = state,
+            ownerName = "PrimaryBouncerToAodTransitionViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
new file mode 100644
index 0000000..24e4920
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2023 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrimaryBouncerToLockscreenTransitionViewModelTest : SysuiTestCase() {
+    private lateinit var underTest: PrimaryBouncerToLockscreenTransitionViewModel
+    private lateinit var repository: FakeKeyguardTransitionRepository
+    private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+    private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+
+    @Before
+    fun setUp() {
+        repository = FakeKeyguardTransitionRepository()
+        fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+        biometricSettingsRepository = FakeBiometricSettingsRepository()
+
+        underTest =
+            PrimaryBouncerToLockscreenTransitionViewModel(
+                KeyguardTransitionInteractorFactory.create(
+                        scope = TestScope().backgroundScope,
+                        repository = repository,
+                    )
+                    .keyguardTransitionInteractor,
+                DeviceEntryUdfpsInteractor(
+                    fingerprintPropertyRepository = fingerprintPropertyRepository,
+                    fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
+                    biometricSettingsRepository = biometricSettingsRepository,
+                ),
+            )
+    }
+
+    @Test
+    fun deviceEntryParentViewAlpha() = runTest {
+        val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
+
+        // immediately 1f
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+        repository.sendTransitionStep(step(0.4f))
+        assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+        repository.sendTransitionStep(step(.85f))
+        assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+
+        repository.sendTransitionStep(step(1f))
+        assertThat(deviceEntryParentViewAlpha).isEqualTo(1f)
+    }
+
+    @Test
+    fun deviceEntryBackgroundViewAlpha_udfpsEnrolled_show() = runTest {
+        fingerprintPropertyRepository.supportsUdfps()
+        val bgViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+
+        // immediately 1f
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertThat(bgViewAlpha).isEqualTo(1f)
+
+        repository.sendTransitionStep(step(0.1f))
+        assertThat(bgViewAlpha).isEqualTo(1f)
+
+        repository.sendTransitionStep(step(.3f))
+        assertThat(bgViewAlpha).isEqualTo(1f)
+
+        repository.sendTransitionStep(step(.5f))
+        assertThat(bgViewAlpha).isEqualTo(1f)
+
+        repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+        assertThat(bgViewAlpha).isEqualTo(1f)
+    }
+
+    @Test
+    fun deviceEntryBackgroundViewAlpha_rearFpEnrolled_noUpdates() = runTest {
+        fingerprintPropertyRepository.supportsRearFps()
+        val bgViewAlpha by collectLastValue(underTest.deviceEntryBackgroundViewAlpha)
+        repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+        assertThat(bgViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(0.5f))
+        assertThat(bgViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(.75f))
+        assertThat(bgViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(1f))
+        assertThat(bgViewAlpha).isNull()
+
+        repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+        assertThat(bgViewAlpha).isNull()
+    }
+
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.PRIMARY_BOUNCER,
+            to = KeyguardState.LOCKSCREEN,
+            value = value,
+            transitionState = state,
+            ownerName = "PrimaryBouncerToLockscreenTransitionViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt
index 0c5e438..005cac4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeFingerprintPropertyRepository.kt
@@ -19,10 +19,15 @@
 import android.hardware.biometrics.SensorLocationInternal
 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
 import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
 
-class FakeFingerprintPropertyRepository : FingerprintPropertyRepository {
+@SysUISingleton
+class FakeFingerprintPropertyRepository @Inject constructor() : FingerprintPropertyRepository {
 
     private val _sensorId: MutableStateFlow<Int> = MutableStateFlow(-1)
     override val sensorId = _sensorId.asStateFlow()
@@ -50,4 +55,29 @@
         _sensorType.value = sensorType
         _sensorLocations.value = sensorLocations
     }
+
+    /** setProperties as if the device supports UDFPS_OPTICAL. */
+    fun supportsUdfps() {
+        setProperties(
+            sensorId = 0,
+            strength = SensorStrength.STRONG,
+            sensorType = FingerprintSensorType.UDFPS_OPTICAL,
+            sensorLocations = emptyMap(),
+        )
+    }
+
+    /** setProperties as if the device supports the rear fingerprint sensor. */
+    fun supportsRearFps() {
+        setProperties(
+            sensorId = 0,
+            strength = SensorStrength.STRONG,
+            sensorType = FingerprintSensorType.REAR,
+            sensorLocations = emptyMap(),
+        )
+    }
+}
+
+@Module
+interface FakeFingerprintPropertyRepositoryModule {
+    @Binds fun bindFake(fake: FakeFingerprintPropertyRepository): FingerprintPropertyRepository
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
index 44286b7..8ff04a63 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/FakeDeviceEntryDataLayerModule.kt
@@ -15,17 +15,23 @@
 
 package com.android.systemui.deviceentry.data
 
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepositoryModule
 import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepositoryModule
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepositoryModule
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepositoryModule
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepositoryModule
 import com.android.systemui.keyguard.data.repository.FakeTrustRepositoryModule
 import dagger.Module
 
 @Module(
     includes =
         [
+            FakeBiometricSettingsRepositoryModule::class,
             FakeDeviceEntryRepositoryModule::class,
-            FakeTrustRepositoryModule::class,
             FakeDeviceEntryFaceAuthRepositoryModule::class,
+            FakeDeviceEntryFingerprintAuthRepositoryModule::class,
+            FakeFingerprintPropertyRepositoryModule::class,
+            FakeTrustRepositoryModule::class,
         ]
 )
 object FakeDeviceEntryDataLayerModule
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
index 85261123..df31a12 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
@@ -18,13 +18,18 @@
 package com.android.systemui.keyguard.data.repository
 
 import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.shared.model.AuthenticationFlags
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.map
 
-class FakeBiometricSettingsRepository : BiometricSettingsRepository {
+@SysUISingleton
+class FakeBiometricSettingsRepository @Inject constructor() : BiometricSettingsRepository {
     private val _isFingerprintEnrolledAndEnabled = MutableStateFlow(false)
     override val isFingerprintEnrolledAndEnabled: StateFlow<Boolean>
         get() = _isFingerprintEnrolledAndEnabled
@@ -97,3 +102,8 @@
         }
     }
 }
+
+@Module
+interface FakeBiometricSettingsRepositoryModule {
+    @Binds fun bindFake(fake: FakeBiometricSettingsRepository): BiometricSettingsRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
index 38791ca..c9160ef 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
@@ -17,14 +17,20 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
+import dagger.Binds
+import dagger.Module
+import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.filterNotNull
 
-class FakeDeviceEntryFingerprintAuthRepository : DeviceEntryFingerprintAuthRepository {
+@SysUISingleton
+class FakeDeviceEntryFingerprintAuthRepository @Inject constructor() :
+    DeviceEntryFingerprintAuthRepository {
     private val _isLockedOut = MutableStateFlow(false)
     override val isLockedOut: StateFlow<Boolean> = _isLockedOut.asStateFlow()
     fun setLockedOut(lockedOut: Boolean) {
@@ -52,3 +58,11 @@
         _authenticationStatus.value = status
     }
 }
+
+@Module
+interface FakeDeviceEntryFingerprintAuthRepositoryModule {
+    @Binds
+    fun bindFake(
+        fake: FakeDeviceEntryFingerprintAuthRepository
+    ): DeviceEntryFingerprintAuthRepository
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index b90ad8c..3674244 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -151,6 +151,17 @@
         _transitions.emit(step)
     }
 
+    suspend fun sendTransitionSteps(
+        steps: List<TransitionStep>,
+        testScope: TestScope,
+        validateStep: Boolean = true
+    ) {
+        steps.forEach {
+            sendTransitionStep(it, validateStep = validateStep)
+            testScope.testScheduler.runCurrent()
+        }
+    }
+
     override fun startTransition(info: TransitionInfo): UUID? {
         return if (info.animator == null) UUID.randomUUID() else null
     }