1 /* <lambda>null2 * Copyright (C) 2023 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.domain.interactor 18 19 import com.android.compose.animation.scene.TransitionKey 20 import kotlinx.coroutines.CoroutineScope 21 import kotlinx.coroutines.flow.Flow 22 import kotlinx.coroutines.flow.SharingStarted 23 import kotlinx.coroutines.flow.StateFlow 24 import kotlinx.coroutines.flow.combine 25 import kotlinx.coroutines.flow.stateIn 26 27 /** Business logic for shade interactions. */ 28 interface ShadeInteractor : BaseShadeInteractor { 29 /** Emits true if the Notifications shade is currently allowed and false otherwise. */ 30 val isShadeEnabled: StateFlow<Boolean> 31 32 /** Emits true if QS shade is currently allowed and false otherwise. */ 33 val isQsEnabled: StateFlow<Boolean> 34 35 /** Whether either the Notifications shade or QS shade is fully expanded. */ 36 val isAnyFullyExpanded: StateFlow<Boolean> 37 38 /** Whether the Notifications Shade is fully expanded. */ 39 val isShadeFullyExpanded: Flow<Boolean> 40 41 /** Whether Notifications Shade is expanded a non-zero amount. */ 42 val isShadeAnyExpanded: StateFlow<Boolean> 43 44 /** Whether the Notifications Shade is fully collapsed. */ 45 val isShadeFullyCollapsed: Flow<Boolean> 46 47 /** 48 * Whether the user is expanding or collapsing either the shade or quick settings with user 49 * input (i.e. dragging a pointer). This will be true even if the user's input gesture had ended 50 * but a transition they initiated is still animating. 51 */ 52 val isUserInteracting: StateFlow<Boolean> 53 54 /** Are touches allowed on the notification panel? */ 55 val isShadeTouchable: Flow<Boolean> 56 57 /** Whether the shade can be expanded from QQS to QS. */ 58 val isExpandToQsEnabled: Flow<Boolean> 59 } 60 61 /** ShadeInteractor methods with implementations that differ between non-empty impls. */ 62 interface BaseShadeInteractor { 63 /** The amount [0-1] either QS or the shade has been opened. */ 64 val anyExpansion: StateFlow<Float> 65 66 /** 67 * Whether either the shade or QS is partially or fully expanded, i.e. not fully collapsed. At 68 * this time, this is not simply a matter of checking if either value in shadeExpansion and 69 * qsExpansion is greater than zero, because it includes the legacy concept of whether input 70 * transfer is about to occur. If the scene container flag is enabled, it just checks whether 71 * either expansion value is positive. 72 * 73 * TODO(b/300258424) remove all but the first sentence of this comment 74 */ 75 val isAnyExpanded: StateFlow<Boolean> 76 77 /** The amount [0-1] that the Notifications Shade has been opened. */ 78 val shadeExpansion: StateFlow<Float> 79 80 /** 81 * The amount [0-1] QS has been opened. Normal shade with notifications (QQS) visible will 82 * report 0f. If split shade is enabled, value matches shadeExpansion. 83 */ 84 val qsExpansion: StateFlow<Float> 85 86 /** Whether Quick Settings Shade is expanded a non-zero amount. */ 87 val isQsExpanded: StateFlow<Boolean> 88 89 /** 90 * Emits true whenever Quick Settings is being expanded without first expanding the Shade or if 91 * if Quick Settings is being collapsed without first collapsing to shade, i.e. expanding with 92 * 2-finger swipe or collapsing by flinging from the bottom of the screen. This concept was 93 * previously called "expand immediate" in the legacy codebase. 94 */ 95 val isQsBypassingShade: Flow<Boolean> 96 97 /** 98 * Emits true when QS is displayed over the entire screen of the device. Currently, this only 99 * happens on phones that are not unfolded when QS expansion is equal to 1. 100 */ 101 val isQsFullscreen: Flow<Boolean> 102 103 /** 104 * Whether the user is expanding or collapsing the shade with user input. This will be true even 105 * if the user's input gesture has ended but a transition they initiated is animating. 106 */ 107 val isUserInteractingWithShade: Flow<Boolean> 108 109 /** 110 * Whether the user is expanding or collapsing quick settings with user input. This will be true 111 * even if the user's input gesture has ended but a transition they initiated is still 112 * animating. 113 */ 114 val isUserInteractingWithQs: Flow<Boolean> 115 116 /** 117 * Triggers the expansion (opening) of the notifications shade. If it is already expanded, this 118 * has no effect. 119 */ expandNotificationsShadenull120 fun expandNotificationsShade(loggingReason: String, transitionKey: TransitionKey? = null) 121 122 /** 123 * Triggers the expansion (opening) of the quick settings shade. If it is already expanded, this 124 * has no effect. 125 */ 126 fun expandQuickSettingsShade(loggingReason: String, transitionKey: TransitionKey? = null) 127 128 /** 129 * Triggers the collapse (closing) of the notifications shade. If it is already collapsed, this 130 * has no effect. 131 */ 132 fun collapseNotificationsShade(loggingReason: String, transitionKey: TransitionKey? = null) 133 134 /** 135 * Triggers the collapse (closing) of the quick settings shade. If it is already collapsed, this 136 * has no effect. 137 */ 138 fun collapseQuickSettingsShade( 139 loggingReason: String, 140 transitionKey: TransitionKey? = null, 141 bypassNotificationsShade: Boolean = false, 142 ) 143 144 /** 145 * Triggers the collapse (closing) of the notifications shade or quick settings shade, whichever 146 * is open. If both are already collapsed, this has no effect. 147 */ 148 fun collapseEitherShade(loggingReason: String, transitionKey: TransitionKey? = null) 149 } 150 151 fun createAnyExpansionFlow( 152 scope: CoroutineScope, 153 shadeExpansion: Flow<Float>, 154 qsExpansion: Flow<Float>, 155 ): StateFlow<Float> { 156 return combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) } 157 .stateIn(scope, SharingStarted.Eagerly, 0f) 158 } 159