Replace NIC+SBIV#setDozing w/ SBIV#setTintAlpha

Flag: ACONFIG com.android.systemui.notifications_icon_container_refactor DEVELOPMENT
Bug: 278765923
Test: atest SystemUITests

Change-Id: Ie7a48d0430d57cbf3e5903cfd532918fec055c27
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 6b8009d..32f9c30 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -605,7 +605,6 @@
                             mAodIconsViewModel,
                             mConfigurationState,
                             mConfigurationController,
-                            mDozeParameters,
                             mAodIconViewStore);
                     final DisposableHandle visHandle = KeyguardRootViewBinder.bindAodIconVisibility(
                             nic,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index 06bb0a6..975d62a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -94,7 +94,6 @@
                     nicAodViewModel,
                     configurationState,
                     configurationController,
-                    dozeParameters,
                     nicAodIconViewStore,
                 )
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 3bf8057..843a454 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -61,6 +61,7 @@
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.NotificationDozeHelper;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
 import com.android.systemui.util.drawable.DrawableSize;
 
 import java.lang.annotation.Retention;
@@ -164,7 +165,6 @@
     private int mDrawableColor;
     private int mIconColor;
     private int mDecorColor;
-    private float mDozeAmount;
     private ValueAnimator mColorAnimator;
     private int mCurrentSetColor = NO_COLOR;
     private int mAnimationStartColor = NO_COLOR;
@@ -174,7 +174,6 @@
                 animation.getAnimatedFraction());
         setColorInternal(newColor);
     };
-    private final NotificationDozeHelper mDozer;
     private int mContrastedDrawableColor;
     private int mCachedContrastBackgroundColor = NO_COLOR;
     private float[] mMatrix;
@@ -184,6 +183,8 @@
     private Runnable mOnDismissListener;
     private boolean mIncreasedSize;
     private boolean mShowsConversation;
+    private float mDozeAmount;
+    private final NotificationDozeHelper mDozer;
 
     public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
         this(context, slot, sbn, false);
@@ -958,18 +959,28 @@
         return mDotAppearAmount;
     }
 
-    public void setDozing(boolean dozing, boolean fade, long delay) {
-        setDozing(dozing, fade, delay, /* onChildCompleted= */ null);
+    public void setDozing(boolean dozing, boolean animate, long delay) {
+        setDozing(dozing, animate, delay, /* onChildCompleted= */ null);
     }
 
-    public void setDozing(boolean dozing, boolean fade, long delay,
+    public void setTintAlpha(float tintAlpha) {
+        if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return;
+        setDozeAmount(tintAlpha);
+    }
+
+    private void setDozeAmount(float dozeAmount) {
+        mDozeAmount = dozeAmount;
+        updateDecorColor();
+        updateIconColor();
+    }
+
+    public void setDozing(boolean dozing, boolean animate, long delay,
             @Nullable Runnable endRunnable) {
+        NotificationIconContainerRefactor.assertInLegacyMode();
         mDozer.setDozing(f -> {
-            mDozeAmount = f;
-            updateDecorColor();
-            updateIconColor();
+            setDozeAmount(f);
             updateAllowAnimation();
-        }, dozing, fade, delay, this, endRunnable);
+        }, dozing, animate, delay, this, endRunnable);
     }
 
     private void updateAllowAnimation() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index 62c3e9e..2ea7f61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -28,15 +28,12 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.notification.NotificationUtils
 import com.android.systemui.statusbar.notification.collection.NotifCollection
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColors
 import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
 import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
 import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel
 import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconsViewData
-import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.NotificationIconContainer
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.onConfigChanged
@@ -44,7 +41,6 @@
 import com.android.systemui.util.kotlin.mapValuesNotNullTo
 import com.android.systemui.util.kotlin.sample
 import com.android.systemui.util.kotlin.stateFlow
-import com.android.systemui.util.ui.AnimatedValue
 import com.android.systemui.util.ui.isAnimating
 import com.android.systemui.util.ui.stopAnimating
 import com.android.systemui.util.ui.value
