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.customization.picker.clock.data.repository 18 19 import android.provider.Settings 20 import androidx.annotation.ColorInt 21 import androidx.annotation.IntRange 22 import com.android.customization.picker.clock.shared.ClockSize 23 import com.android.customization.picker.clock.shared.model.ClockMetadataModel 24 import com.android.systemui.plugins.ClockMetadata 25 import com.android.systemui.shared.clocks.ClockRegistry 26 import com.android.wallpaper.settings.data.repository.SecureSettingsRepository 27 import kotlinx.coroutines.CoroutineScope 28 import kotlinx.coroutines.ExperimentalCoroutinesApi 29 import kotlinx.coroutines.channels.awaitClose 30 import kotlinx.coroutines.delay 31 import kotlinx.coroutines.flow.Flow 32 import kotlinx.coroutines.flow.SharedFlow 33 import kotlinx.coroutines.flow.SharingStarted 34 import kotlinx.coroutines.flow.callbackFlow 35 import kotlinx.coroutines.flow.map 36 import kotlinx.coroutines.flow.mapLatest 37 import kotlinx.coroutines.flow.mapNotNull 38 import kotlinx.coroutines.flow.shareIn 39 import org.json.JSONObject 40 41 /** Implementation of [ClockPickerRepository], using [ClockRegistry]. */ 42 class ClockPickerRepositoryImpl( 43 private val secureSettingsRepository: SecureSettingsRepository, 44 private val registry: ClockRegistry, 45 scope: CoroutineScope, 46 ) : ClockPickerRepository { 47 48 @OptIn(ExperimentalCoroutinesApi::class) 49 override val allClocks: Flow<List<ClockMetadataModel>> = 50 callbackFlow { 51 fun send() { 52 val allClocks = 53 registry 54 .getClocks() 55 .filter { "NOT_IN_USE" !in it.clockId } 56 .map { it.toModel() } 57 trySend(allClocks) 58 } 59 60 val listener = 61 object : ClockRegistry.ClockChangeListener { 62 override fun onAvailableClocksChanged() { 63 send() 64 } 65 } 66 registry.registerClockChangeListener(listener) 67 send() 68 awaitClose { registry.unregisterClockChangeListener(listener) } 69 } 70 .mapLatest { allClocks -> 71 // Loading list of clock plugins can cause many consecutive calls of 72 // onAvailableClocksChanged(). We only care about the final fully-initiated clock 73 // list. Delay to avoid unnecessary too many emits. 74 delay(100) 75 allClocks 76 } 77 78 /** The currently-selected clock. This also emits the clock color information. */ 79 override val selectedClock: Flow<ClockMetadataModel> = 80 callbackFlow { 81 fun send() { 82 val currentClockId = registry.currentClockId 83 val metadata = registry.settings?.metadata 84 val model = 85 registry 86 .getClocks() 87 .find { clockMetadata -> clockMetadata.clockId == currentClockId } 88 ?.toModel( 89 selectedColorId = metadata?.getSelectedColorId(), 90 colorTone = metadata?.getColorTone() 91 ?: ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS, 92 seedColor = registry.seedColor 93 ) 94 trySend(model) 95 } 96 97 val listener = 98 object : ClockRegistry.ClockChangeListener { 99 override fun onCurrentClockChanged() { 100 send() 101 } 102 103 override fun onAvailableClocksChanged() { 104 send() 105 } 106 } 107 registry.registerClockChangeListener(listener) 108 send() 109 awaitClose { registry.unregisterClockChangeListener(listener) } 110 } 111 .mapNotNull { it } 112 113 override fun setSelectedClock(clockId: String) { 114 registry.mutateSetting { oldSettings -> 115 val newSettings = oldSettings.copy(clockId = clockId) 116 newSettings.metadata = oldSettings.metadata 117 newSettings 118 } 119 } 120 121 override fun setClockColor( 122 selectedColorId: String?, 123 @IntRange(from = 0, to = 100) colorToneProgress: Int, 124 @ColorInt seedColor: Int?, 125 ) { 126 registry.mutateSetting { oldSettings -> 127 val newSettings = oldSettings.copy(seedColor = seedColor) 128 newSettings.metadata = 129 oldSettings.metadata 130 .put(KEY_METADATA_SELECTED_COLOR_ID, selectedColorId) 131 .put(KEY_METADATA_COLOR_TONE_PROGRESS, colorToneProgress) 132 newSettings 133 } 134 } 135 136 override val selectedClockSize: SharedFlow<ClockSize> = 137 secureSettingsRepository 138 .intSetting( 139 name = Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 140 ) 141 .map { setting -> setting == 1 } 142 .map { isDynamic -> if (isDynamic) ClockSize.DYNAMIC else ClockSize.SMALL } 143 .shareIn( 144 scope = scope, 145 started = SharingStarted.WhileSubscribed(), 146 replay = 1, 147 ) 148 149 override suspend fun setClockSize(size: ClockSize) { 150 secureSettingsRepository.set( 151 name = Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 152 value = if (size == ClockSize.DYNAMIC) 1 else 0, 153 ) 154 } 155 156 private fun JSONObject.getSelectedColorId(): String? { 157 return if (this.isNull(KEY_METADATA_SELECTED_COLOR_ID)) { 158 null 159 } else { 160 this.getString(KEY_METADATA_SELECTED_COLOR_ID) 161 } 162 } 163 164 private fun JSONObject.getColorTone(): Int { 165 return this.optInt( 166 KEY_METADATA_COLOR_TONE_PROGRESS, 167 ClockMetadataModel.DEFAULT_COLOR_TONE_PROGRESS 168 ) 169 } 170 171 /** By default, [ClockMetadataModel] has no color information unless specified. */ 172 private fun ClockMetadata.toModel( 173 selectedColorId: String? = null, 174 @IntRange(from = 0, to = 100) colorTone: Int = 0, 175 @ColorInt seedColor: Int? = null, 176 ): ClockMetadataModel { 177 return ClockMetadataModel( 178 clockId = clockId, 179 name = name, 180 selectedColorId = selectedColorId, 181 colorToneProgress = colorTone, 182 seedColor = seedColor, 183 ) 184 } 185 186 companion object { 187 // The selected color in the color option list 188 private const val KEY_METADATA_SELECTED_COLOR_ID = "metadataSelectedColorId" 189 190 // The color tone to apply to the selected color 191 private const val KEY_METADATA_COLOR_TONE_PROGRESS = "metadataColorToneProgress" 192 } 193 } 194