Merge "Several tweaks to smoothen tile creation experience" into main
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 0bee48f..12a083e 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
@@ -39,7 +39,6 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.SupervisorJob
 import kotlinx.coroutines.cancel
-import kotlinx.coroutines.channels.BufferOverflow
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -47,6 +46,8 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.cancellable
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOn
@@ -57,6 +58,7 @@
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
 
 /**
  * Provides a hassle-free way to implement new tiles according to current System UI architecture
@@ -83,10 +85,8 @@
 
     private val users: MutableStateFlow<UserHandle> =
         MutableStateFlow(userRepository.getSelectedUserInfo().userHandle)
-    private val userInputs: MutableSharedFlow<QSTileUserAction> =
-        MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
-    private val forceUpdates: MutableSharedFlow<Unit> =
-        MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+    private val userInputs: MutableSharedFlow<QSTileUserAction> = MutableSharedFlow()
+    private val forceUpdates: MutableSharedFlow<Unit> = MutableSharedFlow()
     private val spec
         get() = config.tileSpec
 
@@ -130,7 +130,7 @@
             tileData.replayCache.isNotEmpty(),
             state.replayCache.isNotEmpty()
         )
-        userInputs.tryEmit(userAction)
+        tileScope.launch { userInputs.emit(userAction) }
     }
 
     override fun destroy() {
@@ -151,11 +151,16 @@
                             emit(DataUpdateTrigger.InitialRequest)
                             qsTileLogger.logInitialRequest(spec)
                         }
+                        .shareIn(tileScope, SharingStarted.WhileSubscribed())
                 tileDataInteractor()
                     .tileData(user, updateTriggers)
+                    // combine makes sure updateTriggers is always listened even if
+                    // tileDataInteractor#tileData doesn't flatMapLatest on it
+                    .combine(updateTriggers) { data, _ -> data }
                     .cancellable()
                     .flowOn(backgroundDispatcher)
             }
+            .distinctUntilChanged()
             .shareIn(
                 tileScope,
                 SharingStarted.WhileSubscribed(),
@@ -171,8 +176,8 @@
      *
      * Subscribing to the result flow twice will result in doubling all actions, logs and analytics.
      */
-    private fun userInputFlow(user: UserHandle): Flow<DataUpdateTrigger> {
-        return userInputs
+    private fun userInputFlow(user: UserHandle): Flow<DataUpdateTrigger> =
+        userInputs
             .filterFalseActions()
             .filterByPolicy(user)
             .throttle(CLICK_THROTTLE_DURATION, systemClock)
@@ -187,7 +192,6 @@
             }
             .onEach { userActionInteractor().handleInput(it.input) }
             .flowOn(backgroundDispatcher)
-    }
 
     private fun Flow<QSTileUserAction>.filterByPolicy(user: UserHandle): Flow<QSTileUserAction> =
         config.policy.let { policy ->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
index c4d7dfb..18a4e2d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
@@ -36,9 +36,9 @@
  */
 sealed interface QSTileUIConfig {
 
-    val tileIconRes: Int
+    val iconRes: Int
         @DrawableRes get
-    val tileLabelRes: Int
+    val labelRes: Int
         @StringRes get
 
     /**
@@ -46,16 +46,16 @@
      * of [Resource]. Returns [Resources.ID_NULL] for each field.
      */
     data object Empty : QSTileUIConfig {
-        override val tileIconRes: Int
+        override val iconRes: Int
             get() = Resources.ID_NULL
-        override val tileLabelRes: Int
+        override val labelRes: Int
             get() = Resources.ID_NULL
     }
 
     /** Config containing actual icon and label resources. */
     data class Resource(
-        @StringRes override val tileIconRes: Int,
-        @StringRes override val tileLabelRes: Int,
+        @DrawableRes override val iconRes: Int,
+        @StringRes override val labelRes: Int,
     ) : QSTileUIConfig
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index dc5cccc..30b87cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles.viewmodel
 
+import android.content.Context
 import android.service.quicksettings.Tile
 import com.android.systemui.common.shared.model.Icon
 
@@ -41,11 +42,19 @@
 
     companion object {
 
+        fun build(
+            context: Context,
+            config: QSTileUIConfig,
+            build: Builder.() -> Unit
+        ): QSTileState =
+            build(
+                { Icon.Resource(config.iconRes, null) },
+                context.getString(config.labelRes),
+                build,
+            )
+
         fun build(icon: () -> Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState =
             Builder(icon, label).apply(build).build()
-
-        fun build(icon: Icon, label: CharSequence, build: Builder.() -> Unit): QSTileState =
-            build({ icon }, label, build)
     }
 
     enum class ActivationState(val legacyState: Int) {
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 efa6da7..771d07c 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
@@ -192,7 +192,7 @@
         with(qsTileViewModel.config.uiConfig) {
             when (this) {
                 is QSTileUIConfig.Empty -> qsTileViewModel.currentState?.label ?: ""
-                is QSTileUIConfig.Resource -> context.getString(tileLabelRes)
+                is QSTileUIConfig.Resource -> context.getString(labelRes)
             }
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index fdde56f..8f27e4e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -117,7 +117,7 @@
         underTest.logUserActionPipeline(
             TileSpec.create("test_spec"),
             QSTileUserAction.Click(null),
-            QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {},
+            QSTileState.build({ Icon.Resource(0, ContentDescription.Resource(0)) }, "") {},
             "test_data",
         )
 
@@ -143,7 +143,7 @@
     fun testLogStateUpdate() {
         underTest.logStateUpdate(
             TileSpec.create("test_spec"),
-            QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {},
+            QSTileState.build({ Icon.Resource(0, ContentDescription.Resource(0)) }, "") {},
             "test_data",
         )
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
index 9bf4a75..d3b7daa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelInterfaceComplianceTest.kt
@@ -97,7 +97,10 @@
             {
                 object : QSTileDataToStateMapper<Any> {
                     override fun map(config: QSTileConfig, data: Any): QSTileState =
-                        QSTileState.build(Icon.Resource(0, ContentDescription.Resource(0)), "") {}
+                        QSTileState.build(
+                            { Icon.Resource(0, ContentDescription.Resource(0)) },
+                            ""
+                        ) {}
                 }
             },
             fakeDisabledByPolicyInteractor,