Fix test failures when NICRefactor flag is enabled

Failures are caused by running view-binder logic inside of unrelated
unit tests; these tests do not actually verify anything pertaining to
the NotificationIconContainer, so this CL restructures the code so that
we can mock out the view-binder.

Actual view-binder logic is verified via existing NIC screenshot tests.

Flag: ACONFIG com.android.systemui.notifications_icon_container_refactor DEVELOPMENT
Test: atest SystemUITests
Bug: 278765923
Change-Id: I1af34b9e96616c5a923d87fbf25625fd36024544
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index cdd7b80..74b975c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -39,7 +39,6 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.systemui.Dumpable;
-import com.android.systemui.common.ui.ConfigurationState;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
@@ -48,9 +47,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
-import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder;
 import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager;
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.core.LogLevel;
 import com.android.systemui.log.dagger.KeyguardClockLog;
@@ -62,17 +59,11 @@
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker;
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerAlwaysOnDisplayViewBinder;
 import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
-import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.ui.SystemBarUtilsState;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.settings.SecureSettings;
@@ -102,14 +93,7 @@
     private final DumpManager mDumpManager;
     private final ClockEventController mClockEventController;
     private final LogBuffer mLogBuffer;
-    private final NotificationIconContainerAlwaysOnDisplayViewModel mAodIconsViewModel;
-    private final KeyguardRootViewModel mKeyguardRootViewModel;
-    private final ConfigurationState mConfigurationState;
-    private final SystemBarUtilsState mSystemBarUtilsState;
-    private final DozeParameters mDozeParameters;
-    private final ScreenOffAnimationController mScreenOffAnimationController;
-    private final AlwaysOnDisplayNotificationIconViewStore mAodIconViewStore;
-    private final StatusBarIconViewBindingFailureTracker mIconViewBindingFailureTracker;
+    private final NotificationIconContainerAlwaysOnDisplayViewBinder mNicViewBinder;
     private FrameLayout mSmallClockFrame; // top aligned clock
     private FrameLayout mLargeClockFrame; // centered clock
 
@@ -183,9 +167,7 @@
             KeyguardSliceViewController keyguardSliceViewController,
             NotificationIconAreaController notificationIconAreaController,
             LockscreenSmartspaceController smartspaceController,
-            SystemBarUtilsState systemBarUtilsState,
-            ScreenOffAnimationController screenOffAnimationController,
-            StatusBarIconViewBindingFailureTracker iconViewBindingFailureTracker,
+            NotificationIconContainerAlwaysOnDisplayViewBinder nicViewBinder,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
             SecureSettings secureSettings,
             @Main DelayableExecutor uiExecutor,
@@ -193,11 +175,6 @@
             DumpManager dumpManager,
             ClockEventController clockEventController,
             @KeyguardClockLog LogBuffer logBuffer,
-            NotificationIconContainerAlwaysOnDisplayViewModel aodIconsViewModel,
-            KeyguardRootViewModel keyguardRootViewModel,
-            ConfigurationState configurationState,
-            DozeParameters dozeParameters,
-            AlwaysOnDisplayNotificationIconViewStore aodIconViewStore,
             KeyguardInteractor keyguardInteractor,
             KeyguardClockInteractor keyguardClockInteractor,
             FeatureFlagsClassic featureFlags,
@@ -208,9 +185,7 @@
         mKeyguardSliceViewController = keyguardSliceViewController;
         mNotificationIconAreaController = notificationIconAreaController;
         mSmartspaceController = smartspaceController;
-        mSystemBarUtilsState = systemBarUtilsState;
-        mScreenOffAnimationController = screenOffAnimationController;
-        mIconViewBindingFailureTracker = iconViewBindingFailureTracker;
+        mNicViewBinder = nicViewBinder;
         mSecureSettings = secureSettings;
         mUiExecutor = uiExecutor;
         mBgExecutor = bgExecutor;
@@ -218,11 +193,6 @@
         mDumpManager = dumpManager;
         mClockEventController = clockEventController;
         mLogBuffer = logBuffer;
-        mAodIconsViewModel = aodIconsViewModel;
-        mKeyguardRootViewModel = keyguardRootViewModel;
-        mConfigurationState = configurationState;
-        mDozeParameters = dozeParameters;
-        mAodIconViewStore = aodIconViewStore;
         mView.setLogBuffer(mLogBuffer);
         mFeatureFlags = featureFlags;
         mKeyguardInteractor = keyguardInteractor;
