diff options
6 files changed, 128 insertions, 107 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt index 6ad4b317b94c..314631823e96 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt @@ -675,6 +675,24 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { assertThat(tiles!!.size).isEqualTo(3) } + @Test + fun changeInPackagesTiles_doesntTriggerUserChange_logged() = + testScope.runTest(USER_INFO_0) { + val specs = + listOf( + TileSpec.create("a"), + ) + tileSpecRepository.setTiles(USER_INFO_0.id, specs) + runCurrent() + // Settled on the same list of tiles. + assertThat(underTest.currentTilesSpecs).isEqualTo(specs) + + installedTilesPackageRepository.setInstalledPackagesForUser(USER_INFO_0.id, emptySet()) + runCurrent() + + verify(logger, never()).logTileUserChanged(TileSpec.create("a"), 0) + } + private fun QSTile.State.fillIn(state: Int, label: CharSequence, secondaryLabel: CharSequence) { this.state = state this.label = label diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt index 97b5e87d7167..02379e6efecc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt @@ -46,7 +46,7 @@ import com.android.systemui.qs.toProto import com.android.systemui.retail.data.repository.RetailModeRepository import com.android.systemui.settings.UserTracker import com.android.systemui.user.data.repository.UserRepository -import com.android.systemui.util.kotlin.pairwise +import com.android.systemui.util.kotlin.pairwiseBy import dagger.Lazy import java.io.PrintWriter import javax.inject.Inject @@ -63,7 +63,6 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -169,17 +168,19 @@ constructor( private val userAndTiles = currentUser .flatMapLatest { userId -> - tileSpecRepository.tilesSpecs(userId).map { UserAndTiles(userId, it) } + val currentTiles = tileSpecRepository.tilesSpecs(userId) + val installedComponents = + installedTilesComponentRepository.getInstalledTilesComponents(userId) + currentTiles.combine(installedComponents) { tiles, components -> + UserTilesAndComponents(userId, tiles, components) + } } .distinctUntilChanged() - .pairwise(UserAndTiles(-1, emptyList())) + .pairwiseBy(UserTilesAndComponents(-1, emptyList(), emptySet())) { prev, new -> + DataWithUserChange(data = new, userChange = prev.userId != new.userId) + } .flowOn(backgroundDispatcher) - private val installedPackagesWithTiles = - currentUser.flatMapLatest { - installedTilesComponentRepository.getInstalledTilesComponents(it) - } - private val minTiles: Int get() = if (retailModeRepository.inRetailMode) { @@ -194,7 +195,6 @@ constructor( } } - @OptIn(ExperimentalCoroutinesApi::class) private fun startTileCollection() { scope.launch { launch { @@ -205,95 +205,82 @@ constructor( } launch(backgroundDispatcher) { - userAndTiles - .combine(installedPackagesWithTiles) { usersAndTiles, packages -> - Data( - usersAndTiles.previousValue, - usersAndTiles.newValue, - packages, - ) - } - .collectLatest { - val newTileList = it.newData.tiles - val userChanged = it.oldData.userId != it.newData.userId - val newUser = it.newData.userId - val components = it.installedComponents - - // Destroy all tiles that are not in the new set - specsToTiles - .filter { - it.key !in newTileList && it.value is TileOrNotInstalled.Tile - } - .forEach { entry -> - logger.logTileDestroyed( - entry.key, - if (userChanged) { - QSPipelineLogger.TileDestroyedReason - .TILE_NOT_PRESENT_IN_NEW_USER - } else { - QSPipelineLogger.TileDestroyedReason.TILE_REMOVED - } - ) - (entry.value as TileOrNotInstalled.Tile).tile.destroy() - } - // MutableMap will keep the insertion order - val newTileMap = mutableMapOf<TileSpec, TileOrNotInstalled>() - - newTileList.forEach { tileSpec -> - if (tileSpec !in newTileMap) { - if ( - tileSpec is TileSpec.CustomTileSpec && - tileSpec.componentName !in components - ) { - newTileMap[tileSpec] = TileOrNotInstalled.NotInstalled + userAndTiles.collectLatest { + val newUser = it.userId + val newTileList = it.tiles + val components = it.installedComponents + val userChanged = it.userChange + + // Destroy all tiles that are not in the new set + specsToTiles + .filter { it.key !in newTileList && it.value is TileOrNotInstalled.Tile } + .forEach { entry -> + logger.logTileDestroyed( + entry.key, + if (userChanged) { + QSPipelineLogger.TileDestroyedReason + .TILE_NOT_PRESENT_IN_NEW_USER } else { - // Create tile here will never try to create a CustomTile that - // is not installed - val newTile = - if (tileSpec in specsToTiles) { - processExistingTile( - tileSpec, - specsToTiles.getValue(tileSpec), - userChanged, - newUser - ) - ?: createTile(tileSpec) - } else { - createTile(tileSpec) - } - if (newTile != null) { - newTileMap[tileSpec] = TileOrNotInstalled.Tile(newTile) + QSPipelineLogger.TileDestroyedReason.TILE_REMOVED + } + ) + (entry.value as TileOrNotInstalled.Tile).tile.destroy() + } + // MutableMap will keep the insertion order + val newTileMap = mutableMapOf<TileSpec, TileOrNotInstalled>() + + newTileList.forEach { tileSpec -> + if (tileSpec !in newTileMap) { + if ( + tileSpec is TileSpec.CustomTileSpec && + tileSpec.componentName !in components + ) { + newTileMap[tileSpec] = TileOrNotInstalled.NotInstalled + } else { + // Create tile here will never try to create a CustomTile that + // is not installed + val newTile = + if (tileSpec in specsToTiles) { + processExistingTile( + tileSpec, + specsToTiles.getValue(tileSpec), + userChanged, + newUser + ) ?: createTile(tileSpec) + } else { + createTile(tileSpec) } + if (newTile != null) { + newTileMap[tileSpec] = TileOrNotInstalled.Tile(newTile) } } } + } - val resolvedSpecs = newTileMap.keys.toList() - specsToTiles.clear() - specsToTiles.putAll(newTileMap) - val newResolvedTiles = - newTileMap - .filter { it.value is TileOrNotInstalled.Tile } - .map { - TileModel(it.key, (it.value as TileOrNotInstalled.Tile).tile) - } - - _currentSpecsAndTiles.value = newResolvedTiles - logger.logTilesNotInstalled( - newTileMap.filter { it.value is TileOrNotInstalled.NotInstalled }.keys, - newUser - ) - if (newResolvedTiles.size < minTiles) { - // We ended up with not enough tiles (some may be not installed). - // Prepend the default set of tiles - launch { tileSpecRepository.prependDefault(currentUser.value) } - } else if (resolvedSpecs != newTileList) { - // There were some tiles that couldn't be created. Change the value in - // the - // repository - launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) } - } + val resolvedSpecs = newTileMap.keys.toList() + specsToTiles.clear() + specsToTiles.putAll(newTileMap) + val newResolvedTiles = + newTileMap + .filter { it.value is TileOrNotInstalled.Tile } + .map { TileModel(it.key, (it.value as TileOrNotInstalled.Tile).tile) } + + _currentSpecsAndTiles.value = newResolvedTiles + logger.logTilesNotInstalled( + newTileMap.filter { it.value is TileOrNotInstalled.NotInstalled }.keys, + newUser + ) + if (newResolvedTiles.size < minTiles) { + // We ended up with not enough tiles (some may be not installed). + // Prepend the default set of tiles + launch { tileSpecRepository.prependDefault(currentUser.value) } + } else if (resolvedSpecs != newTileList) { + // There were some tiles that couldn't be created. Change the value in + // the + // repository + launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) } } + } } } } @@ -362,8 +349,7 @@ constructor( newQSTileFactory.get().createTile(spec.spec) } else { null - } - ?: tileFactory.createTile(spec.spec) + } ?: tileFactory.createTile(spec.spec) } if (tile == null) { logger.logTileNotFoundInFactory(spec) @@ -436,15 +422,25 @@ constructor( @JvmInline value class Tile(val tile: QSTile) : TileOrNotInstalled } +} - private data class UserAndTiles( - val userId: Int, - val tiles: List<TileSpec>, - ) - - private data class Data( - val oldData: UserAndTiles, - val newData: UserAndTiles, - val installedComponents: Set<ComponentName>, +private data class UserTilesAndComponents( + val userId: Int, + val tiles: List<TileSpec>, + val installedComponents: Set<ComponentName> +) + +private data class DataWithUserChange( + val userId: Int, + val tiles: List<TileSpec>, + val installedComponents: Set<ComponentName>, + val userChange: Boolean, +) + +private fun DataWithUserChange(data: UserTilesAndComponents, userChange: Boolean) = + DataWithUserChange( + data.userId, + data.tiles, + data.installedComponents, + userChange, ) -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java index d9546ec6ac51..1750347fd2ae 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java @@ -106,7 +106,8 @@ public class WorkModeTile extends QSTileImpl<BooleanState> implements @Override @MainThread public void onManagedProfileRemoved() { - mHost.removeTile(getTileSpec()); + // No OP as this may race with the user change in CurrentTilesInteractor. + // If the tile needs to be removed, AutoAdd (or AutoTileManager) will take care of that. } @Override 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 e9e9d8b0bbfc..cdcefdb50b0f 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 @@ -22,12 +22,15 @@ import androidx.annotation.StringRes import com.android.internal.logging.InstanceId import com.android.systemui.qs.pipeline.shared.TileSpec -data class QSTileConfig( +data class QSTileConfig +@JvmOverloads +constructor( val tileSpec: TileSpec, val uiConfig: QSTileUIConfig, val instanceId: InstanceId, val metricsSpec: String = tileSpec.spec, val policy: QSTilePolicy = QSTilePolicy.NoRestrictions, + val autoRemoveOnUnavailable: Boolean = true, ) /** @@ -38,6 +41,7 @@ sealed interface QSTileUIConfig { val iconRes: Int @DrawableRes get + val labelRes: Int @StringRes get @@ -48,6 +52,7 @@ sealed interface QSTileUIConfig { data object Empty : QSTileUIConfig { override val iconRes: Int get() = Resources.ID_NULL + override val labelRes: Int get() = Resources.ID_NULL } 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 2cdcc24d149f..c6f9ae8f4463 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 @@ -70,7 +70,7 @@ constructor( applicationScope.launch { launch { qsTileViewModel.isAvailable.collectIndexed { index, isAvailable -> - if (!isAvailable) { + if (!isAvailable && qsTileViewModel.config.autoRemoveOnUnavailable) { qsHost.removeTile(tileSpec) } // qsTileViewModel.isAvailable flow often starts with isAvailable == true. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt index f6934095b29d..21ec14fc7f03 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt @@ -289,6 +289,7 @@ interface PolicyModule { labelRes = R.string.quick_settings_work_mode_label, ), instanceId = uiEventLogger.getNewInstanceId(), + autoRemoveOnUnavailable = false, ) /** Inject work mode into tileViewModelMap in QSModule */ |