• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2024 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.communal.data.repository
18 
19 import android.app.admin.DevicePolicyManager
20 import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
21 import android.content.IntentFilter
22 import android.content.pm.UserInfo
23 import android.content.res.Resources
24 import android.os.UserHandle
25 import android.provider.Settings
26 import com.android.systemui.Flags.communalHub
27 import com.android.systemui.Flags.glanceableHubV2
28 import com.android.systemui.broadcast.BroadcastDispatcher
29 import com.android.systemui.communal.data.model.CommunalFeature
30 import com.android.systemui.communal.data.model.FEATURE_ALL
31 import com.android.systemui.communal.data.model.SuppressionReason
32 import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule.Companion.DEFAULT_BACKGROUND_TYPE
33 import com.android.systemui.communal.shared.model.CommunalBackgroundType
34 import com.android.systemui.communal.shared.model.WhenToDream
35 import com.android.systemui.communal.shared.model.WhenToStartHub
36 import com.android.systemui.dagger.SysUISingleton
37 import com.android.systemui.dagger.qualifiers.Background
38 import com.android.systemui.dagger.qualifiers.Main
39 import com.android.systemui.flags.FeatureFlagsClassic
40 import com.android.systemui.flags.Flags
41 import com.android.systemui.util.kotlin.emitOnStart
42 import com.android.systemui.util.settings.SecureSettings
43 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
44 import javax.inject.Inject
45 import javax.inject.Named
46 import kotlinx.coroutines.CoroutineDispatcher
47 import kotlinx.coroutines.flow.Flow
48 import kotlinx.coroutines.flow.MutableStateFlow
49 import kotlinx.coroutines.flow.flowOn
50 import kotlinx.coroutines.flow.map
51 
52 interface CommunalSettingsRepository {
53     /** Whether a particular feature is enabled */
54     fun isEnabled(@CommunalFeature feature: Int): Flow<Boolean>
55 
56     /**
57      * Suppresses the hub with the given reasons. If there are no reasons, the hub will not be
58      * suppressed.
59      */
60     fun setSuppressionReasons(reasons: List<SuppressionReason>)
61 
62     /**
63      * Returns a [WhenToDream] for the specified user, indicating what state the device should be in
64      * to trigger dreams.
65      */
66     fun getWhenToDreamState(user: UserInfo): Flow<WhenToDream>
67 
68     /**
69      * Returns a[WhenToStartHub] for the specified user, indicating what state the device should be
70      * in to automatically display the hub.
71      */
72     fun getWhenToStartHubState(user: UserInfo): Flow<WhenToStartHub>
73 
74     /** Returns whether glanceable hub is enabled by the current user. */
75     fun getSettingEnabledByUser(user: UserInfo): Flow<Boolean>
76 
77     /**
78      * Returns true if any glanceable hub functionality should be enabled via configs and flags.
79      *
80      * This should be used for preventing basic glanceable hub functionality from running on devices
81      * that don't need it.
82      *
83      * If the glanceable_hub_v2 flag is enabled, checks the config_glanceableHubEnabled Android
84      * config boolean. Otherwise, checks the old config_communalServiceEnabled config and
85      * communal_hub flag.
86      */
87     fun getFlagEnabled(): Boolean
88 
89     /**
90      * Returns true if the Android config config_glanceableHubEnabled and the glanceable_hub_v2 flag
91      * are enabled.
92      *
93      * This should be used to flag off new glanceable hub or dream behavior that should launch
94      * together with the new hub experience that brings the hub to mobile.
95      *
96      * The trunk-stable flag is controlled by server rollout and is on all devices. The Android
97      * config flag is enabled via resource overlay only on products we want the hub to be present
98      * on.
99      */
100     fun getV2FlagEnabled(): Boolean
101 
102     /** Keyguard widgets enabled state by Device Policy Manager for the specified user. */
103     fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean>
104 
105     /** The type of background to use for the hub. Used to experiment with different backgrounds. */
106     fun getBackground(user: UserInfo): Flow<CommunalBackgroundType>
107 }
108 
109 @SysUISingleton
110 class CommunalSettingsRepositoryImpl
111 @Inject
112 constructor(
113     @Background private val bgDispatcher: CoroutineDispatcher,
114     @Main private val resources: Resources,
115     private val featureFlagsClassic: FeatureFlagsClassic,
116     private val secureSettings: SecureSettings,
117     private val broadcastDispatcher: BroadcastDispatcher,
118     private val devicePolicyManager: DevicePolicyManager,
119     @Named(DEFAULT_BACKGROUND_TYPE) private val defaultBackgroundType: CommunalBackgroundType,
120 ) : CommunalSettingsRepository {
121 
<lambda>null122     private val dreamsActivatedOnSleepByDefault by lazy {
123         resources.getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault)
124     }
125 
<lambda>null126     private val dreamsActivatedOnDockByDefault by lazy {
127         resources.getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault)
128     }
129 
<lambda>null130     private val dreamsActivatedOnPosturedByDefault by lazy {
131         resources.getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault)
132     }
133 
<lambda>null134     private val whenToStartHubByDefault by lazy {
135         resources.getInteger(com.android.internal.R.integer.config_whenToStartHubModeDefault)
136     }
137 
138     private val _suppressionReasons =
139         MutableStateFlow<List<SuppressionReason>>(
140             // Suppress hub by default until we get an initial update.
141             listOf(SuppressionReason.ReasonUnknown(FEATURE_ALL))
142         )
143 
isEnablednull144     override fun isEnabled(@CommunalFeature feature: Int): Flow<Boolean> =
145         _suppressionReasons.map { reasons -> reasons.none { it.isSuppressed(feature) } }
146 
setSuppressionReasonsnull147     override fun setSuppressionReasons(reasons: List<SuppressionReason>) {
148         _suppressionReasons.value = reasons
149     }
150 
getFlagEnablednull151     override fun getFlagEnabled(): Boolean {
152         return if (getV2FlagEnabled()) {
153             true
154         } else {
155             // This config (exposed as a classic feature flag) is targeted only to tablet.
156             // TODO(b/379181581): clean up usages of communal_hub flag
157             featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()
158         }
159     }
160 
getV2FlagEnablednull161     override fun getV2FlagEnabled(): Boolean {
162         return resources.getBoolean(com.android.internal.R.bool.config_glanceableHubEnabled) &&
163             glanceableHubV2()
164     }
165 
getWhenToDreamStatenull166     override fun getWhenToDreamState(user: UserInfo): Flow<WhenToDream> =
167         secureSettings
168             .observerFlow(
169                 userId = user.id,
170                 names =
171                     arrayOf(
172                         Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
173                         Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
174                         Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
175                     ),
176             )
177             .emitOnStart()
178             .map {
179                 if (
180                     secureSettings.getBoolForUser(
181                         Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
182                         dreamsActivatedOnSleepByDefault,
183                         user.id,
184                     )
185                 ) {
186                     WhenToDream.WHILE_CHARGING
187                 } else if (
188                     secureSettings.getBoolForUser(
189                         Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
190                         dreamsActivatedOnDockByDefault,
191                         user.id,
192                     )
193                 ) {
194                     WhenToDream.WHILE_DOCKED
195                 } else if (
196                     secureSettings.getBoolForUser(
197                         Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
198                         dreamsActivatedOnPosturedByDefault,
199                         user.id,
200                     )
201                 ) {
202                     WhenToDream.WHILE_POSTURED
203                 } else {
204                     WhenToDream.NEVER
205                 }
206             }
207             .flowOn(bgDispatcher)
208 
getWhenToStartHubStatenull209     override fun getWhenToStartHubState(user: UserInfo): Flow<WhenToStartHub> {
210         if (!getV2FlagEnabled()) {
211             return MutableStateFlow(WhenToStartHub.NEVER)
212         }
213         return secureSettings
214             .observerFlow(
215                 userId = user.id,
216                 names = arrayOf(Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB),
217             )
218             .emitOnStart()
219             .map {
220                 when (
221                     secureSettings.getIntForUser(
222                         Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
223                         whenToStartHubByDefault,
224                         user.id,
225                     )
226                 ) {
227                     Settings.Secure.GLANCEABLE_HUB_START_NEVER -> WhenToStartHub.NEVER
228                     Settings.Secure.GLANCEABLE_HUB_START_CHARGING -> WhenToStartHub.WHILE_CHARGING
229                     Settings.Secure.GLANCEABLE_HUB_START_CHARGING_UPRIGHT ->
230                         WhenToStartHub.WHILE_CHARGING_AND_POSTURED
231 
232                     Settings.Secure.GLANCEABLE_HUB_START_DOCKED -> WhenToStartHub.WHILE_DOCKED
233                     else -> WhenToStartHub.NEVER
234                 }
235             }
236             .flowOn(bgDispatcher)
237     }
238 
getAllowedByDevicePolicynull239     override fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
240         broadcastDispatcher
241             .broadcastFlow(
242                 filter =
243                     IntentFilter(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
244                 // In COPE management mode, the restriction from the managed profile may
245                 // propagate to the main profile. Therefore listen to this broadcast across
246                 // all users and update the state each time it changes.
247                 user = UserHandle.ALL,
248             )
249             .emitOnStart()
250             .map { devicePolicyManager.areKeyguardWidgetsAllowed(user.id) }
251 
getBackgroundnull252     override fun getBackground(user: UserInfo): Flow<CommunalBackgroundType> =
253         secureSettings
254             .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_BACKGROUND_SETTING))
255             .emitOnStart()
256             .map {
257                 val intType =
258                     secureSettings.getIntForUser(
259                         GLANCEABLE_HUB_BACKGROUND_SETTING,
260                         defaultBackgroundType.value,
261                         user.id,
262                     )
263                 CommunalBackgroundType.entries.find { type -> type.value == intType }
264                     ?: defaultBackgroundType
265             }
266 
getSettingEnabledByUsernull267     override fun getSettingEnabledByUser(user: UserInfo): Flow<Boolean> =
268         secureSettings
269             .observerFlow(userId = user.id, names = arrayOf(Settings.Secure.GLANCEABLE_HUB_ENABLED))
270             // Force an update
271             .emitOnStart()
272             .map {
273                 secureSettings.getIntForUser(
274                     Settings.Secure.GLANCEABLE_HUB_ENABLED,
275                     ENABLED_SETTING_DEFAULT,
276                     user.id,
277                 ) == 1
278             }
279             .flowOn(bgDispatcher)
280 
281     companion object {
282         const val GLANCEABLE_HUB_BACKGROUND_SETTING = "glanceable_hub_background"
283         private const val ENABLED_SETTING_DEFAULT = 1
284     }
285 }
286 
DevicePolicyManagernull287 private fun DevicePolicyManager.areKeyguardWidgetsAllowed(userId: Int): Boolean =
288     (getKeyguardDisabledFeatures(null, userId) and KEYGUARD_DISABLE_WIDGETS_ALL) == 0
289