• 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.domain.interactor
18 
19 import android.content.pm.UserInfo
20 import com.android.systemui.communal.data.model.FEATURE_AUTO_OPEN
21 import com.android.systemui.communal.data.model.FEATURE_ENABLED
22 import com.android.systemui.communal.data.model.FEATURE_MANUAL_OPEN
23 import com.android.systemui.communal.data.model.SuppressionReason
24 import com.android.systemui.communal.data.repository.CommunalSettingsRepository
25 import com.android.systemui.communal.shared.model.CommunalBackgroundType
26 import com.android.systemui.communal.shared.model.WhenToDream
27 import com.android.systemui.communal.shared.model.WhenToStartHub
28 import com.android.systemui.dagger.SysUISingleton
29 import com.android.systemui.dagger.qualifiers.Background
30 import com.android.systemui.settings.UserTracker
31 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
32 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
33 import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
34 import java.util.concurrent.Executor
35 import javax.inject.Inject
36 import kotlinx.coroutines.CoroutineDispatcher
37 import kotlinx.coroutines.CoroutineScope
38 import kotlinx.coroutines.channels.awaitClose
39 import kotlinx.coroutines.flow.Flow
40 import kotlinx.coroutines.flow.SharingStarted
41 import kotlinx.coroutines.flow.StateFlow
42 import kotlinx.coroutines.flow.flatMapLatest
43 import kotlinx.coroutines.flow.flowOf
44 import kotlinx.coroutines.flow.flowOn
45 import kotlinx.coroutines.flow.map
46 import kotlinx.coroutines.flow.stateIn
47 
48 @SysUISingleton
49 class CommunalSettingsInteractor
50 @Inject
51 constructor(
52     @Background private val bgScope: CoroutineScope,
53     @Background private val bgDispatcher: CoroutineDispatcher,
54     @Background private val bgExecutor: Executor,
55     private val repository: CommunalSettingsRepository,
56     userInteractor: SelectedUserInteractor,
57     private val userTracker: UserTracker,
58 ) {
59     /** Whether communal is enabled at all. */
60     val isCommunalEnabled: StateFlow<Boolean> =
61         repository
62             .isEnabled(FEATURE_ENABLED)
63             .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
64 
65     /** Whether manually opening the hub is enabled */
66     val manualOpenEnabled: StateFlow<Boolean> =
67         repository
68             .isEnabled(FEATURE_MANUAL_OPEN)
69             .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
70 
71     /** Whether auto-opening the hub is enabled */
72     val autoOpenEnabled: StateFlow<Boolean> =
73         repository
74             .isEnabled(FEATURE_AUTO_OPEN)
75             .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
76 
77     /** When to dream for the currently selected user. */
78     val whenToDream: Flow<WhenToDream> =
79         userInteractor.selectedUserInfo.flatMapLatestConflated { user ->
80             repository.getWhenToDreamState(user)
81         }
82 
83     /** When to automatically start hub for the currently selected user. */
84     val whenToStartHub: Flow<WhenToStartHub> =
85         userInteractor.selectedUserInfo.flatMapLatest { user ->
86             repository.getWhenToStartHubState(user)
87         }
88 
89     /** Whether communal hub is allowed by device policy for the current user */
90     val allowedForCurrentUserByDevicePolicy: Flow<Boolean> =
91         userInteractor.selectedUserInfo.flatMapLatestConflated { user ->
92             repository.getAllowedByDevicePolicy(user)
93         }
94 
95     /** Whether the hub is enabled for the current user */
96     val settingEnabledForCurrentUser: Flow<Boolean> =
97         userInteractor.selectedUserInfo.flatMapLatestConflated { user ->
98             repository.getSettingEnabledByUser(user)
99         }
100 
101     /**
102      * Returns true if any glanceable hub functionality should be enabled via configs and flags.
103      *
104      * This should be used for preventing basic glanceable hub functionality from running on devices
105      * that don't need it.
106      *
107      * If this is false, then the hub is definitely not available on the device. If this is true,
108      * refer to [isCommunalEnabled] which takes into account other factors that can change at
109      * runtime.
110      *
111      * If the glanceable_hub_v2 flag is enabled, checks the config_glanceableHubEnabled Android
112      * config boolean. Otherwise, checks the old config_communalServiceEnabled config and
113      * communal_hub flag.
114      */
115     fun isCommunalFlagEnabled(): Boolean = repository.getFlagEnabled()
116 
117     /**
118      * Returns true if the Android config config_glanceableHubEnabled and the glanceable_hub_v2 flag
119      * are enabled.
120      *
121      * This should be used to flag off new glanceable hub or dream behavior that should launch
122      * together with the new hub experience that brings the hub to mobile.
123      *
124      * The trunk-stable flag is controlled by server rollout and is on all devices. The Android
125      * config flag is enabled via resource overlay only on products we want the hub to be present
126      * on.
127      */
128     fun isV2FlagEnabled(): Boolean = repository.getV2FlagEnabled()
129 
130     /**
131      * Suppresses the hub with the given reasons. If there are no reasons, the hub will not be
132      * suppressed.
133      */
134     fun setSuppressionReasons(reasons: List<SuppressionReason>) {
135         repository.setSuppressionReasons(reasons)
136     }
137 
138     /** The type of background to use for the hub. Used to experiment with different backgrounds */
139     val communalBackground: Flow<CommunalBackgroundType> =
140         userInteractor.selectedUserInfo
141             .flatMapLatest { user -> repository.getBackground(user) }
142             .flowOn(bgDispatcher)
143 
144     private val workProfileUserInfoCallbackFlow: Flow<UserInfo?> = conflatedCallbackFlow {
145         fun send(profiles: List<UserInfo>) {
146             trySend(profiles.find { it.isManagedProfile })
147         }
148 
149         val callback =
150             object : UserTracker.Callback {
151                 override fun onProfilesChanged(profiles: List<UserInfo>) {
152                     send(profiles)
153                 }
154             }
155         userTracker.addCallback(callback, bgExecutor)
156         send(userTracker.userProfiles)
157 
158         awaitClose { userTracker.removeCallback(callback) }
159     }
160 
161     /**
162      * A user that device policy says shouldn't allow communal widgets, or null if there are no
163      * restrictions.
164      */
165     val workProfileUserDisallowedByDevicePolicy: StateFlow<UserInfo?> =
166         workProfileUserInfoCallbackFlow
167             .flatMapLatest { workProfile ->
168                 workProfile?.let {
169                     repository.getAllowedByDevicePolicy(it).map { allowed ->
170                         if (!allowed) it else null
171                     }
172                 } ?: flowOf(null)
173             }
174             .stateIn(
175                 scope = bgScope,
176                 started = SharingStarted.WhileSubscribed(),
177                 initialValue = null,
178             )
179 }
180