1 /* 2 * 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.dagger.SysUISingleton 22 import com.android.systemui.dagger.qualifiers.Application 23 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository 24 import com.android.systemui.keyguard.shared.model.KeyguardState 25 import com.android.systemui.keyguard.shared.model.WakefulnessState 26 import com.android.systemui.util.kotlin.Utils.Companion.toTriple 27 import com.android.systemui.util.kotlin.sample 28 import javax.inject.Inject 29 import kotlin.time.Duration.Companion.milliseconds 30 import kotlinx.coroutines.CoroutineScope 31 import kotlinx.coroutines.flow.combine 32 import kotlinx.coroutines.launch 33 34 @SysUISingleton 35 class FromOccludedTransitionInteractor 36 @Inject 37 constructor( 38 override val transitionRepository: KeyguardTransitionRepository, 39 override val transitionInteractor: KeyguardTransitionInteractor, 40 @Application private val scope: CoroutineScope, 41 private val keyguardInteractor: KeyguardInteractor, 42 ) : 43 TransitionInteractor( 44 fromState = KeyguardState.OCCLUDED, 45 ) { 46 47 override fun start() { 48 listenForOccludedToLockscreen() 49 listenForOccludedToDreaming() 50 listenForOccludedToAodOrDozing() 51 listenForOccludedToGone() 52 listenForOccludedToAlternateBouncer() 53 } 54 55 private fun listenForOccludedToDreaming() { 56 scope.launch { 57 keyguardInteractor.isAbleToDream 58 .sample(transitionInteractor.finishedKeyguardState, ::Pair) 59 .collect { pair -> 60 val (isAbleToDream, keyguardState) = pair 61 if (isAbleToDream && keyguardState == KeyguardState.OCCLUDED) { 62 startTransitionTo(KeyguardState.DREAMING) 63 } 64 } 65 } 66 } 67 68 private fun listenForOccludedToLockscreen() { 69 scope.launch { 70 keyguardInteractor.isKeyguardOccluded 71 .sample( 72 combine( 73 keyguardInteractor.isKeyguardShowing, 74 transitionInteractor.startedKeyguardTransitionStep, 75 ::Pair 76 ), 77 ::toTriple 78 ) 79 .collect { (isOccluded, isShowing, lastStartedKeyguardState) -> 80 // Occlusion signals come from the framework, and should interrupt any 81 // existing transition 82 if ( 83 !isOccluded && 84 isShowing && 85 lastStartedKeyguardState.to == KeyguardState.OCCLUDED 86 ) { 87 startTransitionTo(KeyguardState.LOCKSCREEN) 88 } 89 } 90 } 91 } 92 93 private fun listenForOccludedToGone() { 94 scope.launch { 95 keyguardInteractor.isKeyguardOccluded 96 .sample( 97 combine( 98 keyguardInteractor.isKeyguardShowing, 99 transitionInteractor.startedKeyguardTransitionStep, 100 ::Pair 101 ), 102 ::toTriple 103 ) 104 .collect { (isOccluded, isShowing, lastStartedKeyguardState) -> 105 // Occlusion signals come from the framework, and should interrupt any 106 // existing transition 107 if ( 108 !isOccluded && 109 !isShowing && 110 lastStartedKeyguardState.to == KeyguardState.OCCLUDED 111 ) { 112 startTransitionTo(KeyguardState.GONE) 113 } 114 } 115 } 116 } 117 118 private fun listenForOccludedToAodOrDozing() { 119 scope.launch { 120 keyguardInteractor.wakefulnessModel 121 .sample( 122 combine( 123 transitionInteractor.startedKeyguardTransitionStep, 124 keyguardInteractor.isAodAvailable, 125 ::Pair 126 ), 127 ::toTriple 128 ) 129 .collect { (wakefulnessState, lastStartedStep, isAodAvailable) -> 130 if ( 131 lastStartedStep.to == KeyguardState.OCCLUDED && 132 wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP 133 ) { 134 startTransitionTo( 135 if (isAodAvailable) KeyguardState.AOD else KeyguardState.DOZING 136 ) 137 } 138 } 139 } 140 } 141 142 private fun listenForOccludedToAlternateBouncer() { 143 scope.launch { 144 keyguardInteractor.alternateBouncerShowing 145 .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair) 146 .collect { (isAlternateBouncerShowing, lastStartedTransitionStep) -> 147 if ( 148 isAlternateBouncerShowing && 149 lastStartedTransitionStep.to == KeyguardState.OCCLUDED 150 ) { 151 startTransitionTo(KeyguardState.ALTERNATE_BOUNCER) 152 } 153 } 154 } 155 } 156 157 override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator { 158 return ValueAnimator().apply { 159 interpolator = 160 when (toState) { 161 KeyguardState.ALTERNATE_BOUNCER -> Interpolators.FAST_OUT_SLOW_IN 162 else -> Interpolators.LINEAR 163 } 164 165 duration = 166 when (toState) { 167 KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION 168 else -> DEFAULT_DURATION 169 }.inWholeMilliseconds 170 } 171 } 172 173 companion object { 174 private val DEFAULT_DURATION = 500.milliseconds 175 val TO_LOCKSCREEN_DURATION = 933.milliseconds 176 } 177 } 178