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.pipeline.domain.interactor 18 19 import com.android.systemui.Dumpable 20 import com.android.systemui.dagger.SysUISingleton 21 import com.android.systemui.dagger.qualifiers.Application 22 import com.android.systemui.dump.DumpManager 23 import com.android.systemui.qs.pipeline.data.repository.AutoAddRepository 24 import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal 25 import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking 26 import com.android.systemui.qs.pipeline.domain.model.AutoAddable 27 import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger 28 import com.android.systemui.util.asIndenting 29 import com.android.systemui.util.indentIfPossible 30 import java.io.PrintWriter 31 import java.util.concurrent.atomic.AtomicBoolean 32 import javax.inject.Inject 33 import kotlinx.coroutines.CoroutineScope 34 import kotlinx.coroutines.coroutineScope 35 import kotlinx.coroutines.flow.collectLatest 36 import kotlinx.coroutines.flow.emptyFlow 37 import kotlinx.coroutines.flow.filterIsInstance 38 import kotlinx.coroutines.flow.merge 39 import kotlinx.coroutines.flow.stateIn 40 import kotlinx.coroutines.flow.take 41 import kotlinx.coroutines.launch 42 43 /** 44 * Collects the signals coming from all registered [AutoAddable] and adds/removes tiles accordingly. 45 */ 46 @SysUISingleton 47 class AutoAddInteractor 48 @Inject 49 constructor( 50 private val autoAddables: Set<@JvmSuppressWildcards AutoAddable>, 51 private val repository: AutoAddRepository, 52 private val dumpManager: DumpManager, 53 private val qsPipelineLogger: QSPipelineLogger, 54 @Application private val scope: CoroutineScope, 55 ) : Dumpable { 56 57 private val initialized = AtomicBoolean(false) 58 59 /** Start collection of signals following the user from [currentTilesInteractor]. */ 60 fun init(currentTilesInteractor: CurrentTilesInteractor) { 61 if (!initialized.compareAndSet(false, true)) { 62 return 63 } 64 65 dumpManager.registerNormalDumpable(TAG, this) 66 67 scope.launch { 68 currentTilesInteractor.userId.collectLatest { userId -> 69 coroutineScope { 70 val previouslyAdded = repository.autoAddedTiles(userId).stateIn(this) 71 72 autoAddables 73 .map { addable -> 74 val autoAddSignal = addable.autoAddSignal(userId) 75 when (val lifecycle = addable.autoAddTracking) { 76 is AutoAddTracking.Always -> autoAddSignal 77 is AutoAddTracking.Disabled -> emptyFlow() 78 is AutoAddTracking.IfNotAdded -> { 79 if (lifecycle.spec !in previouslyAdded.value) { 80 autoAddSignal.filterIsInstance<AutoAddSignal.Add>().take(1) 81 } else { 82 emptyFlow() 83 } 84 } 85 } 86 } 87 .merge() 88 .collect { signal -> 89 when (signal) { 90 is AutoAddSignal.Add -> { 91 if (signal.spec !in previouslyAdded.value) { 92 currentTilesInteractor.addTile(signal.spec, signal.position) 93 qsPipelineLogger.logTileAutoAdded( 94 userId, 95 signal.spec, 96 signal.position 97 ) 98 repository.markTileAdded(userId, signal.spec) 99 } 100 } 101 is AutoAddSignal.Remove -> { 102 currentTilesInteractor.removeTiles(setOf(signal.spec)) 103 qsPipelineLogger.logTileAutoRemoved(userId, signal.spec) 104 repository.unmarkTileAdded(userId, signal.spec) 105 } 106 } 107 } 108 } 109 } 110 } 111 } 112 113 override fun dump(pw: PrintWriter, args: Array<out String>) { 114 with(pw.asIndenting()) { 115 println("AutoAddables:") 116 indentIfPossible { autoAddables.forEach { println(it.description) } } 117 } 118 } 119 120 companion object { 121 private const val TAG = "AutoAddInteractor" 122 } 123 } 124