[QSDetailedView] Implements the details view/ViewModels for qs_new_tiles

Apply this new pattern to the internet details view as an example for
the other details view.

Flag: com.android.systemui.qs_tile_detailed_view
Test: InternetTileUserActionInteractorTest, QSTileViewModelTest
Bug: b/381941621
Change-Id: Ic791a5db1f9ba46e5974edbed4a7396f4aa126d3
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
index 52c476e..e4a9888 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.nullable
 import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -136,4 +137,13 @@
 
             verify(wifiStateWorker, times(1)).isWifiEnabled = eq(true)
         }
+
+    @Test
+    fun detailsViewModel() =
+        kosmos.testScope.runTest {
+            assertThat(underTest.detailsViewModel.getTitle())
+                .isEqualTo("Internet")
+            assertThat(underTest.detailsViewModel.getSubTitle())
+                .isEqualTo("Tab a network to connect")
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
index 954215ee..2edb9c6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
@@ -173,6 +173,21 @@
                 .isEqualTo(FakeQSTileDataInteractor.AvailabilityRequest(USER))
         }
 
+    @Test
+    fun tileDetails() =
+        testScope.runTest {
+            assertThat(tileUserActionInteractor.detailsViewModel).isNotNull()
+            assertThat(tileUserActionInteractor.detailsViewModel?.getTitle())
+                .isEqualTo("FakeQSTileUserActionInteractor")
+            assertThat(underTest.detailsViewModel).isNotNull()
+            assertThat(underTest.detailsViewModel?.getTitle())
+                .isEqualTo("FakeQSTileUserActionInteractor")
+
+            tileUserActionInteractor.detailsViewModel = null
+            assertThat(tileUserActionInteractor.detailsViewModel).isNull()
+            assertThat(underTest.detailsViewModel).isNull()
+        }
+
     private fun createViewModel(
         scope: TestScope,
         config: QSTileConfig = tileConfig,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
index 17b78eb..e8c4274 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.tiles.base.interactor
 
 import android.annotation.WorkerThread
+import com.android.systemui.plugins.qs.TileDetailsViewModel
 
 interface QSTileUserActionInteractor<DATA_TYPE> {
     /**
@@ -27,4 +28,17 @@
      * It's safe to run long running computations inside this function.
      */
     @WorkerThread suspend fun handleInput(input: QSTileInput<DATA_TYPE>)
+
+    /**
+     * Provides the [TileDetailsViewModel] for constructing the corresponding details view.
+     *
+     * This property is defined here to reuse the business logic. For example, reusing the user
+     * long-click as the go-to-settings callback in the details view.
+     * Subclasses can override this property to provide a specific [TileDetailsViewModel]
+     * implementation.
+     *
+     * @return The [TileDetailsViewModel] instance, or null if not implemented.
+     */
+    val detailsViewModel: TileDetailsViewModel?
+        get() = null
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
index aeb6cef..224fa10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
@@ -20,6 +20,7 @@
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.Dumpable
 import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.TileDetailsViewModel
 import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
@@ -115,6 +116,9 @@
             .flowOn(backgroundDispatcher)
             .stateIn(tileScope, SharingStarted.WhileSubscribed(), true)
 
+    override val detailsViewModel: TileDetailsViewModel?
+        get() = userActionInteractor().detailsViewModel
+
     override fun forceUpdate() {
         tileScope.launch(context = backgroundDispatcher) { forceUpdates.emit(Unit) }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
index a963b28..fdb15b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
@@ -18,15 +18,23 @@
 
 import android.content.Intent
 import android.provider.Settings
+import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.qs.TileDetailsViewModel
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.interactor.QSTileInput
 import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.dialog.InternetDetailsViewModel
 import com.android.systemui.qs.tiles.dialog.InternetDialogManager
 import com.android.systemui.qs.tiles.dialog.WifiStateWorker
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
 import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.statusbar.connectivity.AccessPointController
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.stateIn
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.withContext
@@ -61,11 +69,18 @@
                     wifiStateWorker.isWifiEnabled = !wifiStateWorker.isWifiEnabled
                 }
                 is QSTileUserAction.LongClick -> {
-                    qsTileIntentUserActionHandler.handle(
-                        action.expandable,
-                        Intent(Settings.ACTION_WIFI_SETTINGS)
-                    )
+                    handleLongClick(action.expandable)
                 }
             }
         }
+
+    override val detailsViewModel: TileDetailsViewModel =
+        InternetDetailsViewModel { handleLongClick(null) }
+
+    private fun handleLongClick(expandable:Expandable?){
+        qsTileIntentUserActionHandler.handle(
+            expandable,
+            Intent(Settings.ACTION_WIFI_SETTINGS)
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
index b1b0001..e8b9926 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.qs.tiles.viewmodel
 
 import android.os.UserHandle
+import com.android.systemui.plugins.qs.TileDetailsViewModel
 import kotlinx.coroutines.flow.StateFlow
 
 /**
@@ -37,6 +38,10 @@
     /** Specifies whether this device currently supports this tile. */
     val isAvailable: StateFlow<Boolean>
 
+    /** Specifies the [TileDetailsViewModel] for constructing the corresponding details view. */
+    val detailsViewModel: TileDetailsViewModel?
+        get() = null
+
     /**
      * Notifies about the user change. Implementations should avoid using 3rd party userId sources
      * and use this value instead. This is to maintain consistent and concurrency-free behaviour
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index f9a1ad5..47d0f71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.UiBackground
 import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.qs.TileDetailsViewModel
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon
 import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
@@ -154,6 +155,10 @@
         qsTileViewModel.onUserChanged(UserHandle.of(currentUser))
     }
 
+    override fun getDetailsViewModel(): TileDetailsViewModel? {
+        return qsTileViewModel.detailsViewModel
+    }
+
     @Deprecated(
         "Not needed as {@link com.android.internal.logging.UiEvent} will use #getMetricsSpec",
         replaceWith = ReplaceWith("getMetricsSpec"),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
index 597d52d..bc1c60c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs.tiles.base.interactor
 
+import com.android.systemui.plugins.qs.TileDetailsViewModel
+import com.android.systemui.qs.FakeTileDetailsViewModel
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 
@@ -31,4 +33,7 @@
     override suspend fun handleInput(input: QSTileInput<T>) {
         mutex.withLock { mutableInputs.add(input) }
     }
+
+    override var detailsViewModel: TileDetailsViewModel? =
+        FakeTileDetailsViewModel("FakeQSTileUserActionInteractor")
 }