1 /* 2 * 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 18 package com.android.systemui.keyguard.ui.viewmodel 19 20 import android.os.Handler 21 import android.transition.Transition 22 import android.transition.TransitionManager 23 import android.util.Log 24 import androidx.constraintlayout.widget.ConstraintLayout 25 import com.android.systemui.dagger.qualifiers.Main 26 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor 27 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config 28 import javax.inject.Inject 29 import kotlinx.coroutines.flow.MutableStateFlow 30 import kotlinx.coroutines.flow.asStateFlow 31 32 data class TransitionData( 33 val config: Config, 34 val start: Long = System.currentTimeMillis(), 35 ) 36 37 class KeyguardBlueprintViewModel 38 @Inject 39 constructor( 40 @Main private val handler: Handler, 41 keyguardBlueprintInteractor: KeyguardBlueprintInteractor, 42 ) { 43 val blueprint = keyguardBlueprintInteractor.blueprint 44 val blueprintId = keyguardBlueprintInteractor.blueprintId 45 val refreshTransition = keyguardBlueprintInteractor.refreshTransition 46 47 private val _currentTransition = MutableStateFlow<TransitionData?>(null) 48 val currentTransition = _currentTransition.asStateFlow() 49 50 private val runningTransitions = mutableSetOf<Transition>() 51 private val transitionListener = 52 object : Transition.TransitionListener { onTransitionCancelnull53 override fun onTransitionCancel(transition: Transition) { 54 if (DEBUG) Log.e(TAG, "onTransitionCancel: ${transition::class.simpleName}") 55 updateTransitions(null) { remove(transition) } 56 } 57 onTransitionEndnull58 override fun onTransitionEnd(transition: Transition) { 59 if (DEBUG) Log.e(TAG, "onTransitionEnd: ${transition::class.simpleName}") 60 updateTransitions(null) { remove(transition) } 61 } 62 onTransitionPausenull63 override fun onTransitionPause(transition: Transition) { 64 if (DEBUG) Log.i(TAG, "onTransitionPause: ${transition::class.simpleName}") 65 updateTransitions(null) { remove(transition) } 66 } 67 onTransitionResumenull68 override fun onTransitionResume(transition: Transition) { 69 if (DEBUG) Log.i(TAG, "onTransitionResume: ${transition::class.simpleName}") 70 updateTransitions(null) { add(transition) } 71 } 72 onTransitionStartnull73 override fun onTransitionStart(transition: Transition) { 74 if (DEBUG) Log.i(TAG, "onTransitionStart: ${transition::class.simpleName}") 75 updateTransitions(null) { add(transition) } 76 } 77 } 78 updateTransitionsnull79 fun updateTransitions(data: TransitionData?, mutate: MutableSet<Transition>.() -> Unit) { 80 runningTransitions.mutate() 81 82 if (runningTransitions.size <= 0) _currentTransition.value = null 83 else if (data != null) _currentTransition.value = data 84 } 85 runTransitionnull86 fun runTransition( 87 constraintLayout: ConstraintLayout, 88 transition: Transition, 89 config: Config, 90 apply: () -> Unit, 91 ) { 92 val currentPriority = currentTransition.value?.let { it.config.type.priority } ?: -1 93 if (config.checkPriority && config.type.priority < currentPriority) { 94 if (DEBUG) { 95 Log.w( 96 TAG, 97 "runTransition: skipping ${transition::class.simpleName}: " + 98 "currentPriority=$currentPriority; config=$config" 99 ) 100 } 101 apply() 102 return 103 } 104 105 if (DEBUG) { 106 Log.i( 107 TAG, 108 "runTransition: running ${transition::class.simpleName}: " + 109 "currentPriority=$currentPriority; config=$config" 110 ) 111 } 112 113 // beginDelayedTransition makes a copy, so we temporarially add the uncopied transition to 114 // the running set until the copy is started by the handler. 115 updateTransitions(TransitionData(config)) { add(transition) } 116 transition.addListener(transitionListener) 117 118 handler.post { 119 if (config.terminatePrevious) { 120 TransitionManager.endTransitions(constraintLayout) 121 } 122 123 TransitionManager.beginDelayedTransition(constraintLayout, transition) 124 apply() 125 126 // Delay removal until after copied transition has started 127 handler.post { updateTransitions(null) { remove(transition) } } 128 } 129 } 130 131 companion object { 132 private const val TAG = "KeyguardBlueprintViewModel" 133 private const val DEBUG = false 134 } 135 } 136