@@ -619,28 +589,7 @@
                     mAodIconsBindHandle.dispose();
                 }
                 if (nic != null) {
-                    final DisposableHandle viewHandle =
-                            NotificationIconContainerViewBinder.bindWhileAttached(
-                                    nic,
-                                    mAodIconsViewModel,
-                                    mConfigurationState,
-                                    mSystemBarUtilsState,
-                                    mIconViewBindingFailureTracker,
-                                    mAodIconViewStore);
-                    final DisposableHandle visHandle = KeyguardRootViewBinder.bindAodIconVisibility(
-                            nic,
-                            mKeyguardRootViewModel.isNotifIconContainerVisible(),
-                            mConfigurationState,
-                            mFeatureFlags,
-                            mScreenOffAnimationController);
-                    if (visHandle == null) {
-                        mAodIconsBindHandle = viewHandle;
-                    } else {
-                        mAodIconsBindHandle = () -> {
-                            viewHandle.dispose();
-                            visHandle.dispose();
-                        };
-                    }
+                    mAodIconsBindHandle = mNicViewBinder.bindWhileAttached(nic);
                     mAodIconContainer = nic;
                 }
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 08e2a8f..362e7e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -29,7 +29,6 @@
 import android.view.ViewPropertyAnimator
 import android.view.WindowInsets
 import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.animation.Interpolators
 import com.android.internal.jank.InteractionJankMonitor
@@ -67,6 +66,7 @@
 import javax.inject.Provider
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
@@ -205,7 +205,6 @@
                                     childViews[aodNotificationIconContainerId]
                                         ?.setAodNotifIconContainerIsVisible(
                                             isVisible,
-                                            featureFlags,
                                             iconsAppearTranslationPx.value,
                                             screenOffAnimationController,
                                         )
@@ -359,37 +358,29 @@
         }
     }
 
