Revert "Add View.viewModel utility"

This reverts commit 08cedcebf76dec5149b944b92d731107a4c24e5b.

Reason for revert: Droidmonitor created revert due to b/357987442. Will be verifying through ABTD before submission.

Change-Id: I3425e680d6ccbd42b71d2306ff503d8c7c9ee1bb
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
index 46b370f..d1f908d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
@@ -16,27 +16,16 @@
 
 package com.android.systemui.lifecycle
 
-import android.view.View
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.Assert
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.kotlin.argumentCaptor
-import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.stub
-import org.mockito.kotlin.verify
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -121,45 +110,4 @@
 
         assertThat(isActive).isFalse()
     }
-
-    @Test
-    fun viewModel_viewBinder() = runTest {
-        Assert.setTestThread(Thread.currentThread())
-
-        val view: View = mock { on { isAttachedToWindow } doReturn false }
-        val viewModel = FakeViewModel()
-        backgroundScope.launch {
-            view.viewModel(
-                minWindowLifecycleState = WindowLifecycleState.ATTACHED,
-                factory = { viewModel },
-            ) {
-                awaitCancellation()
-            }
-        }
-        runCurrent()
-
-        assertThat(viewModel.isActivated).isFalse()
-
-        view.stub { on { isAttachedToWindow } doReturn true }
-        argumentCaptor<View.OnAttachStateChangeListener>()
-            .apply { verify(view).addOnAttachStateChangeListener(capture()) }
-            .allValues
-            .forEach { it.onViewAttachedToWindow(view) }
-        runCurrent()
-
-        assertThat(viewModel.isActivated).isTrue()
-    }
-}
-
-private class FakeViewModel : SysUiViewModel() {
-    var isActivated = false
-
-    override suspend fun onActivated() {
-        isActivated = true
-        try {
-            awaitCancellation()
-        } finally {
-            isActivated = false
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
index c2b5d98..661da6d 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
@@ -227,33 +227,13 @@
 }
 
 /**
- * Runs the given [block] in a new coroutine when `this` [View]'s Window's [WindowLifecycleState] is
- * at least at [state] (or immediately after calling this function if the window is already at least
- * at [state]), automatically canceling the work when the window is no longer at least at that
- * state.
- *
- * [block] may be run multiple times, running once per every time this` [View]'s Window's
- * [WindowLifecycleState] becomes at least at [state].
- */
-suspend fun View.repeatOnWindowLifecycle(
-    state: WindowLifecycleState,
-    block: suspend CoroutineScope.() -> Unit,
-): Nothing {
-    when (state) {
-        WindowLifecycleState.ATTACHED -> repeatWhenAttachedToWindow(block)
-        WindowLifecycleState.VISIBLE -> repeatWhenWindowIsVisible(block)
-        WindowLifecycleState.FOCUSED -> repeatWhenWindowHasFocus(block)
-    }
-}
-
-/**
  * Runs the given [block] every time the [View] becomes attached (or immediately after calling this
  * function, if the view was already attached), automatically canceling the work when the view
  * becomes detached.
  *
  * Only use from the main thread.
  *
- * [block] may be run multiple times, running once per every time the view is attached.
+ * The [block] may be run multiple times, running once per every time the view is attached.
  */
 @MainThread
 suspend fun View.repeatWhenAttachedToWindow(block: suspend CoroutineScope.() -> Unit): Nothing {
@@ -269,7 +249,7 @@
  *
  * Only use from the main thread.
  *
- * [block] may be run multiple times, running once per every time the window becomes visible.
+ * The [block] may be run multiple times, running once per every time the window becomes visible.
  */
 @MainThread
 suspend fun View.repeatWhenWindowIsVisible(block: suspend CoroutineScope.() -> Unit): Nothing {
@@ -285,7 +265,7 @@
  *
  * Only use from the main thread.
  *
- * [block] may be run multiple times, running once per every time the window is focused.
+ * The [block] may be run multiple times, running once per every time the window is focused.
  */
 @MainThread
 suspend fun View.repeatWhenWindowHasFocus(block: suspend CoroutineScope.() -> Unit): Nothing {
@@ -294,21 +274,6 @@
     awaitCancellation() // satisfies return type of Nothing
 }
 
-/** Lifecycle states for a [View]'s interaction with a [android.view.Window]. */
-enum class WindowLifecycleState {
-    /** Indicates that the [View] is attached to a [android.view.Window]. */
-    ATTACHED,
-    /**
-     * Indicates that the [View] is attached to a [android.view.Window], and the window is visible.
-     */
-    VISIBLE,
-    /**
-     * Indicates that the [View] is attached to a [android.view.Window], and the window is visible
-     * and focused.
-     */
-    FOCUSED
-}
-
 private val View.isAttached
     get() = conflatedCallbackFlow {
         val onAttachListener =
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
index 7731481..0af5fea 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
@@ -16,10 +16,9 @@
 
 package com.android.systemui.lifecycle
 
-import android.view.View
 import androidx.compose.runtime.Composable
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
 
 /** Base class for all System UI view-models. */
 abstract class SysUiViewModel : SafeActivatable() {
@@ -38,20 +37,8 @@
 fun <T : SysUiViewModel> rememberViewModel(
     key: Any = Unit,
     factory: () -> T,
-): T = rememberActivated(key, factory)
-
-/**
- * Invokes [block] in a new coroutine with a new [SysUiViewModel] that is automatically activated
- * whenever `this` [View]'s Window's [WindowLifecycleState] is at least at
- * [minWindowLifecycleState], and is automatically canceled once that is no longer the case.
- */
-suspend fun <T : SysUiViewModel> View.viewModel(
-    minWindowLifecycleState: WindowLifecycleState,
-    factory: () -> T,
-    block: suspend CoroutineScope.(T) -> Unit,
-): Nothing =
-    repeatOnWindowLifecycle(minWindowLifecycleState) {
-        val instance = factory()
-        launch { instance.activate() }
-        block(instance)
-    }
+): T {
+    val instance = remember(key) { factory() }
+    LaunchedEffect(instance) { instance.activate() }
+    return instance
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index a30b877..fd08e89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -17,14 +17,14 @@
 package com.android.systemui.statusbar.notification.stack.ui.viewbinder
 
 import android.util.Log
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.common.ui.view.onLayoutChanged
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.lifecycle.WindowLifecycleState
 import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.viewModel
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationScrollViewModel
@@ -33,6 +33,7 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.launch
@@ -45,7 +46,7 @@
     dumpManager: DumpManager,
     @Main private val mainImmediateDispatcher: CoroutineDispatcher,
     private val view: NotificationScrollView,
-    private val viewModelFactory: NotificationScrollViewModel.Factory,
+    private val viewModel: NotificationScrollViewModel,
     private val configuration: ConfigurationState,
 ) : FlowDumperImpl(dumpManager) {
 
@@ -60,42 +61,38 @@
     }
 
     fun bindWhileAttached(): DisposableHandle {
-        return view.asView().repeatWhenAttached(mainImmediateDispatcher) { bind() }
+        return view.asView().repeatWhenAttached(mainImmediateDispatcher) {
+            repeatOnLifecycle(Lifecycle.State.CREATED) { bind() }
+        }
     }
 
-    suspend fun bind(): Nothing =
-        view.asView().viewModel(
-            minWindowLifecycleState = WindowLifecycleState.ATTACHED,
-            factory = viewModelFactory::create,
-        ) { viewModel ->
-            launchAndDispose {
-                updateViewPosition()
-                view.asView().onLayoutChanged { updateViewPosition() }
-            }
+    suspend fun bind() = coroutineScope {
+        launchAndDispose {
+            updateViewPosition()
+            view.asView().onLayoutChanged { updateViewPosition() }
+        }
 
-            launch {
-                viewModel
-                    .shadeScrimShape(cornerRadius = scrimRadius, viewLeftOffset = viewLeftOffset)
-                    .collect { view.setScrimClippingShape(it) }
-            }
+        launch {
+            viewModel
+                .shadeScrimShape(cornerRadius = scrimRadius, viewLeftOffset = viewLeftOffset)
+                .collect { view.setScrimClippingShape(it) }
+        }
 
-            launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
-            launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
-            launch {
-                viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) }
-            }
-            launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
-            launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
+        launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
+        launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
+        launch { viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) } }
+        launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
+        launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
 
-            launchAndDispose {
-                view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
-                view.setCurrentGestureOverscrollConsumer(viewModel.currentGestureOverscrollConsumer)
-                DisposableHandle {
-                    view.setSyntheticScrollConsumer(null)
-                    view.setCurrentGestureOverscrollConsumer(null)
-                }
+        launchAndDispose {
+            view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
+            view.setCurrentGestureOverscrollConsumer(viewModel.currentGestureOverscrollConsumer)
+            DisposableHandle {
+                view.setSyntheticScrollConsumer(null)
+                view.setCurrentGestureOverscrollConsumer(null)
             }
         }
+    }
 
     /** flow of the scrim clipping radius */
     private val scrimRadius: Flow<Int>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 4281025..2ba79a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -19,9 +19,9 @@
 
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.lifecycle.SysUiViewModel
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.SceneFamilies
@@ -33,11 +33,9 @@
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_DELAYED_STACK_FADE_IN
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA
-import com.android.systemui.util.kotlin.FlowDumper
 import com.android.systemui.util.kotlin.FlowDumperImpl
 import dagger.Lazy
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
+import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -45,8 +43,9 @@
 import kotlinx.coroutines.flow.map
 
 /** ViewModel which represents the state of the NSSL/Controller in the world of flexiglass */
+@SysUISingleton
 class NotificationScrollViewModel
-@AssistedInject
+@Inject
 constructor(
     dumpManager: DumpManager,
     stackAppearanceInteractor: NotificationStackAppearanceInteractor,
@@ -55,9 +54,7 @@
     // TODO(b/336364825) Remove Lazy when SceneContainerFlag is released -
     // while the flag is off, creating this object too early results in a crash
     keyguardInteractor: Lazy<KeyguardInteractor>,
-) : FlowDumper by FlowDumperImpl(dumpManager, "NotificationScrollViewModel"),
-    SysUiViewModel() {
-
+) : FlowDumperImpl(dumpManager) {
     /**
      * The expansion fraction of the notification stack. It should go from 0 to 1 when transitioning
      * from Gone to Shade scenes, and remain at 1 when in Lockscreen or Shade scenes and while
@@ -189,9 +186,4 @@
             keyguardInteractor.get().isDozing.dumpWhileCollecting("isDozing")
         }
     }
-
-    @AssistedFactory
-    interface Factory {
-        fun create(): NotificationScrollViewModel
-    }
 }