1 /* <lambda>null2 * Copyright (C) 2024 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.communal.domain.interactor.CommunalInteractor 22 import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel 23 import com.android.systemui.communal.shared.model.CommunalScenes 24 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository 25 import com.android.systemui.keyguard.shared.model.KeyguardState 26 import com.android.systemui.keyguard.shared.model.TransitionInfo 27 import com.android.systemui.keyguard.shared.model.TransitionState 28 import com.android.systemui.scene.shared.flag.SceneContainerFlag 29 import com.android.systemui.util.kotlin.sample 30 import java.util.UUID 31 import javax.inject.Inject 32 33 class GlanceableHubTransitions 34 @Inject 35 constructor( 36 private val transitionInteractor: KeyguardTransitionInteractor, 37 private val transitionRepository: KeyguardTransitionRepository, 38 private val communalInteractor: CommunalInteractor, 39 ) { 40 /** 41 * Listens for the glanceable hub transition to the specified scene and directly drives the 42 * keyguard transition between the lockscreen and the hub. 43 * 44 * The glanceable hub transition progress is used as the source of truth as it cannot be driven 45 * externally. The progress is used for both transitions caused by user touch input or by 46 * programmatic changes. 47 */ 48 suspend fun listenForGlanceableHubTransition( 49 transitionOwnerName: String, 50 fromState: KeyguardState, 51 toState: KeyguardState, 52 ) { 53 // TODO(b/336576536): Check if adaptation for scene framework is needed 54 if (SceneContainerFlag.isEnabled) return 55 val toScene = 56 if (fromState == KeyguardState.GLANCEABLE_HUB) { 57 CommunalScenes.Blank 58 } else { 59 CommunalScenes.Communal 60 } 61 var transitionId: UUID? = null 62 63 communalInteractor 64 .transitionProgressToScene(toScene) 65 .sample( 66 transitionInteractor.startedKeyguardState, 67 ::Pair, 68 ) 69 .collect { (transitionProgress, lastStartedState) -> 70 val id = transitionId 71 if (id == null) { 72 // No transition started. 73 if ( 74 transitionProgress is CommunalTransitionProgressModel.Transition && 75 lastStartedState == fromState 76 ) { 77 transitionId = 78 transitionRepository.startTransition( 79 TransitionInfo( 80 ownerName = transitionOwnerName, 81 from = fromState, 82 to = toState, 83 animator = null, // transition will be manually controlled 84 ) 85 ) 86 } 87 } else { 88 if (lastStartedState != toState) { 89 return@collect 90 } 91 // An existing `id` means a transition is started, and calls to 92 // `updateTransition` will control it until FINISHED or CANCELED 93 val nextState: TransitionState 94 val progressFraction: Float 95 when (transitionProgress) { 96 is CommunalTransitionProgressModel.Idle -> { 97 if (transitionProgress.scene == toScene) { 98 nextState = TransitionState.FINISHED 99 progressFraction = 1f 100 } else { 101 nextState = TransitionState.CANCELED 102 progressFraction = 0f 103 } 104 } 105 is CommunalTransitionProgressModel.Transition -> { 106 nextState = TransitionState.RUNNING 107 progressFraction = transitionProgress.progress 108 } 109 is CommunalTransitionProgressModel.OtherTransition -> { 110 // Shouldn't happen but if another transition starts during the 111 // current one, mark the current one as canceled. 112 nextState = TransitionState.CANCELED 113 progressFraction = 0f 114 } 115 } 116 transitionRepository.updateTransition( 117 id, 118 progressFraction, 119 nextState, 120 ) 121 122 if ( 123 nextState == TransitionState.CANCELED || 124 nextState == TransitionState.FINISHED 125 ) { 126 transitionId = null 127 } 128 129 // If canceled, just put the state back. 130 if (nextState == TransitionState.CANCELED) { 131 transitionRepository.startTransition( 132 TransitionInfo( 133 ownerName = transitionOwnerName, 134 from = toState, 135 to = fromState, 136 animator = 137 ValueAnimator().apply { 138 interpolator = Interpolators.LINEAR 139 duration = 0 140 } 141 ) 142 ) 143 } 144 } 145 } 146 } 147 } 148