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.provider.Settings
24 import com.android.systemui.Flags.communalHub
25 import com.android.systemui.broadcast.BroadcastDispatcher
26 import com.android.systemui.communal.data.model.CommunalEnabledState
27 import com.android.systemui.communal.data.model.CommunalWidgetCategories
28 import com.android.systemui.communal.data.model.DisabledReason
29 import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_DEVICE_POLICY
30 import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_FLAG
31 import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_INVALID_USER
32 import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_USER_SETTING
33 import com.android.systemui.communal.shared.model.CommunalBackgroundType
34 import com.android.systemui.dagger.SysUISingleton
35 import com.android.systemui.dagger.qualifiers.Background
36 import com.android.systemui.flags.FeatureFlagsClassic
37 import com.android.systemui.flags.Flags
38 import com.android.systemui.util.kotlin.emitOnStart
39 import com.android.systemui.util.settings.SecureSettings
40 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
41 import java.util.EnumSet
42 import javax.inject.Inject
43 import kotlinx.coroutines.CoroutineDispatcher
44 import kotlinx.coroutines.flow.Flow
45 import kotlinx.coroutines.flow.combine
46 import kotlinx.coroutines.flow.flowOf
47 import kotlinx.coroutines.flow.flowOn
48 import kotlinx.coroutines.flow.map
49 import kotlinx.coroutines.flow.onStart
50 
51 interface CommunalSettingsRepository {
52     /** A [CommunalEnabledState] for the specified user. */
53     fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState>
54 
55     /**
56      * A flow that reports the widget categories to show on the hub as selected by the user in
57      * Settings.
58      */
59     fun getWidgetCategories(user: UserInfo): Flow<CommunalWidgetCategories>
60 
61     /** Keyguard widgets enabled state by Device Policy Manager for the specified user. */
62     fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean>
63 
64     /** The type of background to use for the hub. Used to experiment with different backgrounds. */
65     fun getBackground(user: UserInfo): Flow<CommunalBackgroundType>
66 }
67 
68 @SysUISingleton
69 class CommunalSettingsRepositoryImpl
70 @Inject
71 constructor(
72     @Background private val bgDispatcher: CoroutineDispatcher,
73     private val featureFlagsClassic: FeatureFlagsClassic,
74     private val secureSettings: SecureSettings,
75     private val broadcastDispatcher: BroadcastDispatcher,
76     private val devicePolicyManager: DevicePolicyManager,
77 ) : CommunalSettingsRepository {
78 
<lambda>null79     private val flagEnabled: Boolean by lazy {
80         featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()
81     }
82 
getEnabledStatenull83     override fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState> {
84         if (!user.isMain) {
85             return flowOf(CommunalEnabledState(DISABLED_REASON_INVALID_USER))
86         }
87         if (!flagEnabled) {
88             return flowOf(CommunalEnabledState(DISABLED_REASON_FLAG))
89         }
90         return combine(
91                 getEnabledByUser(user).mapToReason(DISABLED_REASON_USER_SETTING),
92                 getAllowedByDevicePolicy(user).mapToReason(DISABLED_REASON_DEVICE_POLICY),
93             ) { reasons ->
94                 reasons.filterNotNull()
95             }
96             .map { reasons ->
97                 if (reasons.isEmpty()) {
98                     EnumSet.noneOf(DisabledReason::class.java)
99                 } else {
100                     EnumSet.copyOf(reasons)
101                 }
102             }
103             .map { reasons -> CommunalEnabledState(reasons) }
104             .flowOn(bgDispatcher)
105     }
106 
getWidgetCategoriesnull107     override fun getWidgetCategories(user: UserInfo): Flow<CommunalWidgetCategories> =
108         secureSettings
109             .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_CONTENT_SETTING))
110             // Force an update
111             .onStart { emit(Unit) }
<lambda>null112             .map {
113                 CommunalWidgetCategories(
114                     secureSettings.getIntForUser(
115                         GLANCEABLE_HUB_CONTENT_SETTING,
116                         CommunalWidgetCategories.defaultCategories,
117                         user.id
118                     )
119                 )
120             }
121             .flowOn(bgDispatcher)
122 
getAllowedByDevicePolicynull123     override fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
124         broadcastDispatcher
125             .broadcastFlow(
126                 filter =
127                     IntentFilter(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
128                 user = user.userHandle
129             )
130             .emitOnStart()
131             .map { devicePolicyManager.areKeyguardWidgetsAllowed(user.id) }
132 
getBackgroundnull133     override fun getBackground(user: UserInfo): Flow<CommunalBackgroundType> =
134         secureSettings
135             .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_BACKGROUND_SETTING))
136             .emitOnStart()
137             .map {
138                 val intType =
139                     secureSettings.getIntForUser(
140                         GLANCEABLE_HUB_BACKGROUND_SETTING,
141                         CommunalBackgroundType.DEFAULT.value,
142                         user.id
143                     )
144                 CommunalBackgroundType.entries.find { type -> type.value == intType }
145                     ?: CommunalBackgroundType.DEFAULT
146             }
147 
getEnabledByUsernull148     private fun getEnabledByUser(user: UserInfo): Flow<Boolean> =
149         secureSettings
150             .observerFlow(userId = user.id, names = arrayOf(Settings.Secure.GLANCEABLE_HUB_ENABLED))
151             // Force an update
152             .onStart { emit(Unit) }
<lambda>null153             .map {
154                 secureSettings.getIntForUser(
155                     Settings.Secure.GLANCEABLE_HUB_ENABLED,
156                     ENABLED_SETTING_DEFAULT,
157                     user.id,
158                 ) == 1
159             }
160 
161     companion object {
162         const val GLANCEABLE_HUB_CONTENT_SETTING = "glanceable_hub_content_setting"
163         const val GLANCEABLE_HUB_BACKGROUND_SETTING = "glanceable_hub_background"
164         private const val ENABLED_SETTING_DEFAULT = 1
165     }
166 }
167 
DevicePolicyManagernull168 private fun DevicePolicyManager.areKeyguardWidgetsAllowed(userId: Int): Boolean =
169     (getKeyguardDisabledFeatures(null, userId) and KEYGUARD_DISABLE_WIDGETS_ALL) == 0
170 
171 private fun Flow<Boolean>.mapToReason(reason: DisabledReason) = map { enabled ->
172     if (enabled) null else reason
173 }
174