1 /* <lambda>null2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.qs.pipeline.data.repository 18 19 import android.annotation.UserIdInt 20 import android.content.res.Resources 21 import android.util.SparseArray 22 import com.android.systemui.dagger.SysUISingleton 23 import com.android.systemui.dagger.qualifiers.Main 24 import com.android.systemui.qs.pipeline.data.model.RestoreData 25 import com.android.systemui.qs.pipeline.shared.TileSpec 26 import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger 27 import com.android.systemui.res.R 28 import com.android.systemui.retail.data.repository.RetailModeRepository 29 import javax.inject.Inject 30 import kotlinx.coroutines.ExperimentalCoroutinesApi 31 import kotlinx.coroutines.flow.Flow 32 import kotlinx.coroutines.flow.flatMapLatest 33 import kotlinx.coroutines.flow.flowOf 34 35 /** Repository that tracks the current tiles. */ 36 interface TileSpecRepository { 37 38 /** 39 * Returns a flow of the current list of [TileSpec] for a given [userId]. 40 * 41 * Tiles will never be [TileSpec.Invalid] in the list and it will never be empty. 42 */ 43 suspend fun tilesSpecs(@UserIdInt userId: Int): Flow<List<TileSpec>> 44 45 /** 46 * Adds a [tile] for a given [userId] at [position]. Using [POSITION_AT_END] will add the tile 47 * at the end of the list. 48 * 49 * Passing [TileSpec.Invalid] is a noop. 50 * 51 * Trying to add a tile beyond the end of the list will add it at the end. 52 */ 53 suspend fun addTile(@UserIdInt userId: Int, tile: TileSpec, position: Int = POSITION_AT_END) 54 55 /** 56 * Removes a [tile] for a given [userId]. 57 * 58 * Passing [TileSpec.Invalid] or a non present tile is a noop. 59 */ 60 suspend fun removeTiles(@UserIdInt userId: Int, tiles: Collection<TileSpec>) 61 62 /** 63 * Sets the list of current [tiles] for a given [userId]. 64 * 65 * [TileSpec.Invalid] will be ignored, and an effectively empty list will not be stored. 66 */ 67 suspend fun setTiles(@UserIdInt userId: Int, tiles: List<TileSpec>) 68 69 suspend fun reconcileRestore(restoreData: RestoreData, currentAutoAdded: Set<TileSpec>) 70 71 /** Prepend the default list of tiles to the current set of tiles */ 72 suspend fun prependDefault(@UserIdInt userId: Int) 73 74 companion object { 75 /** Position to indicate the end of the list */ 76 const val POSITION_AT_END = -1 77 } 78 } 79 80 /** 81 * Implementation of [TileSpecRepository] that delegates to an instance of [UserTileSpecRepository] 82 * for each user. 83 * 84 * If the device is in retail mode, the tiles are fixed to the value of 85 * [R.string.quick_settings_tiles_retail_mode]. 86 */ 87 @OptIn(ExperimentalCoroutinesApi::class) 88 @SysUISingleton 89 class TileSpecSettingsRepository 90 @Inject 91 constructor( 92 @Main private val resources: Resources, 93 private val logger: QSPipelineLogger, 94 private val retailModeRepository: RetailModeRepository, 95 private val userTileSpecRepositoryFactory: UserTileSpecRepository.Factory, 96 ) : TileSpecRepository { 97 <lambda>null98 private val retailModeTiles by lazy { 99 resources 100 .getString(R.string.quick_settings_tiles_retail_mode) 101 .split(DELIMITER) 102 .map(TileSpec::create) 103 .filter { it !is TileSpec.Invalid } 104 } 105 106 private val userTileRepositories = SparseArray<UserTileSpecRepository>() 107 tilesSpecsnull108 override suspend fun tilesSpecs(userId: Int): Flow<List<TileSpec>> { 109 if (userId !in userTileRepositories) { 110 val userTileRepository = userTileSpecRepositoryFactory.create(userId) 111 userTileRepositories.put(userId, userTileRepository) 112 } 113 val realTiles = userTileRepositories.get(userId).tiles() 114 115 return retailModeRepository.retailMode.flatMapLatest { inRetailMode -> 116 if (inRetailMode) { 117 logger.logUsingRetailTiles() 118 flowOf(retailModeTiles) 119 } else { 120 realTiles 121 } 122 } 123 } 124 addTilenull125 override suspend fun addTile(userId: Int, tile: TileSpec, position: Int) { 126 if (retailModeRepository.inRetailMode) { 127 return 128 } 129 if (tile is TileSpec.Invalid) { 130 return 131 } 132 userTileRepositories.get(userId)?.addTile(tile, position) 133 } 134 removeTilesnull135 override suspend fun removeTiles(userId: Int, tiles: Collection<TileSpec>) { 136 if (retailModeRepository.inRetailMode) { 137 return 138 } 139 userTileRepositories.get(userId)?.removeTiles(tiles) 140 } 141 setTilesnull142 override suspend fun setTiles(userId: Int, tiles: List<TileSpec>) { 143 if (retailModeRepository.inRetailMode) { 144 return 145 } 146 userTileRepositories.get(userId)?.setTiles(tiles) 147 } 148 reconcileRestorenull149 override suspend fun reconcileRestore( 150 restoreData: RestoreData, 151 currentAutoAdded: Set<TileSpec> 152 ) { 153 userTileRepositories 154 .get(restoreData.userId) 155 ?.reconcileRestore(restoreData, currentAutoAdded) 156 } 157 prependDefaultnull158 override suspend fun prependDefault( 159 userId: Int, 160 ) { 161 if (retailModeRepository.inRetailMode) { 162 return 163 } 164 userTileRepositories.get(userId)?.prependDefault() 165 } 166 167 companion object { 168 private const val DELIMITER = TilesSettingConverter.DELIMITER 169 } 170 } 171