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.keyguard.domain.interactor 18 19 import android.animation.ValueAnimator 20 import com.android.app.animation.Interpolators 21 import com.android.app.tracing.coroutines.launch 22 import com.android.systemui.Flags 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.keyguard.shared.model.TransitionModeOnCanceled 30 import com.android.systemui.power.domain.interactor.PowerInteractor 31 import com.android.systemui.scene.shared.flag.SceneContainerFlag 32 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf 33 import com.android.systemui.util.kotlin.BooleanFlowOperators.not 34 import javax.inject.Inject 35 import kotlin.time.Duration.Companion.milliseconds 36 import kotlin.time.Duration.Companion.seconds 37 import kotlinx.coroutines.CoroutineDispatcher 38 import kotlinx.coroutines.CoroutineScope 39 import kotlinx.coroutines.flow.collectLatest 40 import kotlinx.coroutines.launch 41 import kotlinx.coroutines.withContext 42 43 @SysUISingleton 44 class FromGlanceableHubTransitionInteractor 45 @Inject 46 constructor( 47 @Background private val scope: CoroutineScope, 48 @Main mainDispatcher: CoroutineDispatcher, 49 @Background bgDispatcher: CoroutineDispatcher, 50 private val glanceableHubTransitions: GlanceableHubTransitions, 51 keyguardInteractor: KeyguardInteractor, 52 override val transitionRepository: KeyguardTransitionRepository, 53 transitionInteractor: KeyguardTransitionInteractor, 54 powerInteractor: PowerInteractor, 55 keyguardOcclusionInteractor: KeyguardOcclusionInteractor, 56 ) : 57 TransitionInteractor( 58 fromState = KeyguardState.GLANCEABLE_HUB, 59 transitionInteractor = transitionInteractor, 60 mainDispatcher = mainDispatcher, 61 bgDispatcher = bgDispatcher, 62 powerInteractor = powerInteractor, 63 keyguardOcclusionInteractor = keyguardOcclusionInteractor, 64 keyguardInteractor = keyguardInteractor, 65 ) { 66 67 override fun start() { 68 // TODO(b/336576536): Check if adaptation for scene framework is needed 69 if (SceneContainerFlag.isEnabled) return 70 if (!Flags.communalHub()) { 71 return 72 } 73 listenForHubToLockscreenOrDreaming() 74 listenForHubToDozing() 75 listenForHubToPrimaryBouncer() 76 listenForHubToAlternateBouncer() 77 listenForHubToOccluded() 78 listenForHubToGone() 79 } 80 81 override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator { 82 return ValueAnimator().apply { 83 interpolator = Interpolators.LINEAR 84 duration = 85 when (toState) { 86 KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION 87 KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION 88 else -> DEFAULT_DURATION 89 }.inWholeMilliseconds 90 } 91 } 92 93 /** 94 * Listens for the glanceable hub transition to lock screen and directly drives the keyguard 95 * transition. 96 */ 97 private fun listenForHubToLockscreenOrDreaming() { 98 scope.launch("$TAG#listenForGlanceableHubToLockscreenOrDream") { 99 keyguardInteractor.isDreaming.collectLatest { dreaming -> 100 withContext(mainDispatcher) { 101 val toState = 102 if (dreaming) { 103 KeyguardState.DREAMING 104 } else { 105 KeyguardState.LOCKSCREEN 106 } 107 glanceableHubTransitions.listenForGlanceableHubTransition( 108 transitionOwnerName = TAG, 109 fromState = KeyguardState.GLANCEABLE_HUB, 110 toState = toState, 111 ) 112 } 113 } 114 } 115 } 116 117 private fun listenForHubToPrimaryBouncer() { 118 scope.launch("$TAG#listenForHubToPrimaryBouncer") { 119 keyguardInteractor.primaryBouncerShowing 120 .filterRelevantKeyguardStateAnd { primaryBouncerShowing -> primaryBouncerShowing } 121 .collect { startTransitionTo(KeyguardState.PRIMARY_BOUNCER) } 122 } 123 } 124 125 private fun listenForHubToAlternateBouncer() { 126 scope.launch("$TAG#listenForHubToAlternateBouncer") { 127 keyguardInteractor.alternateBouncerShowing 128 .filterRelevantKeyguardStateAnd { alternateBouncerShowing -> 129 alternateBouncerShowing 130 } 131 .collect { pair -> startTransitionTo(KeyguardState.ALTERNATE_BOUNCER) } 132 } 133 } 134 135 private fun listenForHubToDozing() { 136 scope.launch { 137 powerInteractor.isAsleep 138 .filterRelevantKeyguardStateAnd { isAsleep -> isAsleep } 139 .collect { 140 startTransitionTo( 141 toState = KeyguardState.DOZING, 142 modeOnCanceled = TransitionModeOnCanceled.LAST_VALUE, 143 ) 144 } 145 } 146 } 147 148 private fun listenForHubToOccluded() { 149 if (KeyguardWmStateRefactor.isEnabled) { 150 scope.launch { 151 keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop 152 .filterRelevantKeyguardStateAnd { onTop -> onTop } 153 .collect { maybeStartTransitionToOccludedOrInsecureCamera() } 154 } 155 } else { 156 scope.launch { 157 allOf(keyguardInteractor.isKeyguardOccluded, not(keyguardInteractor.isDreaming)) 158 .filterRelevantKeyguardStateAnd { isOccludedAndNotDreaming -> 159 isOccludedAndNotDreaming 160 } 161 .collect { isOccludedAndNotDreaming -> 162 startTransitionTo(KeyguardState.OCCLUDED) 163 } 164 } 165 } 166 } 167 168 private fun listenForHubToGone() { 169 scope.launch { 170 keyguardInteractor.isKeyguardGoingAway 171 .filterRelevantKeyguardStateAnd { isKeyguardGoingAway -> isKeyguardGoingAway } 172 .collect { startTransitionTo(KeyguardState.GONE) } 173 } 174 } 175 176 companion object { 177 const val TAG = "FromGlanceableHubTransitionInteractor" 178 val DEFAULT_DURATION = 1.seconds 179 val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION 180 val TO_OCCLUDED_DURATION = 450.milliseconds 181 } 182 } 183