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.keyguard.data.repository 18 19 import android.content.Context 20 import android.os.UserHandle 21 import android.provider.Settings 22 import com.android.keyguard.ClockEventController 23 import com.android.systemui.dagger.SysUISingleton 24 import com.android.systemui.dagger.qualifiers.Application 25 import com.android.systemui.dagger.qualifiers.Background 26 import com.android.systemui.flags.FeatureFlagsClassic 27 import com.android.systemui.flags.Flags 28 import com.android.systemui.keyguard.shared.model.ClockSize 29 import com.android.systemui.keyguard.shared.model.ClockSizeSetting 30 import com.android.systemui.plugins.clocks.ClockController 31 import com.android.systemui.plugins.clocks.ClockId 32 import com.android.systemui.res.R 33 import com.android.systemui.scene.shared.flag.SceneContainerFlag 34 import com.android.systemui.shared.clocks.ClockRegistry 35 import com.android.systemui.util.settings.SecureSettings 36 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow 37 import javax.inject.Inject 38 import kotlinx.coroutines.CoroutineDispatcher 39 import kotlinx.coroutines.CoroutineScope 40 import kotlinx.coroutines.channels.awaitClose 41 import kotlinx.coroutines.flow.Flow 42 import kotlinx.coroutines.flow.MutableStateFlow 43 import kotlinx.coroutines.flow.SharingStarted 44 import kotlinx.coroutines.flow.StateFlow 45 import kotlinx.coroutines.flow.asStateFlow 46 import kotlinx.coroutines.flow.callbackFlow 47 import kotlinx.coroutines.flow.map 48 import kotlinx.coroutines.flow.mapNotNull 49 import kotlinx.coroutines.flow.onStart 50 import kotlinx.coroutines.flow.stateIn 51 import kotlinx.coroutines.withContext 52 53 interface KeyguardClockRepository { 54 /** 55 * clock size determined by notificationPanelViewController, LARGE or SMALL 56 * 57 * @deprecated When scene container flag is on use clockSize from domain level. 58 */ 59 val clockSize: StateFlow<ClockSize> 60 61 /** clock size selected in picker, DYNAMIC or SMALL */ 62 val selectedClockSize: StateFlow<ClockSizeSetting> 63 64 /** clock id, selected from clock carousel in wallpaper picker */ 65 val currentClockId: Flow<ClockId> 66 67 val currentClock: StateFlow<ClockController?> 68 69 val previewClock: Flow<ClockController> 70 71 val clockEventController: ClockEventController 72 73 val shouldForceSmallClock: Boolean 74 setClockSizenull75 fun setClockSize(size: ClockSize) 76 } 77 78 @SysUISingleton 79 class KeyguardClockRepositoryImpl 80 @Inject 81 constructor( 82 private val secureSettings: SecureSettings, 83 private val clockRegistry: ClockRegistry, 84 override val clockEventController: ClockEventController, 85 @Background private val backgroundDispatcher: CoroutineDispatcher, 86 @Application private val applicationScope: CoroutineScope, 87 @Application private val applicationContext: Context, 88 private val featureFlags: FeatureFlagsClassic, 89 ) : KeyguardClockRepository { 90 91 /** Receive SMALL or LARGE clock should be displayed on keyguard. */ 92 private val _clockSize: MutableStateFlow<ClockSize> = MutableStateFlow(ClockSize.LARGE) 93 override val clockSize: StateFlow<ClockSize> = _clockSize.asStateFlow() 94 95 override fun setClockSize(size: ClockSize) { 96 SceneContainerFlag.assertInLegacyMode() 97 _clockSize.value = size 98 } 99 100 override val selectedClockSize: StateFlow<ClockSizeSetting> = 101 secureSettings 102 .observerFlow( 103 names = arrayOf(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK), 104 userId = UserHandle.USER_SYSTEM, 105 ) 106 .onStart { emit(Unit) } // Forces an initial update. 107 .map { withContext(backgroundDispatcher) { getClockSize() } } 108 .stateIn( 109 scope = applicationScope, 110 started = SharingStarted.WhileSubscribed(), 111 initialValue = getClockSize() 112 ) 113 114 override val currentClockId: Flow<ClockId> = 115 callbackFlow { 116 fun send() { 117 trySend(clockRegistry.currentClockId) 118 } 119 120 val listener = 121 object : ClockRegistry.ClockChangeListener { 122 override fun onCurrentClockChanged() { 123 send() 124 } 125 } 126 clockRegistry.registerClockChangeListener(listener) 127 send() 128 awaitClose { clockRegistry.unregisterClockChangeListener(listener) } 129 } 130 .mapNotNull { it } 131 132 override val currentClock: StateFlow<ClockController?> = 133 currentClockId 134 .map { 135 clockEventController.clock = clockRegistry.createCurrentClock() 136 clockEventController.clock 137 } 138 .stateIn( 139 scope = applicationScope, 140 started = SharingStarted.WhileSubscribed(), 141 initialValue = clockRegistry.createCurrentClock() 142 ) 143 144 override val previewClock: Flow<ClockController> = 145 currentClockId.map { 146 // We should create a new instance for each collect call 147 // cause in preview, the same clock will be attached to different view 148 // at the same time 149 clockRegistry.createCurrentClock() 150 } 151 152 override val shouldForceSmallClock: Boolean 153 get() = 154 featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE) && 155 // True on small landscape screens 156 applicationContext.resources.getBoolean(R.bool.force_small_clock_on_lockscreen) 157 158 private fun getClockSize(): ClockSizeSetting { 159 return ClockSizeSetting.fromSettingValue( 160 secureSettings.getIntForUser( 161 Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 162 /* defaultValue= */ 1, 163 UserHandle.USER_CURRENT 164 ) 165 ) 166 } 167 } 168