@@ -95,7 +91,11 @@
                         configurationController,
                         viewStore,
                     ) { _, sbiv ->
-                        iconColors.collect { sbiv.updateTintForIcon(it, contrastColorUtil) }
+                        StatusBarIconViewBinder.bindIconColors(
+                            sbiv,
+                            iconColors,
+                            contrastColorUtil,
+                        )
                     }
                 }
                 launch { viewModel.bindIsolatedIcon(view, viewStore) }
@@ -110,7 +110,6 @@
         viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
         configuration: ConfigurationState,
         configurationController: ConfigurationController,
-        dozeParameters: DozeParameters,
         viewStore: IconViewStore,
     ): DisposableHandle {
         return view.repeatWhenAttached {
@@ -122,48 +121,37 @@
                         configurationController,
                         viewStore,
                     ) { _, sbiv ->
-                        configuration
-                            .getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR)
-                            .collect { tint ->
-                                sbiv.staticDrawableColor = tint
-                                sbiv.setDecorColor(tint)
-                            }
+                        viewModel.bindAodStatusBarIconView(sbiv, configuration)
                     }
                 }
-                launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
-                launch { viewModel.isDozing.bindIsDozing(view, dozeParameters) }
+                launch { viewModel.areContainerChangesAnimated.bindAnimationsEnabled(view) }
             }
         }
     }
 
+    private suspend fun NotificationIconContainerAlwaysOnDisplayViewModel.bindAodStatusBarIconView(
+        sbiv: StatusBarIconView,
+        configuration: ConfigurationState,
+    ) {
+        coroutineScope {
+            launch {
+                val color: Flow<Int> =
+                    configuration.getColorAttr(
+                        R.attr.wallpaperTextColor,
+                        DEFAULT_AOD_ICON_COLOR,
+                    )
+                StatusBarIconViewBinder.bindColor(sbiv, color)
+            }
+            launch { StatusBarIconViewBinder.bindTintAlpha(sbiv, tintAlpha) }
+            launch { StatusBarIconViewBinder.bindAnimationsEnabled(sbiv, areIconAnimationsEnabled) }
+        }
+    }
+
     /** Binds to [NotificationIconContainer.setAnimationsEnabled] */
     private suspend fun Flow<Boolean>.bindAnimationsEnabled(view: NotificationIconContainer) {
         collect(view::setAnimationsEnabled)
     }
 
-    private suspend fun Flow<AnimatedValue<Boolean>>.bindIsDozing(
-        view: NotificationIconContainer,
-        dozeParameters: DozeParameters,
-    ) {
-        collect { isDozing ->
-            if (isDozing.isAnimating) {
-                val animate = !dozeParameters.displayNeedsBlanking
-                view.setDozing(
-                    /* dozing = */ isDozing.value,
-                    /* fade = */ animate,
-                    /* delay = */ 0,
-                    /* endRunnable = */ isDozing::stopAnimating,
-                )
-            } else {
-                view.setDozing(
-                    /* dozing = */ isDozing.value,
-                    /* fade= */ false,
-                    /* delay= */ 0,
-                )
-            }
-        }
-    }
-
     private suspend fun NotificationIconContainerStatusBarViewModel.bindIsolatedIcon(
         view: NotificationIconContainer,
         viewStore: IconViewStore,
@@ -261,6 +249,7 @@
                 // and added again
                 view.removeTransientView(sbiv)
                 view.addView(sbiv, i, layoutParams)
+                iconBindings.remove(key)?.cancel()
                 iconBindings[key] = launch { bindIcon(key, sbiv) }
             }
 
@@ -282,18 +271,6 @@
         }
     }
 
