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.systemui.dagger.SysUISingleton 20 import com.android.systemui.dagger.qualifiers.Application 21 import com.android.systemui.keyguard.data.repository.KeyguardRepository 22 import com.android.systemui.keyguard.shared.model.StatusBarState 23 import com.android.systemui.shade.data.repository.ShadeRepository 24 import com.android.systemui.shade.shared.model.ShadeMode 25 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor 26 import javax.inject.Inject 27 import kotlinx.coroutines.CoroutineScope 28 import kotlinx.coroutines.currentCoroutineContext 29 import kotlinx.coroutines.flow.Flow 30 import kotlinx.coroutines.flow.SharingStarted 31 import kotlinx.coroutines.flow.StateFlow 32 import kotlinx.coroutines.flow.combine 33 import kotlinx.coroutines.flow.distinctUntilChanged 34 import kotlinx.coroutines.flow.first 35 import kotlinx.coroutines.flow.flow 36 import kotlinx.coroutines.flow.stateIn 37 import kotlinx.coroutines.isActive 38 39 /** ShadeInteractor implementation for the legacy codebase, e.g. NPVC. */ 40 @SysUISingleton 41 class ShadeInteractorLegacyImpl 42 @Inject 43 constructor( 44 @Application val scope: CoroutineScope, 45 keyguardRepository: KeyguardRepository, 46 sharedNotificationContainerInteractor: SharedNotificationContainerInteractor, 47 repository: ShadeRepository, 48 ) : BaseShadeInteractor { 49 /** 50 * The amount [0-1] that the shade has been opened. Uses stateIn to avoid redundant calculations 51 * in downstream flows. 52 */ 53 override val shadeExpansion: StateFlow<Float> = 54 combine( 55 repository.lockscreenShadeExpansion, 56 keyguardRepository.statusBarState, 57 repository.legacyShadeExpansion, 58 repository.qsExpansion, 59 sharedNotificationContainerInteractor.isSplitShadeEnabled 60 ) { 61 lockscreenShadeExpansion, 62 statusBarState, 63 legacyShadeExpansion, 64 qsExpansion, 65 splitShadeEnabled -> 66 when (statusBarState) { 67 // legacyShadeExpansion is 1 instead of 0 when QS is expanded 68 StatusBarState.SHADE -> 69 if (!splitShadeEnabled && qsExpansion > 0f) 1f - qsExpansion 70 else legacyShadeExpansion 71 StatusBarState.KEYGUARD -> lockscreenShadeExpansion 72 // dragDownAmount, which drives lockscreenShadeExpansion resets to 0f when 73 // the pointer is lifted and the lockscreen shade is fully expanded 74 StatusBarState.SHADE_LOCKED -> 1f 75 } 76 } 77 .distinctUntilChanged() 78 .stateIn(scope, SharingStarted.Eagerly, 0f) 79 80 override val qsExpansion: StateFlow<Float> = repository.qsExpansion 81 82 override val isQsExpanded: StateFlow<Boolean> = repository.legacyIsQsExpanded 83 84 override val isQsBypassingShade: Flow<Boolean> = repository.legacyExpandImmediate 85 override val isQsFullscreen: Flow<Boolean> = repository.legacyQsFullscreen 86 87 override val anyExpansion: StateFlow<Float> = 88 createAnyExpansionFlow(scope, shadeExpansion, qsExpansion) 89 90 override val isAnyExpanded = 91 repository.legacyExpandedOrAwaitingInputTransfer.stateIn( 92 scope, 93 SharingStarted.Eagerly, 94 false 95 ) 96 97 override val isUserInteractingWithShade: Flow<Boolean> = 98 combine( 99 userInteractingFlow(repository.legacyShadeTracking, repository.legacyShadeExpansion), 100 repository.legacyLockscreenShadeTracking 101 ) { legacyShadeTracking, legacyLockscreenShadeTracking -> 102 legacyShadeTracking || legacyLockscreenShadeTracking 103 } 104 105 override val isUserInteractingWithQs: Flow<Boolean> = 106 userInteractingFlow(repository.legacyQsTracking, repository.qsExpansion) 107 108 override val shadeMode: StateFlow<ShadeMode> = repository.shadeMode 109 110 /** 111 * Return a flow for whether a user is interacting with an expandable shade component using 112 * tracking and expansion flows. NOTE: expansion must be a `StateFlow` to guarantee that 113 * [expansion.first] checks the current value of the flow. 114 */ 115 private fun userInteractingFlow( 116 tracking: Flow<Boolean>, 117 expansion: StateFlow<Float> 118 ): Flow<Boolean> { 119 return flow { 120 // initial value is false 121 emit(false) 122 while (currentCoroutineContext().isActive) { 123 // wait for tracking to become true 124 tracking.first { it } 125 emit(true) 126 // wait for tracking to become false 127 tracking.first { !it } 128 // wait for expansion to complete in either direction 129 expansion.first { it <= 0f || it >= 1f } 130 // interaction complete 131 emit(false) 132 } 133 } 134 } 135 } 136