1 /*
2  * Copyright (C) 2021 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 package com.android.systemui.unfold.util
17 
18 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
19 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
20 
21 /**
22  * Manages progress listeners that can have smaller lifespan than the unfold animation.
23  *
24  * Allows to limit getting transition updates to only when
25  * [ScopedUnfoldTransitionProgressProvider.setReadyToHandleTransition] is called with
26  * readyToHandleTransition = true
27  *
28  * If the transition has already started by the moment when the clients are ready to play the
29  * transition then it will report transition started callback and current animation progress.
30  */
31 open class ScopedUnfoldTransitionProgressProvider
32 @JvmOverloads
33 constructor(source: UnfoldTransitionProgressProvider? = null) :
34     UnfoldTransitionProgressProvider, TransitionProgressListener {
35 
36     private var source: UnfoldTransitionProgressProvider? = null
37 
38     private val listeners: MutableList<TransitionProgressListener> = mutableListOf()
39 
40     private var isReadyToHandleTransition = false
41     private var isTransitionRunning = false
42     private var lastTransitionProgress = PROGRESS_UNSET
43 
44     init {
45         setSourceProvider(source)
46     }
47     /**
48      * Sets the source for the unfold transition progress updates. Replaces current provider if it
49      * is already set
50      *
51      * @param provider transition provider that emits transition progress updates
52      */
53     fun setSourceProvider(provider: UnfoldTransitionProgressProvider?) {
54         source?.removeCallback(this)
55 
56         if (provider != null) {
57             source = provider
58             provider.addCallback(this)
59         } else {
60             source = null
61         }
62     }
63 
64     /**
65      * Allows to notify this provide whether the listeners can play the transition or not.
66      *
67      * Call this method with readyToHandleTransition = true when all listeners are ready to consume
68      * the transition progress events.
69      *
70      * Call it with readyToHandleTransition = false when listeners can't process the events.
71      */
72     fun setReadyToHandleTransition(isReadyToHandleTransition: Boolean) {
73         if (isTransitionRunning) {
74             if (isReadyToHandleTransition) {
75                 listeners.forEach { it.onTransitionStarted() }
76                 if (lastTransitionProgress != PROGRESS_UNSET) {
77                     listeners.forEach { it.onTransitionProgress(lastTransitionProgress) }
78                 }
79             } else {
80                 isTransitionRunning = false
81                 listeners.forEach { it.onTransitionFinished() }
82             }
83         }
84         this.isReadyToHandleTransition = isReadyToHandleTransition
85     }
86 
87     override fun addCallback(listener: TransitionProgressListener) {
88         listeners += listener
89     }
90 
91     override fun removeCallback(listener: TransitionProgressListener) {
92         listeners -= listener
93     }
94 
95     override fun destroy() {
96         source?.removeCallback(this)
97         source?.destroy()
98     }
99 
100     override fun onTransitionStarted() {
101         isTransitionRunning = true
102         if (isReadyToHandleTransition) {
103             listeners.forEach { it.onTransitionStarted() }
104         }
105     }
106 
107     override fun onTransitionProgress(progress: Float) {
108         if (isReadyToHandleTransition) {
109             listeners.forEach { it.onTransitionProgress(progress) }
110         }
111         lastTransitionProgress = progress
112     }
113 
114     override fun onTransitionFinishing() {
115         if (isReadyToHandleTransition) {
116             listeners.forEach { it.onTransitionFinishing() }
117         }
118     }
119 
120     override fun onTransitionFinished() {
121         if (isReadyToHandleTransition) {
122             listeners.forEach { it.onTransitionFinished() }
123         }
124         isTransitionRunning = false
125         lastTransitionProgress = PROGRESS_UNSET
126     }
127 
128     companion object {
129         private const val PROGRESS_UNSET = -1f
130     }
131 }
132