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