• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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