-    // TODO(b/305739416): Once StatusBarIconView has its own Recommended Architecture stack, this
-    //  can be moved there and cleaned up.
-    private fun StatusBarIconView.updateTintForIcon(
-        iconColors: NotificationIconColors,
-        contrastColorUtil: ContrastColorUtil,
-    ) {
-        val isPreL = java.lang.Boolean.TRUE == getTag(R.id.icon_is_pre_L)
-        val isColorized = !isPreL || NotificationUtils.isGrayscale(this, contrastColorUtil)
-        staticDrawableColor = iconColors.staticDrawableColor(viewBounds, isColorized)
-        setDecorColor(iconColors.tint)
-    }
-
     /** External storage for [StatusBarIconView] instances. */
     fun interface IconViewStore {
         fun iconView(key: String): StatusBarIconView
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
new file mode 100644
index 0000000..3a2e21a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.statusbar.notification.icon.ui.viewbinder
+
+import android.graphics.Rect
+import android.view.View
+import com.android.internal.util.ContrastColorUtil
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.NotificationUtils
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColors
+import kotlinx.coroutines.flow.Flow
+
+object StatusBarIconViewBinder {
+
+    // TODO(b/305739416): Once StatusBarIconView has its own Recommended Architecture stack, these
+    //  methods can become private and we can have a single bind() method for SBIV and its
+    //  view-model (which, at the time of this writing, does not yet exist).
+
+    suspend fun bindColor(view: StatusBarIconView, color: Flow<Int>) {
+        color.collect { color ->
+            view.staticDrawableColor = color
+            view.setDecorColor(color)
+        }
+    }
+
+    suspend fun bindTintAlpha(view: StatusBarIconView, tintAlpha: Flow<Float>) {
+        tintAlpha.collect { amt -> view.setTintAlpha(amt) }
+    }
+
+    suspend fun bindAnimationsEnabled(view: StatusBarIconView, allowAnimation: Flow<Boolean>) {
+        allowAnimation.collect(view::setAllowAnimation)
+    }
+
+    suspend fun bindIconColors(
+        view: StatusBarIconView,
+        iconColors: Flow<NotificationIconColors>,
+        contrastColorUtil: ContrastColorUtil,
+    ) {
+        iconColors.collect { colors ->
+            val isPreL = java.lang.Boolean.TRUE == view.getTag(R.id.icon_is_pre_L)
+            val isColorized = !isPreL || NotificationUtils.isGrayscale(view, contrastColorUtil)
+            view.staticDrawableColor = colors.staticDrawableColor(view.viewBounds, isColorized)
+            view.setDecorColor(colors.tint)
+        }
+    }
+}
+
+private val View.viewBounds: Rect
+    get() {
+        val tmpArray = intArrayOf(0, 0)
+        getLocationOnScreen(tmpArray)
+        return Rect(
+            /* left = */ tmpArray[0],
+            /* top = */ tmpArray[1],
+            /* right = */ left + width,
+            /* bottom = */ top + height,
+        )
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
index 835c059..b11eca2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModel.kt
@@ -19,17 +19,13 @@
 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.shared.model.TransitionStep
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.icon.domain.interactor.AlwaysOnDisplayNotificationIconsInteractor
-import com.android.systemui.util.ui.AnimatableEvent
-import com.android.systemui.util.ui.AnimatedValue
-import com.android.systemui.util.ui.toAnimatedValueFlow
 import javax.inject.Inject
 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
 
 /** View-model for the row of notification icons displayed on the always-on display. */
 @SysUISingleton
@@ -43,7 +39,7 @@
 ) {
 
     /** Are changes to the icon container animated? */
-    val animationsEnabled: Flow<Boolean> =
+    val areContainerChangesAnimated: Flow<Boolean> =
         combine(
             shadeInteractor.isShadeTouchable,
             keyguardInteractor.isKeyguardVisible,
@@ -51,23 +47,21 @@
             panelTouchesEnabled && isKeyguardVisible
         }
 
-    /** Should icons be rendered in "dozing" mode? */
-    val isDozing: Flow<AnimatedValue<Boolean>> =
-        keyguardTransitionInteractor.startedKeyguardTransitionStep
-            // Determine if we're dozing based on the most recent transition
-            .map { step: TransitionStep ->
-                val isDozing = step.to == KeyguardState.AOD || step.to == KeyguardState.DOZING
-                isDozing to step
-            }
-            // Only emit changes based on whether we've started or stopped dozing
-            .distinctUntilChanged { (wasDozing, _), (isDozing, _) -> wasDozing != isDozing }
-            // Determine whether we need to animate
-            .map { (isDozing, step) ->
-                val animate = step.to == KeyguardState.AOD || step.from == KeyguardState.AOD
-                AnimatableEvent(isDozing, animate)
-            }
-            .distinctUntilChanged()
-            .toAnimatedValueFlow()
+    /** Amount of a "white" tint to be applied to the icons. */
+    val tintAlpha: Flow<Float> =
+        combine(
+            keyguardTransitionInteractor.transitionValue(KeyguardState.AOD).onStart { emit(0f) },
+            keyguardTransitionInteractor.transitionValue(KeyguardState.DOZING).onStart { emit(0f) },
+        ) { aodAmt, dozeAmt ->
+            aodAmt + dozeAmt // If transitioning between them, they should sum to 1f
+        }
+
+    /** Are notification icons animated (ex: animated gif)? */
+    val areIconAnimationsEnabled: Flow<Boolean> =
+        keyguardTransitionInteractor.isFinishedInStateWhere {
+            // Don't animate icons when we're on AOD / dozing
+            it != KeyguardState.AOD && it != KeyguardState.DOZING
+        }
 
     /** [NotificationIconsViewData] indicating which icons to display in the view. */
     val icons: Flow<NotificationIconsViewData> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 01f3b63..9e5fd95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -337,7 +337,9 @@
         }
         if (child instanceof StatusBarIconView) {
             ((StatusBarIconView) child).updateIconDimens();
-            ((StatusBarIconView) child).setDozing(mDozing, false, 0);
+            if (!NotificationIconContainerRefactor.isEnabled()) {
+                ((StatusBarIconView) child).setDozing(mDozing, false, 0);
+            }
         }
     }
 
