1 /* <lambda>null2 * Copyright (C) 2022 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.keyguard.domain.interactor 18 19 import android.animation.ValueAnimator 20 import com.android.app.animation.Interpolators 21 import com.android.systemui.Flags.restartDreamOnUnocclude 22 import com.android.systemui.communal.domain.interactor.CommunalInteractor 23 import com.android.systemui.dagger.SysUISingleton 24 import com.android.systemui.dagger.qualifiers.Background 25 import com.android.systemui.dagger.qualifiers.Main 26 import com.android.systemui.keyguard.KeyguardWmStateRefactor 27 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository 28 import com.android.systemui.keyguard.shared.model.KeyguardState 29 import com.android.systemui.power.domain.interactor.PowerInteractor 30 import com.android.systemui.scene.shared.flag.SceneContainerFlag 31 import com.android.systemui.util.kotlin.Utils.Companion.sample 32 import com.android.systemui.util.kotlin.sample 33 import javax.inject.Inject 34 import kotlin.time.Duration.Companion.milliseconds 35 import kotlinx.coroutines.CoroutineDispatcher 36 import kotlinx.coroutines.CoroutineScope 37 import kotlinx.coroutines.launch 38 39 @SysUISingleton 40 class FromOccludedTransitionInteractor 41 @Inject 42 constructor( 43 override val transitionRepository: KeyguardTransitionRepository, 44 transitionInteractor: KeyguardTransitionInteractor, 45 @Background private val scope: CoroutineScope, 46 @Background bgDispatcher: CoroutineDispatcher, 47 @Main mainDispatcher: CoroutineDispatcher, 48 keyguardInteractor: KeyguardInteractor, 49 powerInteractor: PowerInteractor, 50 private val communalInteractor: CommunalInteractor, 51 keyguardOcclusionInteractor: KeyguardOcclusionInteractor, 52 ) : 53 TransitionInteractor( 54 fromState = KeyguardState.OCCLUDED, 55 transitionInteractor = transitionInteractor, 56 mainDispatcher = mainDispatcher, 57 bgDispatcher = bgDispatcher, 58 powerInteractor = powerInteractor, 59 keyguardOcclusionInteractor = keyguardOcclusionInteractor, 60 keyguardInteractor = keyguardInteractor, 61 ) { 62 63 override fun start() { 64 listenForOccludedToLockscreenOrHub() 65 listenForOccludedToDreaming() 66 listenForOccludedToAsleep() 67 listenForOccludedToGone() 68 listenForOccludedToAlternateBouncer() 69 listenForOccludedToPrimaryBouncer() 70 } 71 72 private fun listenForOccludedToPrimaryBouncer() { 73 scope.launch { 74 keyguardInteractor.primaryBouncerShowing 75 .filterRelevantKeyguardStateAnd { isBouncerShowing -> isBouncerShowing } 76 .collect { startTransitionTo(KeyguardState.PRIMARY_BOUNCER) } 77 } 78 } 79 80 private fun listenForOccludedToDreaming() { 81 scope.launch { 82 keyguardInteractor.isAbleToDream 83 .filterRelevantKeyguardStateAnd { isAbleToDream -> isAbleToDream } 84 .collect { startTransitionTo(KeyguardState.DREAMING) } 85 } 86 } 87 88 private fun listenForOccludedToLockscreenOrHub() { 89 if (KeyguardWmStateRefactor.isEnabled) { 90 scope.launch { 91 keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop 92 .filterRelevantKeyguardStateAnd { onTop -> !onTop } 93 .sample( 94 communalInteractor.isIdleOnCommunal, 95 communalInteractor.showCommunalFromOccluded, 96 communalInteractor.dreamFromOccluded, 97 ) 98 .collect { (_, isIdleOnCommunal, showCommunalFromOccluded, dreamFromOccluded) -> 99 startTransitionToLockscreenOrHub( 100 isIdleOnCommunal, 101 showCommunalFromOccluded, 102 dreamFromOccluded 103 ) 104 } 105 } 106 } else { 107 scope.launch { 108 keyguardInteractor.isKeyguardOccluded 109 .sample( 110 keyguardInteractor.isKeyguardShowing, 111 communalInteractor.isIdleOnCommunal, 112 communalInteractor.showCommunalFromOccluded, 113 communalInteractor.dreamFromOccluded, 114 ) 115 .filterRelevantKeyguardStateAnd { (isOccluded, isShowing, _, _, _) -> 116 !isOccluded && isShowing 117 } 118 .collect { (_, _, isIdleOnCommunal, showCommunalFromOccluded, dreamFromOccluded) 119 -> 120 startTransitionToLockscreenOrHub( 121 isIdleOnCommunal, 122 showCommunalFromOccluded, 123 dreamFromOccluded 124 ) 125 } 126 } 127 } 128 } 129 130 private suspend fun FromOccludedTransitionInteractor.startTransitionToLockscreenOrHub( 131 isIdleOnCommunal: Boolean, 132 showCommunalFromOccluded: Boolean, 133 dreamFromOccluded: Boolean, 134 ) { 135 if (restartDreamOnUnocclude() && dreamFromOccluded) { 136 startTransitionTo(KeyguardState.DREAMING) 137 } else if (isIdleOnCommunal || showCommunalFromOccluded) { 138 // TODO(b/336576536): Check if adaptation for scene framework is needed 139 if (SceneContainerFlag.isEnabled) return 140 startTransitionTo(KeyguardState.GLANCEABLE_HUB) 141 } else { 142 startTransitionTo(KeyguardState.LOCKSCREEN) 143 } 144 } 145 146 private fun listenForOccludedToGone() { 147 // TODO(b/336576536): Check if adaptation for scene framework is needed 148 if (SceneContainerFlag.isEnabled) return 149 if (KeyguardWmStateRefactor.isEnabled) { 150 // We don't think OCCLUDED to GONE is possible. You should always have to go via a 151 // *_BOUNCER state to end up GONE. Launching an activity over a dismissable keyguard 152 // dismisses it, and even "extend unlock" doesn't unlock the device in the background. 153 // If we're wrong - sorry, add it back here. 154 return 155 } 156 157 scope.launch { 158 keyguardInteractor.isKeyguardOccluded 159 .sample(keyguardInteractor.isKeyguardShowing, ::Pair) 160 .filterRelevantKeyguardStateAnd { (occluded, showing) -> !occluded && !showing } 161 .collect { 162 // Occlusion signals come from the framework, and should interrupt any 163 // existing transition 164 startTransitionTo(KeyguardState.GONE) 165 } 166 } 167 } 168 169 private fun listenForOccludedToAsleep() { 170 scope.launch { listenForSleepTransition() } 171 } 172 173 private fun listenForOccludedToAlternateBouncer() { 174 scope.launch { 175 keyguardInteractor.alternateBouncerShowing 176 .filterRelevantKeyguardStateAnd { isAlternateBouncerShowing -> 177 isAlternateBouncerShowing 178 } 179 .collect { startTransitionTo(KeyguardState.ALTERNATE_BOUNCER) } 180 } 181 } 182 183 override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator { 184 return ValueAnimator().apply { 185 interpolator = 186 when (toState) { 187 KeyguardState.ALTERNATE_BOUNCER -> Interpolators.FAST_OUT_SLOW_IN 188 else -> Interpolators.LINEAR 189 } 190 191 duration = 192 when (toState) { 193 KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION 194 KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION 195 else -> DEFAULT_DURATION 196 }.inWholeMilliseconds 197 } 198 } 199 200 companion object { 201 const val TAG = "FromOccludedTransitionInteractor" 202 private val DEFAULT_DURATION = 500.milliseconds 203 val TO_LOCKSCREEN_DURATION = 933.milliseconds 204 val TO_GLANCEABLE_HUB_DURATION = 250.milliseconds 205 val TO_AOD_DURATION = DEFAULT_DURATION 206 val TO_DOZING_DURATION = DEFAULT_DURATION 207 } 208 } 209