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