@@ -633,14 +635,16 @@
         mChangingViewPositions = changingViewPositions;
     }
 
-    public void setDozing(boolean dozing, boolean fade, long delay) {
-        setDozing(dozing, fade, delay, /* endRunnable= */ null);
+    public void setDozing(boolean dozing, boolean animate, long delay) {
+        NotificationIconContainerRefactor.assertInLegacyMode();
+        setDozing(dozing, animate, delay, /* endRunnable= */ null);
     }
 
-    public void setDozing(boolean dozing, boolean fade, long delay,
+    private void setDozing(boolean dozing, boolean animate, long delay,
             @Nullable Runnable endRunnable) {
+        NotificationIconContainerRefactor.assertInLegacyMode();
         mDozing = dozing;
-        mDisallowNextAnimation |= !fade;
+        mDisallowNextAnimation |= !animate;
         final int childCount = getChildCount();
         // Track all the child invocations of setDozing, invoking the top-level endRunnable once
         // they have all completed.
@@ -657,7 +661,7 @@
         for (int i = 0; i < childCount; i++) {
             View view = getChildAt(i);
             if (view instanceof StatusBarIconView) {
-                ((StatusBarIconView) view).setDozing(dozing, fade, delay, onChildCompleted);
+                ((StatusBarIconView) view).setDozing(dozing, animate, delay, onChildCompleted);
             } else if (onChildCompleted != null) {
                 onChildCompleted.run();
             }
@@ -780,15 +784,7 @@
                 StatusBarIconView icon = (StatusBarIconView) view;
                 boolean animate = false;
                 AnimationProperties animationProperties = null;
-                final boolean isLowPriorityIconChange =
-                        (visibleState == StatusBarIconView.STATE_HIDDEN
-                                && icon.getVisibleState() == StatusBarIconView.STATE_DOT)
-                        || (visibleState == StatusBarIconView.STATE_DOT
-                            && icon.getVisibleState() == StatusBarIconView.STATE_HIDDEN);
-                final boolean animationsAllowed = areAnimationsEnabled(icon)
-                        && !mDisallowNextAnimation
-                        && !noAnimations
-                        && !isLowPriorityIconChange;
+                final boolean animationsAllowed = animationsAllowed(icon);
                 if (animationsAllowed) {
                     if (justAdded || justReplaced) {
                         super.applyToView(icon);
@@ -879,6 +875,18 @@
             needsCannedAnimation = false;
         }
 
+        private boolean animationsAllowed(StatusBarIconView icon) {
+            final boolean isLowPriorityIconChange =
+                    (visibleState == StatusBarIconView.STATE_HIDDEN
+                            && icon.getVisibleState() == StatusBarIconView.STATE_DOT)
+                    || (visibleState == StatusBarIconView.STATE_DOT
+                        && icon.getVisibleState() == StatusBarIconView.STATE_HIDDEN);
+            return areAnimationsEnabled(icon)
+                    && !mDisallowNextAnimation
+                    && !noAnimations
+                    && !isLowPriorityIconChange;
+        }
+
         @Nullable
         private Consumer<Property> getEndAction() {
             if (mIsolatedIconAnimationEndRunnable == null) return null;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
index 788cfbc..c2c33de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
@@ -45,9 +45,6 @@
 import com.android.systemui.user.domain.UserDomainLayerModule
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.ui.isAnimating
-import com.android.systemui.util.ui.stopAnimating
-import com.android.systemui.util.ui.value
 import com.google.common.truth.Truth.assertThat
 import dagger.BindsInstance
 import dagger.Component
@@ -129,7 +126,7 @@
                     transitionState = TransitionState.STARTED,
                 )
             )
-            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
             runCurrent()
             assertThat(animationsEnabled).isFalse()
         }
@@ -152,7 +149,7 @@
                     to = DozeStateModel.DOZE_AOD,
                 )
             )
-            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
             runCurrent()
             assertThat(animationsEnabled).isFalse()
         }
