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.data.repository 18 19 import android.database.ContentObserver 20 import android.provider.Settings 21 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 22 import com.android.systemui.dagger.SysUISingleton 23 import com.android.systemui.dagger.qualifiers.Background 24 import com.android.systemui.qs.pipeline.shared.TileSpec 25 import com.android.systemui.util.settings.SecureSettings 26 import javax.inject.Inject 27 import kotlinx.coroutines.CoroutineDispatcher 28 import kotlinx.coroutines.channels.awaitClose 29 import kotlinx.coroutines.flow.Flow 30 import kotlinx.coroutines.flow.distinctUntilChanged 31 import kotlinx.coroutines.flow.flowOn 32 import kotlinx.coroutines.flow.map 33 import kotlinx.coroutines.flow.onStart 34 import kotlinx.coroutines.withContext 35 36 /** Repository to track what QS tiles have been auto-added */ 37 interface AutoAddRepository { 38 39 /** Flow of tiles that have been auto-added */ 40 fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>> 41 42 /** Mark a tile as having been auto-added */ 43 suspend fun markTileAdded(userId: Int, spec: TileSpec) 44 45 /** 46 * Unmark a tile as having been auto-added. This is used for tiles that can be auto-added 47 * multiple times. 48 */ 49 suspend fun unmarkTileAdded(userId: Int, spec: TileSpec) 50 } 51 52 /** 53 * Implementation that tracks the auto-added tiles stored in [Settings.Secure.QS_AUTO_ADDED_TILES]. 54 */ 55 @SysUISingleton 56 class AutoAddSettingRepository 57 @Inject 58 constructor( 59 private val secureSettings: SecureSettings, 60 @Background private val bgDispatcher: CoroutineDispatcher, 61 ) : AutoAddRepository { 62 override fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>> { 63 return conflatedCallbackFlow { 64 val observer = 65 object : ContentObserver(null) { 66 override fun onChange(selfChange: Boolean) { 67 trySend(Unit) 68 } 69 } 70 71 secureSettings.registerContentObserverForUser(SETTING, observer, userId) 72 73 awaitClose { secureSettings.unregisterContentObserver(observer) } 74 } 75 .onStart { emit(Unit) } 76 .map { secureSettings.getStringForUser(SETTING, userId) ?: "" } 77 .distinctUntilChanged() 78 .map { 79 it.split(DELIMITER).map(TileSpec::create).filter { it !is TileSpec.Invalid }.toSet() 80 } 81 .flowOn(bgDispatcher) 82 } 83 84 override suspend fun markTileAdded(userId: Int, spec: TileSpec) { 85 if (spec is TileSpec.Invalid) { 86 return 87 } 88 val added = load(userId).toMutableSet() 89 if (added.add(spec)) { 90 store(userId, added) 91 } 92 } 93 94 override suspend fun unmarkTileAdded(userId: Int, spec: TileSpec) { 95 if (spec is TileSpec.Invalid) { 96 return 97 } 98 val added = load(userId).toMutableSet() 99 if (added.remove(spec)) { 100 store(userId, added) 101 } 102 } 103 104 private suspend fun store(userId: Int, tiles: Set<TileSpec>) { 105 val toStore = 106 tiles 107 .filter { it !is TileSpec.Invalid } 108 .joinToString(DELIMITER, transform = TileSpec::spec) 109 withContext(bgDispatcher) { 110 secureSettings.putStringForUser( 111 SETTING, 112 toStore, 113 null, 114 false, 115 userId, 116 true, 117 ) 118 } 119 } 120 121 private suspend fun load(userId: Int): Set<TileSpec> { 122 return withContext(bgDispatcher) { 123 (secureSettings.getStringForUser(SETTING, userId) ?: "") 124 .split(",") 125 .map(TileSpec::create) 126 .filter { it !is TileSpec.Invalid } 127 .toSet() 128 } 129 } 130 131 companion object { 132 private const val SETTING = Settings.Secure.QS_AUTO_ADDED_TILES 133 private const val DELIMITER = "," 134 } 135 } 136