• 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.shade.data.repository
18 
19 import android.provider.Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS
20 import android.view.Display
21 import com.android.systemui.dagger.SysUISingleton
22 import com.android.systemui.dagger.qualifiers.Background
23 import com.android.systemui.display.data.repository.DisplayRepository
24 import com.android.systemui.keyguard.data.repository.KeyguardRepository
25 import com.android.systemui.shade.ShadeOnDefaultDisplayWhenLocked
26 import com.android.systemui.shade.display.ShadeDisplayPolicy
27 import com.android.systemui.util.settings.GlobalSettings
28 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
29 import javax.inject.Inject
30 import kotlinx.coroutines.CoroutineScope
31 import kotlinx.coroutines.flow.Flow
32 import kotlinx.coroutines.flow.MutableStateFlow
33 import kotlinx.coroutines.flow.SharingStarted
34 import kotlinx.coroutines.flow.StateFlow
35 import kotlinx.coroutines.flow.combine
36 import kotlinx.coroutines.flow.distinctUntilChanged
37 import kotlinx.coroutines.flow.flatMapLatest
38 import kotlinx.coroutines.flow.map
39 import kotlinx.coroutines.flow.onStart
40 import kotlinx.coroutines.flow.stateIn
41 
42 /** Source of truth for the display currently holding the shade. */
43 interface ShadeDisplaysRepository {
44     /** ID of the display which currently hosts the shade. */
45     val displayId: StateFlow<Int>
46     /** The current policy set. */
47     val currentPolicy: ShadeDisplayPolicy
48 
49     /**
50      * Id of the display that should host the shade.
51      *
52      * If this differs from [displayId], it means there is a shade movement in progress. Classes
53      * that rely on the shade being already moved (and its context/resources updated) should rely on
54      * [displayId]. Classes that need to do work associated with the shade move, should listen at
55      * this.
56      */
57     val pendingDisplayId: StateFlow<Int>
58 }
59 
60 /** Provides a way to set whether the display changed succeeded. */
61 interface MutableShadeDisplaysRepository : ShadeDisplaysRepository {
62     /**
63      * To be called when the shade changed window, and its resources have been completely updated.
64      */
onDisplayChangedSucceedednull65     fun onDisplayChangedSucceeded(displayId: Int)
66 }
67 
68 /**
69  * Keeps the policy and propagates the display id for the shade from it.
70  *
71  * If the display set by the policy is not available (e.g. after the cable is disconnected), this
72  * falls back to the [Display.DEFAULT_DISPLAY].
73  */
74 @SysUISingleton
75 class ShadeDisplaysRepositoryImpl
76 @Inject
77 constructor(
78     globalSettings: GlobalSettings,
79     defaultPolicy: ShadeDisplayPolicy,
80     @Background bgScope: CoroutineScope,
81     policies: Set<@JvmSuppressWildcards ShadeDisplayPolicy>,
82     @ShadeOnDefaultDisplayWhenLocked private val shadeOnDefaultDisplayWhenLocked: Boolean,
83     keyguardRepository: KeyguardRepository,
84     displayRepository: DisplayRepository,
85 ) : MutableShadeDisplaysRepository {
86 
87     private val policy: StateFlow<ShadeDisplayPolicy> =
88         globalSettings
89             .observerFlow(DEVELOPMENT_SHADE_DISPLAY_AWARENESS)
90             .onStart { emit(Unit) }
91             .map {
92                 val current = globalSettings.getString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS)
93                 for (policy in policies) {
94                     if (policy.name == current) return@map policy
95                 }
96                 globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, defaultPolicy.name)
97                 return@map defaultPolicy
98             }
99             .distinctUntilChanged()
100             .stateIn(bgScope, SharingStarted.Eagerly, defaultPolicy)
101 
102     private val displayIdFromPolicy: Flow<Int> =
103         policy
104             .flatMapLatest { it.displayId }
105             .combine(displayRepository.displayIds) { policyDisplayId, availableIds ->
106                 if (policyDisplayId !in availableIds) Display.DEFAULT_DISPLAY else policyDisplayId
107             }
108 
109     private val keyguardAwareDisplayPolicy: Flow<Int> =
110         if (!shadeOnDefaultDisplayWhenLocked) {
111             displayIdFromPolicy
112         } else {
113             keyguardRepository.isKeyguardShowing.combine(displayIdFromPolicy) {
114                 isKeyguardShowing,
115                 currentDisplayId ->
116                 if (isKeyguardShowing) {
117                     Display.DEFAULT_DISPLAY
118                 } else {
119                     currentDisplayId
120                 }
121             }
122         }
123 
124     override val currentPolicy: ShadeDisplayPolicy
125         get() = policy.value
126 
127     override val pendingDisplayId: StateFlow<Int> =
128         keyguardAwareDisplayPolicy.stateIn(
129             bgScope,
130             SharingStarted.WhileSubscribed(),
131             Display.DEFAULT_DISPLAY,
132         )
133     private val _committedDisplayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
134     override val displayId: StateFlow<Int> = _committedDisplayId
135 
136     override fun onDisplayChangedSucceeded(displayId: Int) {
137         _committedDisplayId.value = displayId
138     }
139 }
140