1 /*
2  * 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
18 
19 import android.content.ComponentName
20 import android.content.Context
21 import androidx.annotation.GuardedBy
22 import com.android.systemui.dagger.SysUISingleton
23 import com.android.systemui.dagger.qualifiers.Application
24 import com.android.systemui.dump.DumpManager
25 import com.android.systemui.plugins.qs.QSTile
26 import com.android.systemui.qs.external.TileServiceRequestController
27 import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository.Companion.POSITION_AT_END
28 import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
29 import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
30 import com.android.systemui.qs.pipeline.shared.TileSpec
31 import javax.inject.Inject
32 import kotlinx.coroutines.CoroutineScope
33 import kotlinx.coroutines.Job
34 import kotlinx.coroutines.launch
35 
36 /**
37  * Adapter to determine what real class to use for classes that depend on [QSHost].
38  * * When [QSPipelineFlagsRepository.pipelineEnabled] is false, all calls will be routed to
39  *   [QSTileHost].
40  * * When [QSPipelineFlagsRepository.pipelineEnabled] is true, calls regarding the current set of
41  *   tiles will be routed to [CurrentTilesInteractor]. Other calls (like [createTileView]) will
42  *   still be routed to [QSTileHost].
43  *
44  * This routing also includes dumps.
45  */
46 @SysUISingleton
47 class QSHostAdapter
48 @Inject
49 constructor(
50     private val qsTileHost: QSTileHost,
51     private val interactor: CurrentTilesInteractor,
52     private val context: Context,
53     private val tileServiceRequestControllerBuilder: TileServiceRequestController.Builder,
54     @Application private val scope: CoroutineScope,
55     flags: QSPipelineFlagsRepository,
56     dumpManager: DumpManager,
57 ) : QSHost {
58 
59     companion object {
60         private const val TAG = "QSTileHost"
61     }
62 
63     private val useNewHost = flags.pipelineEnabled
64 
65     @GuardedBy("callbacksMap") private val callbacksMap = mutableMapOf<QSHost.Callback, Job>()
66 
67     init {
<lambda>null68         scope.launch { tileServiceRequestControllerBuilder.create(this@QSHostAdapter).init() }
69         // Redirect dump to the correct host (needed for CTS tests)
70         dumpManager.registerCriticalDumpable(TAG, if (useNewHost) interactor else qsTileHost)
71     }
72 
getTilesnull73     override fun getTiles(): Collection<QSTile> {
74         return if (useNewHost) {
75             interactor.currentQSTiles
76         } else {
77             qsTileHost.getTiles()
78         }
79     }
80 
getSpecsnull81     override fun getSpecs(): List<String> {
82         return if (useNewHost) {
83             interactor.currentTilesSpecs.map { it.spec }
84         } else {
85             qsTileHost.getSpecs()
86         }
87     }
88 
removeTilenull89     override fun removeTile(spec: String) {
90         if (useNewHost) {
91             interactor.removeTiles(listOf(TileSpec.create(spec)))
92         } else {
93             qsTileHost.removeTile(spec)
94         }
95     }
96 
addCallbacknull97     override fun addCallback(callback: QSHost.Callback) {
98         if (useNewHost) {
99             val job = scope.launch { interactor.currentTiles.collect { callback.onTilesChanged() } }
100             synchronized(callbacksMap) { callbacksMap.put(callback, job) }
101         } else {
102             qsTileHost.addCallback(callback)
103         }
104     }
105 
removeCallbacknull106     override fun removeCallback(callback: QSHost.Callback) {
107         if (useNewHost) {
108             synchronized(callbacksMap) { callbacksMap.remove(callback)?.cancel() }
109         } else {
110             qsTileHost.removeCallback(callback)
111         }
112     }
113 
removeTilesnull114     override fun removeTiles(specs: Collection<String>) {
115         if (useNewHost) {
116             interactor.removeTiles(specs.map(TileSpec::create))
117         } else {
118             qsTileHost.removeTiles(specs)
119         }
120     }
121 
removeTileByUsernull122     override fun removeTileByUser(component: ComponentName) {
123         if (useNewHost) {
124             interactor.removeTiles(listOf(TileSpec.create(component)))
125         } else {
126             qsTileHost.removeTileByUser(component)
127         }
128     }
129 
addTilenull130     override fun addTile(spec: String, position: Int) {
131         if (useNewHost) {
132             interactor.addTile(TileSpec.create(spec), position)
133         } else {
134             qsTileHost.addTile(spec, position)
135         }
136     }
137 
addTilenull138     override fun addTile(component: ComponentName, end: Boolean) {
139         if (useNewHost) {
140             interactor.addTile(TileSpec.create(component), if (end) POSITION_AT_END else 0)
141         } else {
142             qsTileHost.addTile(component, end)
143         }
144     }
145 
changeTilesByUsernull146     override fun changeTilesByUser(previousTiles: List<String>, newTiles: List<String>) {
147         if (useNewHost) {
148             interactor.setTiles(newTiles.map(TileSpec::create))
149         } else {
150             qsTileHost.changeTilesByUser(previousTiles, newTiles)
151         }
152     }
153 
getContextnull154     override fun getContext(): Context {
155         return if (useNewHost) {
156             context
157         } else {
158             qsTileHost.context
159         }
160     }
161 
getUserContextnull162     override fun getUserContext(): Context {
163         return if (useNewHost) {
164             interactor.userContext.value
165         } else {
166             qsTileHost.userContext
167         }
168     }
169 
getUserIdnull170     override fun getUserId(): Int {
171         return if (useNewHost) {
172             interactor.userId.value
173         } else {
174             qsTileHost.userId
175         }
176     }
177 
createTilenull178     override fun createTile(tileSpec: String): QSTile? {
179         return qsTileHost.createTile(tileSpec)
180     }
181 
addTilenull182     override fun addTile(spec: String) {
183         return addTile(spec, QSHost.POSITION_AT_END)
184     }
185 
addTilenull186     override fun addTile(tile: ComponentName) {
187         return addTile(tile, false)
188     }
189 
indexOfnull190     override fun indexOf(tileSpec: String): Int {
191         return specs.indexOf(tileSpec)
192     }
193 }
194