-    @JvmStatic
-    fun bindAodIconVisibility(
+    suspend fun bindAodNotifIconVisibility(
         view: View,
         isVisible: Flow<AnimatedValue<Boolean>>,
         configuration: ConfigurationState,
-        featureFlags: FeatureFlagsClassic,
         screenOffAnimationController: ScreenOffAnimationController,
-    ): DisposableHandle? {
+    ) {
         KeyguardShadeMigrationNssl.assertInLegacyMode()
-        if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return null
-        return view.repeatWhenAttached {
-            lifecycleScope.launch {
-                val iconAppearTranslationPx =
-                    configuration
-                        .getDimensionPixelSize(R.dimen.shelf_appear_translation)
-                        .stateIn(this)
-                isVisible.collect { isVisible ->
-                    view.setAodNotifIconContainerIsVisible(
-                        isVisible,
-                        featureFlags,
-                        iconAppearTranslationPx.value,
-                        screenOffAnimationController,
-                    )
-                }
+        if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return
+        coroutineScope {
+            val iconAppearTranslationPx =
+                configuration.getDimensionPixelSize(R.dimen.shelf_appear_translation).stateIn(this)
+            isVisible.collect { isVisible ->
+                view.setAodNotifIconContainerIsVisible(
+                    isVisible = isVisible,
+                    iconsAppearTranslationPx = iconAppearTranslationPx.value,
+                    screenOffAnimationController = screenOffAnimationController,
+                )
             }
         }
     }
 
     private fun View.setAodNotifIconContainerIsVisible(
         isVisible: AnimatedValue<Boolean>,
-        featureFlags: FeatureFlagsClassic,
         iconsAppearTranslationPx: Int,
         screenOffAnimationController: ScreenOffAnimationController,
     ) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
new file mode 100644
index 0000000..d7c29f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerAlwaysOnDisplayViewBinder.kt
@@ -0,0 +1,76 @@
+/*
+ * 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 androidx.lifecycle.lifecycleScope
+import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+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.NotificationIconContainerAlwaysOnDisplayViewModel
+import com.android.systemui.statusbar.phone.NotificationIconContainer
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.launch
+
+/** Binds a [NotificationIconContainer] to a [NotificationIconContainerAlwaysOnDisplayViewModel]. */
+class NotificationIconContainerAlwaysOnDisplayViewBinder
+@Inject
+constructor(
+    private val viewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
+    private val keyguardRootViewModel: KeyguardRootViewModel,
+    private val configuration: ConfigurationState,
+    private val failureTracker: StatusBarIconViewBindingFailureTracker,
+    private val screenOffAnimationController: ScreenOffAnimationController,
+    private val systemBarUtilsState: SystemBarUtilsState,
+    private val viewStore: AlwaysOnDisplayNotificationIconViewStore,
+) {
+    fun bindWhileAttached(view: NotificationIconContainer): DisposableHandle {
+        return view.repeatWhenAttached {
+            lifecycleScope.launch {
+                launch {
+                    NotificationIconContainerViewBinder.bind(
+                        view = view,
+                        viewModel = viewModel,
+                        configuration = configuration,
+                        systemBarUtilsState = systemBarUtilsState,
+                        failureTracker = failureTracker,
+                        viewStore = viewStore,
+                    )
+                }
+                launch {
+                    KeyguardRootViewBinder.bindAodNotifIconVisibility(
+                        view = view,
+                        isVisible = keyguardRootViewModel.isNotifIconContainerVisible,
+                        configuration = configuration,
+                        screenOffAnimationController = screenOffAnimationController,
+                    )
+                }
+            }
+        }
+    }
+}
+
+/** [IconViewStore] for the always-on display. */
+class AlwaysOnDisplayNotificationIconViewStore
+@Inject
+constructor(notifCollection: NotifCollection) :
+    IconViewStore by (notifCollection.iconViewStoreBy { it.aodIcon })
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt
new file mode 100644
index 0000000..783488af
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerShelfViewBinder.kt
@@ -0,0 +1,51 @@
+/*
+ * 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 com.android.systemui.common.ui.ConfigurationState
+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.viewbinder.NotificationIconContainerViewBinder.bindIcons
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
+import com.android.systemui.statusbar.phone.NotificationIconContainer
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
+import javax.inject.Inject
+
+/** Binds a [NotificationIconContainer] to a [NotificationIconContainerShelfViewModel]. */
+class NotificationIconContainerShelfViewBinder
+@Inject
+constructor(
+    private val viewModel: NotificationIconContainerShelfViewModel,
+    private val configuration: ConfigurationState,
+    private val systemBarUtilsState: SystemBarUtilsState,
+    private val failureTracker: StatusBarIconViewBindingFailureTracker,
+    private val viewStore: ShelfNotificationIconViewStore,
+) {
+    suspend fun bind(view: NotificationIconContainer) {
+        viewModel.icons.bindIcons(
+            view,
+            configuration,
+            systemBarUtilsState,
+            notifyBindingFailures = { failureTracker.shelfFailures = it },
+            viewStore,
+        )
+    }
+}
+
+/** [IconViewStore] for the [com.android.systemui.statusbar.NotificationShelf] */
+class ShelfNotificationIconViewStore @Inject constructor(notifCollection: NotifCollection) :
+    IconViewStore by (notifCollection.iconViewStoreBy { it.shelfIcon })
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
new file mode 100644
index 0000000..8e089b1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.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.statusbar.notification.icon.ui.viewbinder
+
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.common.ui.ConfigurationState
+import com.android.systemui.lifecycle.repeatWhenAttached
+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.NotificationIconContainerStatusBarViewModel
+import com.android.systemui.statusbar.phone.NotificationIconContainer
+import com.android.systemui.statusbar.ui.SystemBarUtilsState
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.launch
+
+/** Binds a [NotificationIconContainer] to a [NotificationIconContainerStatusBarViewModel]. */
+class NotificationIconContainerStatusBarViewBinder
+@Inject
+constructor(
+    private val viewModel: NotificationIconContainerStatusBarViewModel,
+    private val configuration: ConfigurationState,
+    private val systemBarUtilsState: SystemBarUtilsState,
+    private val failureTracker: StatusBarIconViewBindingFailureTracker,
+    private val viewStore: StatusBarNotificationIconViewStore,
+) {
+    fun bindWhileAttached(view: NotificationIconContainer): DisposableHandle {
+        return view.repeatWhenAttached {
+            lifecycleScope.launch {
+                NotificationIconContainerViewBinder.bind(
+                    view = view,
+                    viewModel = viewModel,
+                    configuration = configuration,
+                    systemBarUtilsState = systemBarUtilsState,
+                    failureTracker = failureTracker,
+                    viewStore = viewStore,
+                )
+            }
+        }
+    }
+}
+
+/** [IconViewStore] for the status bar. */
+class StatusBarNotificationIconViewStore @Inject constructor(notifCollection: NotifCollection) :
+    IconViewStore by (notifCollection.iconViewStoreBy { it.statusBarIcon })
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 e1e30e1..8fe0022 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
@@ -35,7 +35,6 @@
 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.notification.icon.ui.viewmodel.NotificationIconsViewData.LimitType
@@ -45,7 +44,6 @@
 import com.android.systemui.util.ui.isAnimating
 import com.android.systemui.util.ui.stopAnimating
 import com.android.systemui.util.ui.value
-import javax.inject.Inject
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.coroutineScope
@@ -56,42 +54,6 @@
 
 /** Binds a view-model to a [NotificationIconContainer]. */
 object NotificationIconContainerViewBinder {
-    @JvmStatic
-    fun bindWhileAttached(
-        view: NotificationIconContainer,
-        viewModel: NotificationIconContainerShelfViewModel,
-        configuration: ConfigurationState,
-        systemBarUtilsState: SystemBarUtilsState,
-        failureTracker: StatusBarIconViewBindingFailureTracker,
-        viewStore: IconViewStore,
-    ): DisposableHandle {
-        return view.repeatWhenAttached {
-            lifecycleScope.launch {
-                viewModel.icons.bindIcons(
-                    view,
-                    configuration,
-                    systemBarUtilsState,
-                    notifyBindingFailures = { failureTracker.shelfFailures = it },
-                    viewStore,
-                )
-            }
-        }
-    }
-
-    @JvmStatic
-    fun bindWhileAttached(
-        view: NotificationIconContainer,
-        viewModel: NotificationIconContainerStatusBarViewModel,
-        configuration: ConfigurationState,
-        systemBarUtilsState: SystemBarUtilsState,
-        failureTracker: StatusBarIconViewBindingFailureTracker,
-        viewStore: IconViewStore,
-    ): DisposableHandle =
-        view.repeatWhenAttached {
-            lifecycleScope.launch {
-                bind(view, viewModel, configuration, systemBarUtilsState, failureTracker, viewStore)
-            }
-        }
 
     suspend fun bind(
         view: NotificationIconContainer,
@@ -215,7 +177,7 @@
      * given `iconKey`. The parent [Job] of this coroutine will be cancelled automatically when the
      * view is to be unbound.
      */
-    private suspend fun Flow<NotificationIconsViewData>.bindIcons(
+    suspend fun Flow<NotificationIconsViewData>.bindIcons(
         view: NotificationIconContainer,
         configuration: ConfigurationState,
         systemBarUtilsState: SystemBarUtilsState,
@@ -377,24 +339,14 @@
     }
 
     @ColorInt private const val DEFAULT_AOD_ICON_COLOR = Color.WHITE
-    private const val TAG =  "NotifIconContainerViewBinder"
+    private const val TAG = "NotifIconContainerViewBinder"
 }
 
-/** [IconViewStore] for the [com.android.systemui.statusbar.NotificationShelf] */
-class ShelfNotificationIconViewStore @Inject constructor(notifCollection: NotifCollection) :
-    IconViewStore by (notifCollection.iconViewStoreBy { it.shelfIcon })
-
-/** [IconViewStore] for the always-on display. */
-class AlwaysOnDisplayNotificationIconViewStore
-@Inject
-constructor(notifCollection: NotifCollection) :
-    IconViewStore by (notifCollection.iconViewStoreBy { it.aodIcon })
-
-/** [IconViewStore] for the status bar. */
-class StatusBarNotificationIconViewStore @Inject constructor(notifCollection: NotifCollection) :
-    IconViewStore by (notifCollection.iconViewStoreBy { it.statusBarIcon })
-
-private fun NotifCollection.iconViewStoreBy(block: (IconPack) -> StatusBarIconView?) =
+/**
+ * Convenience builder for [IconViewStore] that uses [block] to extract the relevant
+ * [StatusBarIconView] from an [IconPack] stored inside of the [NotifCollection].
+ */
+fun NotifCollection.iconViewStoreBy(block: (IconPack) -> StatusBarIconView?) =
     IconViewStore { key ->
         getEntry(key)?.icons?.let(block)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 699e140..5ab4d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -16,60 +16,38 @@
 
 package com.android.systemui.statusbar.notification.shelf.ui.viewbinder
 
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.NotificationShelf
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerShelfViewBinder
 import com.android.systemui.statusbar.notification.row.ui.viewbinder.ActivatableNotificationViewBinder
 import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
 import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
-import com.android.systemui.statusbar.ui.SystemBarUtilsState
 import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
 
 /** Binds a [NotificationShelf] to its [view model][NotificationShelfViewModel]. */
 object NotificationShelfViewBinder {
-    fun bind(
+    suspend fun bind(
         shelf: NotificationShelf,
         viewModel: NotificationShelfViewModel,
-        configuration: ConfigurationState,
-        systemBarUtilsState: SystemBarUtilsState,
         falsingManager: FalsingManager,
-        iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
+        nicBinder: NotificationIconContainerShelfViewBinder,
         notificationIconAreaController: NotificationIconAreaController,
-        shelfIconViewStore: ShelfNotificationIconViewStore,
-    ) {
+    ): Unit = coroutineScope {
         ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
         shelf.apply {
             if (NotificationIconContainerRefactor.isEnabled) {
-                NotificationIconContainerViewBinder.bindWhileAttached(
-                    shelfIcons,
-                    viewModel.icons,
-                    configuration,
-                    systemBarUtilsState,
-                    iconViewBindingFailureTracker,
-                    shelfIconViewStore,
-                )
+                launch { nicBinder.bind(shelfIcons) }
             } else {
                 notificationIconAreaController.setShelfIcons(shelfIcons)
             }
-            repeatWhenAttached {
-                repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    launch {
-                        viewModel.canModifyColorOfNotifications.collect(
-                            ::setCanModifyColorOfNotifications
-                        )
-                    }
-                    launch { viewModel.isClickable.collect(::setCanInteract) }
-                    registerViewListenersWhileAttached(shelf, viewModel)
-                }
+            launch {
+                viewModel.canModifyColorOfNotifications.collect(::setCanModifyColorOfNotifications)
             }
+            launch { viewModel.isClickable.collect(::setCanInteract) }
+            registerViewListenersWhileAttached(shelf, viewModel)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
index 64b5b62c..5ca8b53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
@@ -18,7 +18,6 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.NotificationShelf
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
 import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel
 import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor
 import javax.inject.Inject
@@ -32,7 +31,6 @@
 constructor(
     private val interactor: NotificationShelfInteractor,
     activatableViewModel: ActivatableNotificationViewModel,
-    val icons: NotificationIconContainerShelfViewModel,
 ) : ActivatableNotificationViewModel by activatableViewModel {
     /** Is the shelf allowed to be clickable when it has content? */
     val isClickable: Flow<Boolean>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index d2fca8f..7c7d943 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -859,7 +859,7 @@
         mGroupExpansionManager.registerGroupExpansionChangeListener(
                 (changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded));
 
-        mViewBinder.bind(mView, this);
+        mViewBinder.bindWhileAttached(mView, this);
 
         if (!FooterViewRefactor.isEnabled()) {
             collectFlow(mView, mKeyguardTransitionRepo.getTransitions(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt
index 274bf94..910b40f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/HideNotificationsBinder.kt
@@ -16,29 +16,22 @@
 package com.android.systemui.statusbar.notification.stack.ui.viewbinder
 
 import androidx.core.view.doOnDetach
-import androidx.lifecycle.lifecycleScope
-import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
-import kotlinx.coroutines.launch
 
 /**
  * Binds a [NotificationStackScrollLayoutController] to its [view model][NotificationListViewModel].
  */
 object HideNotificationsBinder {
-    fun bindHideList(
+    suspend fun bindHideList(
         viewController: NotificationStackScrollLayoutController,
         viewModel: NotificationListViewModel
     ) {
-        viewController.view.repeatWhenAttached {
-            lifecycleScope.launch {
-                viewModel.hideListViewModel.shouldHideListForPerformance.collect { shouldHide ->
-                    viewController.bindHideState(shouldHide)
-                }
-            }
-        }
-
         viewController.view.doOnDetach { viewController.bindHideState(shouldHide = false) }
+
+        viewModel.hideListViewModel.shouldHideListForPerformance.collect { shouldHide ->
+            viewController.bindHideState(shouldHide)
+        }
     }
 
     private fun NotificationStackScrollLayoutController.bindHideState(shouldHide: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 9373d49..1b36660 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -32,15 +32,14 @@
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
 import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ShelfNotificationIconViewStore
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerShelfViewBinder
 import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.notification.stack.ui.viewbinder.HideNotificationsBinder.bindHideList
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
-import com.android.systemui.statusbar.ui.SystemBarUtilsState
+import com.android.systemui.util.kotlin.getOrNull
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.combine
@@ -55,25 +54,27 @@
     private val configuration: ConfigurationState,
     private val falsingManager: FalsingManager,
     private val iconAreaController: NotificationIconAreaController,
-    private val iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
     private val metricsLogger: MetricsLogger,
-    private val shelfIconViewStore: ShelfNotificationIconViewStore,
-    private val systemBarUtilsState: SystemBarUtilsState,
+    private val nicBinder: NotificationIconContainerShelfViewBinder,
 ) {
 
-    fun bind(
+    fun bindWhileAttached(
         view: NotificationStackScrollLayout,
         viewController: NotificationStackScrollLayoutController
     ) {
-        bindShelf(view)
-        bindHideList(viewController, viewModel)
+        val shelf =
+            LayoutInflater.from(view.context)
+                .inflate(R.layout.status_bar_notification_shelf, view, false) as NotificationShelf
+        view.setShelf(shelf)
 
-        if (FooterViewRefactor.isEnabled) {
-            bindFooter(view)
-            bindEmptyShade(view)
+        view.repeatWhenAttached {
+            lifecycleScope.launch {
+                launch { bindShelf(shelf) }
+                launch { bindHideList(viewController, viewModel) }
 
-            view.repeatWhenAttached {
-                lifecycleScope.launch {
+                if (FooterViewRefactor.isEnabled) {
+                    launch { bindFooter(view) }
+                    launch { bindEmptyShade(view) }
                     viewModel.isImportantForAccessibility.collect { isImportantForAccessibility ->
                         view.setImportantForAccessibilityYesNo(isImportantForAccessibility)
                     }
@@ -82,73 +83,57 @@
         }
     }
 
-    private fun bindShelf(parentView: NotificationStackScrollLayout) {
-        val shelf =
-            LayoutInflater.from(parentView.context)
-                .inflate(R.layout.status_bar_notification_shelf, parentView, false)
-                as NotificationShelf
+    private suspend fun bindShelf(shelf: NotificationShelf) {
         NotificationShelfViewBinder.bind(
             shelf,
             viewModel.shelf,
-            configuration,
-            systemBarUtilsState,
             falsingManager,
-            iconViewBindingFailureTracker,
+            nicBinder,
             iconAreaController,
-            shelfIconViewStore,
         )
-        parentView.setShelf(shelf)
     }
 
-    private fun bindFooter(parentView: NotificationStackScrollLayout) {
-        viewModel.footer.ifPresent { footerViewModel ->
+    private suspend fun bindFooter(parentView: NotificationStackScrollLayout) {
+        viewModel.footer.getOrNull()?.let { footerViewModel ->
             // The footer needs to be re-inflated every time the theme or the font size changes.
-            parentView.repeatWhenAttached {
-                configuration.reinflateAndBindLatest(
-                    R.layout.status_bar_notification_footer,
-                    parentView,
-                    attachToRoot = false,
-                    backgroundDispatcher,
-                ) { footerView: FooterView ->
-                    traceSection("bind FooterView") {
-                        val disposableHandle =
-                            FooterViewBinder.bind(
-                                footerView,
-                                footerViewModel,
-                                clearAllNotifications = {
-                                    metricsLogger.action(
-                                        MetricsProto.MetricsEvent.ACTION_DISMISS_ALL_NOTES
-                                    )
-                                    parentView.clearAllNotifications()
-                                },
-                            )
-                        parentView.setFooterView(footerView)
-                        return@reinflateAndBindLatest disposableHandle
-                    }
+            configuration.reinflateAndBindLatest(
+                R.layout.status_bar_notification_footer,
+                parentView,
+                attachToRoot = false,
+                backgroundDispatcher,
+            ) { footerView: FooterView ->
+                traceSection("bind FooterView") {
+                    val disposableHandle =
+                        FooterViewBinder.bind(
+                            footerView,
+                            footerViewModel,
+                            clearAllNotifications = {
+                                metricsLogger.action(
+                                    MetricsProto.MetricsEvent.ACTION_DISMISS_ALL_NOTES
+                                )
+                                parentView.clearAllNotifications()
+                            },
+                        )
+                    parentView.setFooterView(footerView)
+                    return@reinflateAndBindLatest disposableHandle
                 }
             }
         }
     }
 
-    private fun bindEmptyShade(
-        parentView: NotificationStackScrollLayout,
-    ) {
-        parentView.repeatWhenAttached {
-            lifecycleScope.launch {
-                combine(
-                        viewModel.shouldShowEmptyShadeView,
-                        viewModel.areNotificationsHiddenInShade,
-                        viewModel.hasFilteredOutSeenNotifications,
-                        ::Triple
-                    )
-                    .collect { (shouldShow, areNotifsHidden, hasFilteredNotifs) ->
-                        parentView.updateEmptyShadeView(
-                            shouldShow,
-                            areNotifsHidden,
-                            hasFilteredNotifs,
-                        )
-                    }
+    private suspend fun bindEmptyShade(parentView: NotificationStackScrollLayout) {
+        combine(
+                viewModel.shouldShowEmptyShadeView,
+                viewModel.areNotificationsHiddenInShade,
+                viewModel.hasFilteredOutSeenNotifications,
+                ::Triple
+            )
+            .collect { (shouldShow, areNotifsHidden, hasFilteredNotifs) ->
+                parentView.updateEmptyShadeView(
+                    shouldShow,
+                    areNotifsHidden,
+                    hasFilteredNotifs,
+                )
             }
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index cd99934..2740cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -38,7 +38,6 @@
 import com.android.app.animation.InterpolatorsAndroidX;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dumpable;
-import com.android.systemui.common.ui.ConfigurationState;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
@@ -54,10 +53,7 @@
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarNotificationIconViewStore;
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
 import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
@@ -75,7 +71,6 @@
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener;
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.ui.SystemBarUtilsState;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
 import com.android.systemui.util.CarrierConfigTracker;
@@ -95,6 +90,8 @@
 
 import kotlin.Unit;
 
+import kotlinx.coroutines.DisposableHandle;
+
 /**
  * Contains the collapsed status bar and handles hiding/showing based on disable flags
  * and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -151,10 +148,7 @@
     private final DumpManager mDumpManager;
     private final StatusBarWindowStateController mStatusBarWindowStateController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    private final NotificationIconContainerStatusBarViewModel mStatusBarIconsViewModel;
-    private final ConfigurationState mConfigurationState;
-    private final SystemBarUtilsState mSystemBarUtilsState;
-    private final StatusBarNotificationIconViewStore mStatusBarIconViewStore;
+    private final NotificationIconContainerStatusBarViewBinder mNicViewBinder;
     private final DemoModeController mDemoModeController;
 
     private List<String> mBlockedIcons = new ArrayList<>();
@@ -216,7 +210,7 @@
         mWaitingForWindowStateChangeAfterCameraLaunch = false;
         mTransitionFromLockscreenToDreamStarted = false;
     };
-    private final StatusBarIconViewBindingFailureTracker mIconViewBindingFailureTracker;
+    private DisposableHandle mNicBindingDisposable;
 
     @Inject
     public CollapsedStatusBarFragment(
@@ -234,7 +228,7 @@
             KeyguardStateController keyguardStateController,
             ShadeViewController shadeViewController,
             StatusBarStateController statusBarStateController,
-            StatusBarIconViewBindingFailureTracker iconViewBindingFailureTracker,
+            NotificationIconContainerStatusBarViewBinder nicViewBinder,
             CommandQueue commandQueue,
             CarrierConfigTracker carrierConfigTracker,
             CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
@@ -244,10 +238,6 @@
             DumpManager dumpManager,
             StatusBarWindowStateController statusBarWindowStateController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            NotificationIconContainerStatusBarViewModel statusBarIconsViewModel,
-            ConfigurationState configurationState,
-            SystemBarUtilsState systemBarUtilsState,
-            StatusBarNotificationIconViewStore statusBarIconViewStore,
             DemoModeController demoModeController) {
         mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
         mOngoingCallController = ongoingCallController;
@@ -263,7 +253,7 @@
         mKeyguardStateController = keyguardStateController;
         mShadeViewController = shadeViewController;
         mStatusBarStateController = statusBarStateController;
-        mIconViewBindingFailureTracker = iconViewBindingFailureTracker;
+        mNicViewBinder = nicViewBinder;
         mCommandQueue = commandQueue;
         mCarrierConfigTracker = carrierConfigTracker;
         mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
@@ -273,10 +263,6 @@
         mDumpManager = dumpManager;
         mStatusBarWindowStateController = statusBarWindowStateController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mStatusBarIconsViewModel = statusBarIconsViewModel;
-        mConfigurationState = configurationState;
-        mSystemBarUtilsState = systemBarUtilsState;
-        mStatusBarIconViewStore = statusBarIconViewStore;
         mDemoModeController = demoModeController;
     }
 
@@ -455,6 +441,12 @@
             mStartableStates.put(startable, Startable.State.STOPPED);
         }
         mDumpManager.unregisterDumpable(getClass().getSimpleName());
+        if (NotificationIconContainerRefactor.isEnabled()) {
+            if (mNicBindingDisposable != null) {
+                mNicBindingDisposable.dispose();
+                mNicBindingDisposable = null;
+            }
+        }
     }
 
     /** Initializes views related to the notification icon area. */
@@ -466,13 +458,7 @@
                         .inflate(R.layout.notification_icon_area, notificationIconArea, true);
             NotificationIconContainer notificationIcons =
                     notificationIconArea.requireViewById(R.id.notificationIcons);
-            NotificationIconContainerViewBinder.bindWhileAttached(
-                    notificationIcons,
-                    mStatusBarIconsViewModel,
-                    mConfigurationState,
-                    mSystemBarUtilsState,
-                    mIconViewBindingFailureTracker,
-                    mStatusBarIconViewStore);
+            mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
         } else {
             mNotificationIconAreaInner =
                     mNotificationIconAreaController.getNotificationInnerAreaView();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
index 88f63ad..a249961 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java
@@ -34,14 +34,12 @@
 import android.widget.RelativeLayout;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.common.ui.ConfigurationState;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
 import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager;
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.plugins.clocks.ClockAnimations;
 import com.android.systemui.plugins.clocks.ClockController;
@@ -56,14 +54,9 @@
 import com.android.systemui.shared.clocks.ClockRegistry;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker;
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel;
-import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerAlwaysOnDisplayViewBinder;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.ui.SystemBarUtilsState;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -185,9 +178,7 @@
                 mKeyguardSliceViewController,
                 mNotificationIconAreaController,
                 mSmartspaceController,
-                mock(SystemBarUtilsState.class),
-                mock(ScreenOffAnimationController.class),
-                mock(StatusBarIconViewBindingFailureTracker.class),
+                mock(NotificationIconContainerAlwaysOnDisplayViewBinder.class),
                 mKeyguardUnlockAnimationController,
                 mSecureSettings,
                 mExecutor,
@@ -195,11 +186,6 @@
                 mDumpManager,
                 mClockEventController,
                 mLogBuffer,
-                mock(NotificationIconContainerAlwaysOnDisplayViewModel.class),
-                mock(KeyguardRootViewModel.class),
-                mock(ConfigurationState.class),
-                mock(DozeParameters.class),
-                mock(AlwaysOnDisplayNotificationIconViewStore.class),
                 KeyguardInteractorFactory.create(mFakeFeatureFlags).getKeyguardInteractor(),
                 mKeyguardClockInteractor,
                 mFakeFeatureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 1cc611c..14751c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -43,7 +43,6 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.animation.AnimatorTestRule;
-import com.android.systemui.common.ui.ConfigurationState;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.log.LogBuffer;
@@ -57,9 +56,7 @@
 import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker;
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarNotificationIconViewStore;
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerStatusBarViewModel;
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
@@ -70,7 +67,6 @@
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewBinder;
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewModel;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.ui.SystemBarUtilsState;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
 import com.android.systemui.util.CarrierConfigTracker;
@@ -702,7 +698,7 @@
                 mKeyguardStateController,
                 mShadeViewController,
                 mStatusBarStateController,
-                mock(StatusBarIconViewBindingFailureTracker.class),
+                mock(NotificationIconContainerStatusBarViewBinder.class),
                 mCommandQueue,
                 mCarrierConfigTracker,
                 new CollapsedStatusBarFragmentLogger(
@@ -715,10 +711,6 @@
                 mDumpManager,
                 mStatusBarWindowStateController,
                 mKeyguardUpdateMonitor,
-                mock(NotificationIconContainerStatusBarViewModel.class),
-                mock(ConfigurationState.class),
-                mock(SystemBarUtilsState.class),
-                mock(StatusBarNotificationIconViewStore.class),
                 mock(DemoModeController.class));
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ShelfNotificationIconViewStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinderKosmos.kt
similarity index 64%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ShelfNotificationIconViewStoreKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinderKosmos.kt
index f7f16a4..67fecb4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ShelfNotificationIconViewStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinderKosmos.kt
@@ -16,9 +16,22 @@
 
 package com.android.systemui.statusbar.notification.icon.ui.viewbinder
 
+import com.android.systemui.common.ui.configurationState
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.notificationIconContainerShelfViewModel
 import com.android.systemui.statusbar.notification.stack.ui.viewbinder.notifCollection
+import com.android.systemui.statusbar.ui.systemBarUtilsState
+
+val Kosmos.notificationIconContainerShelfViewBinder by Fixture {
+    NotificationIconContainerShelfViewBinder(
+        notificationIconContainerShelfViewModel,
+        configurationState,
+        systemBarUtilsState,
+        statusBarIconViewBindingFailureTracker,
+        shelfNotificationIconViewStore,
+    )
+}
 
 val Kosmos.shelfNotificationIconViewStore by Fixture {
     ShelfNotificationIconViewStore(notifCollection = notifCollection)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelKosmos.kt
index 988172c..b906b60 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelKosmos.kt
@@ -18,7 +18,6 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.notificationIconContainerShelfViewModel
 import com.android.systemui.statusbar.notification.row.ui.viewmodel.activatableNotificationViewModel
 import com.android.systemui.statusbar.notification.shelf.domain.interactor.notificationShelfInteractor
 
@@ -26,6 +25,5 @@
     NotificationShelfViewModel(
         interactor = notificationShelfInteractor,
         activatableViewModel = activatableNotificationViewModel,
-        icons = notificationIconContainerShelfViewModel,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
index ca5b401..04716b9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinderKosmos.kt
@@ -22,11 +22,9 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.shelfNotificationIconViewStore
-import com.android.systemui.statusbar.notification.icon.ui.viewbinder.statusBarIconViewBindingFailureTracker
+import com.android.systemui.statusbar.notification.icon.ui.viewbinder.notificationIconContainerShelfViewBinder
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationListViewModel
 import com.android.systemui.statusbar.phone.notificationIconAreaController
-import com.android.systemui.statusbar.ui.systemBarUtilsState
 
 val Kosmos.notificationListViewBinder by Fixture {
     NotificationListViewBinder(
@@ -35,9 +33,7 @@
         configuration = configurationState,
         falsingManager = falsingManager,
         iconAreaController = notificationIconAreaController,
-        iconViewBindingFailureTracker = statusBarIconViewBindingFailureTracker,
         metricsLogger = metricsLogger,
-        shelfIconViewStore = shelfNotificationIconViewStore,
-        systemBarUtilsState = systemBarUtilsState,
+        nicBinder = notificationIconContainerShelfViewBinder,
     )
 }