Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ed2a7758 authored by Anton Potapov's avatar Anton Potapov
Browse files

Several tweaks to smoothen tile creation experience

 - Add QSTileState builder to start a state from a config
 - Improve QSTileViewModelImpl bahviour when data interactor doesn't
   listen to the triggers
 - Better naming

Bug: 299908705
Test: atest QSTielViewModelInterfaceComplianceTest
Flag: LEGACY QS_PIPELINE_NEW_TILES DISABLED
Change-Id: Id20c4bc43000e738cee7d76f24b90673e579648c
parent 7a17d789
Loading
Loading
Loading
Loading
+13 −9
Original line number Diff line number Diff line
@@ -39,7 +39,6 @@ import kotlinx.coroutines.CoroutineScope
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.SharedFlow
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.onEach
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 @@ class QSTileViewModelImpl<DATA_TYPE>(

    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 @@ class QSTileViewModelImpl<DATA_TYPE>(
            tileData.replayCache.isNotEmpty(),
            state.replayCache.isNotEmpty()
        )
        userInputs.tryEmit(userAction)
        tileScope.launch { userInputs.emit(userAction) }
    }

    override fun destroy() {
@@ -151,11 +151,16 @@ class QSTileViewModelImpl<DATA_TYPE>(
                            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 @@ class QSTileViewModelImpl<DATA_TYPE>(
     *
     * 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 @@ class QSTileViewModelImpl<DATA_TYPE>(
            }
            .onEach { userActionInteractor().handleInput(it.input) }
            .flowOn(backgroundDispatcher)
    }

    private fun Flow<QSTileUserAction>.filterByPolicy(user: UserHandle): Flow<QSTileUserAction> =
        config.policy.let { policy ->
+6 −6
Original line number Diff line number Diff line
@@ -36,9 +36,9 @@ data class QSTileConfig(
 */
sealed interface QSTileUIConfig {

    val tileIconRes: Int
    val iconRes: Int
        @DrawableRes get
    val tileLabelRes: Int
    val labelRes: Int
        @StringRes get

    /**
@@ -46,16 +46,16 @@ sealed interface QSTileUIConfig {
     * 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
}

+12 −3
Original line number Diff line number Diff line
@@ -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 @@ data class QSTileState(

    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) {
+1 −1
Original line number Diff line number Diff line
@@ -192,7 +192,7 @@ constructor(
        with(qsTileViewModel.config.uiConfig) {
            when (this) {
                is QSTileUIConfig.Empty -> qsTileViewModel.currentState?.label ?: ""
                is QSTileUIConfig.Resource -> context.getString(tileLabelRes)
                is QSTileUIConfig.Resource -> context.getString(labelRes)
            }
        }

+2 −2
Original line number Diff line number Diff line
@@ -117,7 +117,7 @@ class QSTileLoggerTest : SysuiTestCase() {
        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 @@ class QSTileLoggerTest : SysuiTestCase() {
    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",
        )

Loading