@@ -175,7 +172,7 @@
                     to = DozeStateModel.DOZE_PULSING,
                 )
             )
-            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
             runCurrent()
             assertThat(animationsEnabled).isTrue()
         }
@@ -196,7 +193,7 @@
                 )
             )
             whenever(dozeParams.shouldControlScreenOff()).thenReturn(false)
-            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
             runCurrent()
             assertThat(animationsEnabled).isFalse()
         }
@@ -217,7 +214,7 @@
                 )
             )
             whenever(dozeParams.shouldControlScreenOff()).thenReturn(true)
-            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
             runCurrent()
             assertThat(animationsEnabled).isTrue()
         }
@@ -235,7 +232,7 @@
                     transitionState = TransitionState.STARTED,
                 )
             )
-            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
             runCurrent()
             assertThat(animationsEnabled).isTrue()
         }
@@ -248,7 +245,7 @@
                     transitionState = TransitionState.STARTED,
                 )
             )
-            val animationsEnabled by collectLastValue(underTest.animationsEnabled)
+            val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
             runCurrent()
 
             keyguardRepository.setKeyguardShowing(true)
@@ -270,91 +267,151 @@
         }
 
     @Test
-    fun isDozing_startAodTransition() =
+    fun tintAlpha_isZero_whenNotOnAodOrDozing() =
         testComponent.runTest {
-            val isDozing by collectLastValue(underTest.isDozing)
+            val tintAlpha by collectLastValue(underTest.tintAlpha)
             runCurrent()
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.GONE,
-                    to = KeyguardState.AOD,
-                    transitionState = TransitionState.STARTED,
-                )
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.DOZING,
+                to = KeyguardState.GONE,
+                testScope,
             )
             runCurrent()
-            assertThat(isDozing?.value).isTrue()
-            assertThat(isDozing?.isAnimating).isTrue()
+            assertThat(tintAlpha).isZero()
         }
 
     @Test
-    fun isDozing_startDozeTransition() =
+    fun tintAlpha_isOne_whenOnAod() =
         testComponent.runTest {
-            val isDozing by collectLastValue(underTest.isDozing)
+            val tintAlpha by collectLastValue(underTest.tintAlpha)
             runCurrent()
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.GONE,
+                to = KeyguardState.AOD,
+                testScope,
+            )
+            runCurrent()
+            assertThat(tintAlpha).isEqualTo(1f)
+        }
+
+    @Test
+    fun tintAlpha_isOne_whenDozing() =
+        testComponent.runTest {
+            val tintAlpha by collectLastValue(underTest.tintAlpha)
+            runCurrent()
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.GONE,
+                to = KeyguardState.DOZING,
+                testScope,
+            )
+            assertThat(tintAlpha).isEqualTo(1f)
+        }
+
+    @Test
+    fun tintAlpha_isOne_whenTransitionFromAodToDoze() =
+        testComponent.runTest {
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.GONE,
+                to = KeyguardState.AOD,
+                testScope,
+            )
+            val tintAlpha by collectLastValue(underTest.tintAlpha)
+            runCurrent()
+
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.GONE,
+                    transitionState = TransitionState.STARTED,
+                    from = KeyguardState.AOD,
                     to = KeyguardState.DOZING,
