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