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,