-                    transitionState = TransitionState.STARTED,
+                    value = 0f,
                 )
             )
             runCurrent()
-            assertThat(isDozing?.value).isTrue()
-            assertThat(isDozing?.isAnimating).isFalse()
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.RUNNING,
+                    from = KeyguardState.AOD,
+                    to = KeyguardState.DOZING,
+                    value = 0.5f,
+                )
+            )
+            runCurrent()
+
+            assertThat(tintAlpha).isEqualTo(1f)
         }
 
     @Test
-    fun isDozing_startDozeToAodTransition() =
+    fun tintAlpha_isFraction_midTransitionToAod() =
         testComponent.runTest {
-            val isDozing by collectLastValue(underTest.isDozing)
+            val tintAlpha by collectLastValue(underTest.tintAlpha)
             runCurrent()
+
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.DOZING,
+                    transitionState = TransitionState.STARTED,
+                    from = KeyguardState.GONE,
                     to = KeyguardState.AOD,
-                    transitionState = TransitionState.STARTED,
+                    value = 0f,
                 )
             )
             runCurrent()
-            assertThat(isDozing?.value).isTrue()
-            assertThat(isDozing?.isAnimating).isTrue()
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    transitionState = TransitionState.RUNNING,
+                    from = KeyguardState.GONE,
+                    to = KeyguardState.AOD,
+                    value = 0.5f,
+                )
+            )
+            runCurrent()
+
+            assertThat(tintAlpha).isEqualTo(0.5f)
         }
 
     @Test
-    fun isNotDozing_startAodToGoneTransition() =
+    fun iconAnimationsEnabled_whenOnLockScreen() =
         testComponent.runTest {
-            val isDozing by collectLastValue(underTest.isDozing)
+            val iconAnimationsEnabled by collectLastValue(underTest.areIconAnimationsEnabled)
             runCurrent()
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.AOD,
-                    to = KeyguardState.GONE,
-                    transitionState = TransitionState.STARTED,
-                )
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.GONE,
+                to = KeyguardState.LOCKSCREEN,
+                testScope,
             )
-            runCurrent()
-            assertThat(isDozing?.value).isFalse()
-            assertThat(isDozing?.isAnimating).isTrue()
+
+            assertThat(iconAnimationsEnabled).isTrue()
         }
 
     @Test
-    fun isDozing_stopAnimation() =
+    fun iconAnimationsDisabled_whenOnAod() =
         testComponent.runTest {
-            val isDozing by collectLastValue(underTest.isDozing)
+            val iconAnimationsEnabled by collectLastValue(underTest.areIconAnimationsEnabled)
             runCurrent()
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.AOD,
-                    to = KeyguardState.GONE,
-                    transitionState = TransitionState.STARTED,
-                )
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.GONE,
+                to = KeyguardState.AOD,
+                testScope,
             )
+
+            assertThat(iconAnimationsEnabled).isFalse()
+        }
+
+    @Test
+    fun iconAnimationsDisabled_whenDozing() =
+        testComponent.runTest {
+            val iconAnimationsEnabled by collectLastValue(underTest.areIconAnimationsEnabled)
             runCurrent()
 
-            assertThat(isDozing?.isAnimating).isEqualTo(true)
-            isDozing?.stopAnimating()
-            runCurrent()
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.GONE,
+                to = KeyguardState.DOZING,
+                testScope,
+            )
 
-            assertThat(isDozing?.isAnimating).isEqualTo(false)
+            assertThat(iconAnimationsEnabled).isFalse()
         }
 }