diff options
| author | 2024-07-01 16:58:59 +0000 | |
|---|---|---|
| committer | 2024-07-01 16:58:59 +0000 | |
| commit | bd4dca1d1fadb46332494bedb620b993f4ef6e58 (patch) | |
| tree | 6429089883c1bf4b68de1215efc574318106567c | |
| parent | 0081c7ebc6cb013f9798f437be0a549a0f9e1bd3 (diff) | |
| parent | 8d8635ddf11d9d6e091e5e04c68e39749d9e3b6d (diff) | |
Merge "fix performance regression on qs_new_tiles" into main
13 files changed, 80 insertions, 49 deletions
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 9ce2e0f87499..c33e2a49ef5d 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 @@ -92,7 +92,8 @@ class QSTileViewModelTest : SysuiTestCase() { runCurrent() assertThat(states()).isNotEmpty() - assertThat(states().first().label).isEqualTo(testTileData) + assertThat(states().last()).isNotNull() + assertThat(states().last()!!.label).isEqualTo(testTileData) verify(qsTileLogger).logInitialRequest(eq(tileConfig.tileSpec)) } @@ -196,6 +197,7 @@ class QSTileViewModelTest : SysuiTestCase() { qsTileLogger, FakeSystemClock(), testCoroutineDispatcher, + testCoroutineDispatcher, scope.backgroundScope, ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt index 6066d24c2214..7955f2fc1335 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt @@ -256,6 +256,7 @@ class QSTileViewModelUserInputTest : SysuiTestCase() { qsTileLogger, FakeSystemClock(), testCoroutineDispatcher, + testCoroutineDispatcher, scope.backgroundScope, ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt index 4c210804b0c0..8c75cf001441 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles.base.viewmodel import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.plugins.FalsingManager import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics @@ -61,6 +62,7 @@ sealed interface QSTileViewModelFactory<T> { private val qsTileConfigProvider: QSTileConfigProvider, private val systemClock: SystemClock, @Background private val backgroundDispatcher: CoroutineDispatcher, + @UiBackground private val uiBackgroundDispatcher: CoroutineDispatcher, private val customTileComponentBuilder: CustomTileComponent.Builder, ) : QSTileViewModelFactory<CustomTileDataModel> { @@ -86,6 +88,7 @@ sealed interface QSTileViewModelFactory<T> { qsTileLogger, systemClock, backgroundDispatcher, + uiBackgroundDispatcher, component.coroutineScope(), ) } @@ -106,6 +109,7 @@ sealed interface QSTileViewModelFactory<T> { private val qsTileConfigProvider: QSTileConfigProvider, private val systemClock: SystemClock, @Background private val backgroundDispatcher: CoroutineDispatcher, + @UiBackground private val uiBackgroundDispatcher: CoroutineDispatcher, private val coroutineScopeFactory: QSTileCoroutineScopeFactory, ) : QSTileViewModelFactory<T> { @@ -136,6 +140,7 @@ sealed interface QSTileViewModelFactory<T> { qsTileLogger, systemClock, backgroundDispatcher, + uiBackgroundDispatcher, coroutineScopeFactory.create(), ) } 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 8782524cf250..9e84f01c6bc4 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 @@ -40,6 +40,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -59,7 +60,9 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext /** * Provides a hassle-free way to implement new tiles according to current System UI architecture @@ -81,6 +84,7 @@ class QSTileViewModelImpl<DATA_TYPE>( private val qsTileLogger: QSTileLogger, private val systemClock: SystemClock, private val backgroundDispatcher: CoroutineDispatcher, + uiBackgroundDispatcher: CoroutineDispatcher, private val tileScope: CoroutineScope, ) : QSTileViewModel, Dumpable { @@ -93,18 +97,16 @@ class QSTileViewModelImpl<DATA_TYPE>( private val tileData: SharedFlow<DATA_TYPE> = createTileDataFlow() - override val state: SharedFlow<QSTileState> = + override val state: StateFlow<QSTileState?> = tileData .map { data -> - mapper().map(config, data).also { state -> - qsTileLogger.logStateUpdate(spec, state, data) - } + withContext(uiBackgroundDispatcher) { mapper().map(config, data) } + .also { state -> qsTileLogger.logStateUpdate(spec, state, data) } } - .flowOn(backgroundDispatcher) - .shareIn( + .stateIn( tileScope, SharingStarted.WhileSubscribed(), - replay = 1, + null, ) override val isAvailable: StateFlow<Boolean> = users @@ -147,26 +149,26 @@ class QSTileViewModelImpl<DATA_TYPE>( private fun createTileDataFlow(): SharedFlow<DATA_TYPE> = users - .flatMapLatest { user -> - val updateTriggers = - merge( - userInputFlow(user), - forceUpdates - .map { DataUpdateTrigger.ForceUpdate } - .onEach { qsTileLogger.logForceUpdate(spec) }, - ) - .onStart { - 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) + .transformLatest { user -> + coroutineScope { + val updateTriggers: Flow<DataUpdateTrigger> = + merge( + userInputFlow(user), + forceUpdates + .map { DataUpdateTrigger.ForceUpdate } + .onEach { qsTileLogger.logForceUpdate(spec) }, + ) + .onStart { qsTileLogger.logInitialRequest(spec) } + .stateIn(this, SharingStarted.Eagerly, DataUpdateTrigger.InitialRequest) + tileDataInteractor() + .tileData(user, updateTriggers) + // combine makes sure updateTriggers is always listened even if + // tileDataInteractor#tileData doesn't transformLatest on it + .combine(updateTriggers) { data, _ -> data } + .cancellable() + .flowOn(backgroundDispatcher) + .collect { emit(it) } + } } .distinctUntilChanged() .shareIn( diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt index 1e8ce588b4e0..233e913a27aa 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt @@ -30,11 +30,9 @@ import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.flow.stateIn /** Observes one qr scanner state changes providing the [QRCodeScannerTileModel]. */ class QRCodeScannerTileDataInteractor @@ -66,11 +64,6 @@ constructor( } .onStart { emit(generateModel()) } .flowOn(bgCoroutineContext) - .stateIn( - scope, - SharingStarted.WhileSubscribed(), - QRCodeScannerTileModel.TemporarilyUnavailable - ) override fun availability(user: UserHandle): Flow<Boolean> = flowOf(qrController.isCameraAvailable) 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 ae6c0143f603..30247c41200f 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 @@ -54,18 +54,21 @@ data class QSTileState( resources: Resources, theme: Theme, config: QSTileUIConfig, - build: Builder.() -> Unit + builder: Builder.() -> Unit ): QSTileState { val iconDrawable = resources.getDrawable(config.iconRes, theme) return build( { Icon.Loaded(iconDrawable, null) }, resources.getString(config.labelRes), - build, + builder, ) } - fun build(icon: () -> Icon?, label: CharSequence, build: Builder.() -> Unit): QSTileState = - Builder(icon, label).apply(build).build() + fun build( + icon: () -> Icon?, + label: CharSequence, + builder: Builder.() -> Unit + ): QSTileState = Builder(icon, label).apply { builder() }.build() } enum class ActivationState(val legacyState: Int) { @@ -107,7 +110,9 @@ data class QSTileState( sealed interface SideViewIcon { data class Custom(val icon: Icon) : SideViewIcon + data object Chevron : SideViewIcon + data object None : SideViewIcon } 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 226e2fa0549f..b1b0001b6361 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,7 +17,6 @@ package com.android.systemui.qs.tiles.viewmodel import android.os.UserHandle -import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow /** @@ -31,7 +30,7 @@ import kotlinx.coroutines.flow.StateFlow interface QSTileViewModel { /** State of the tile to be shown by the view. */ - val state: SharedFlow<QSTileState> + val state: StateFlow<QSTileState?> val config: QSTileConfig @@ -62,9 +61,7 @@ interface QSTileViewModel { } /** - * Returns the immediate state of the tile or null if the state haven't been collected yet. Favor - * reactive consumption over the [currentState], because there is no guarantee that current value - * would be available at any time. + * Returns the immediate state of the tile or null if the state haven't been collected yet. */ val QSTileViewModel.currentState: QSTileState? - get() = state.replayCache.lastOrNull() + get() = state.value 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 7be13e08f972..ba0a8d694a14 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 @@ -38,6 +38,7 @@ import java.util.function.Supplier import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collectIndexed +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map @@ -168,6 +169,7 @@ constructor( if (listeningClients.size == 1) { stateJob = qsTileViewModel.state + .filterNotNull() .map { mapState(context, it, qsTileViewModel.config) } .onEach { legacyState -> synchronized(callbacks) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt index 64f1fd3d6416..00b7e61eb1c6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt @@ -17,12 +17,11 @@ package com.android.systemui.qs.tiles.viewmodel import android.os.UserHandle -import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow object StubQSTileViewModel : QSTileViewModel { - override val state: SharedFlow<QSTileState> + override val state: StateFlow<QSTileState?> get() = error("Don't call stubs") override val config: QSTileConfig diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt index d10554f9c254..b836016eb2ef 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/SysUICoroutinesModule.kt @@ -20,13 +20,16 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Tracing +import com.android.systemui.dagger.qualifiers.UiBackground import dagger.Module import dagger.Provides +import java.util.concurrent.Executor import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.newFixedThreadPoolContext import kotlinx.coroutines.plus @@ -83,4 +86,25 @@ class SysUICoroutinesModule { ): CoroutineContext { return bgCoroutineDispatcher + tracingCoroutineContext } + + /** Coroutine dispatcher for background operations on for UI. */ + @Provides + @SysUISingleton + @UiBackground + @Deprecated( + "Use @UiBackground CoroutineContext instead", + ReplaceWith("uiBgCoroutineContext()", "kotlin.coroutines.CoroutineContext") + ) + fun uiBgDispatcher(@UiBackground uiBgExecutor: Executor): CoroutineDispatcher = + uiBgExecutor.asCoroutineDispatcher() + + @Provides + @UiBackground + @SysUISingleton + fun uiBgCoroutineContext( + @Tracing tracingCoroutineContext: CoroutineContext, + @UiBackground uiBgCoroutineDispatcher: CoroutineDispatcher, + ): CoroutineContext { + return uiBgCoroutineDispatcher + tracingCoroutineContext + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt index 42b81de92af7..c918ed82604c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt @@ -97,6 +97,7 @@ class QSTileViewModelImplTest : SysuiTestCase() { qsTileLogger, FakeSystemClock(), testCoroutineDispatcher, + testCoroutineDispatcher, testScope.backgroundScope, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt index 419e7810b604..dceb8bff0ae7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt @@ -33,7 +33,6 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import javax.inject.Provider import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow var Kosmos.newFactoryTileMap by Kosmos.Fixture { emptyMap<String, Provider<QSTileViewModel>>() } @@ -50,7 +49,7 @@ val Kosmos.customTileViewModelFactory: QSTileViewModelFactory.Component by instanceIdSequenceFake.newInstanceId(), ) object : QSTileViewModel { - override val state: SharedFlow<QSTileState> = + override val state: StateFlow<QSTileState?> = MutableStateFlow(QSTileState.build({ null }, tileSpec.spec) {}) override val config: QSTileConfig = config override val isAvailable: StateFlow<Boolean> = MutableStateFlow(true) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt index dcfcce77942e..537be4fc2527 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt @@ -69,6 +69,7 @@ val Kosmos.qsQRCodeScannerViewModel by qsTileLogger, systemClock, testDispatcher, + testDispatcher, testScope.backgroundScope, ) } |