1 /*
2  * 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.compose.animation.scene
18 
19 import com.android.compose.animation.scene.content.state.TransitionState
20 import kotlinx.coroutines.CoroutineScope
21 
22 /** Trigger a one-off transition to show or hide an overlay. */
showOrHideOverlaynull23 internal fun CoroutineScope.showOrHideOverlay(
24     layoutState: MutableSceneTransitionLayoutStateImpl,
25     overlay: OverlayKey,
26     fromOrToScene: SceneKey,
27     isShowing: Boolean,
28     transitionKey: TransitionKey?,
29     replacedTransition: TransitionState.Transition.ShowOrHideOverlay?,
30     reversed: Boolean,
31 ): TransitionState.Transition.ShowOrHideOverlay {
32     val targetProgress = if (reversed) 0f else 1f
33     val (fromContent, toContent) =
34         if (isShowing xor reversed) {
35             fromOrToScene to overlay
36         } else {
37             overlay to fromOrToScene
38         }
39 
40     val oneOffAnimation = OneOffAnimation()
41     val transition =
42         OneOffShowOrHideOverlayTransition(
43             overlay = overlay,
44             fromOrToScene = fromOrToScene,
45             fromContent = fromContent,
46             toContent = toContent,
47             isEffectivelyShown = isShowing,
48             key = transitionKey,
49             replacedTransition = replacedTransition,
50             oneOffAnimation = oneOffAnimation,
51         )
52 
53     animateContent(
54         layoutState = layoutState,
55         transition = transition,
56         oneOffAnimation = oneOffAnimation,
57         targetProgress = targetProgress,
58     )
59 
60     return transition
61 }
62 
63 /** Trigger a one-off transition to replace an overlay by another one. */
replaceOverlaynull64 internal fun CoroutineScope.replaceOverlay(
65     layoutState: MutableSceneTransitionLayoutStateImpl,
66     fromOverlay: OverlayKey,
67     toOverlay: OverlayKey,
68     transitionKey: TransitionKey?,
69     replacedTransition: TransitionState.Transition.ReplaceOverlay?,
70     reversed: Boolean,
71 ): TransitionState.Transition.ReplaceOverlay {
72     val targetProgress = if (reversed) 0f else 1f
73     val effectivelyShownOverlay = if (reversed) fromOverlay else toOverlay
74 
75     val oneOffAnimation = OneOffAnimation()
76     val transition =
77         OneOffOverlayReplacingTransition(
78             fromOverlay = fromOverlay,
79             toOverlay = toOverlay,
80             effectivelyShownOverlay = effectivelyShownOverlay,
81             key = transitionKey,
82             replacedTransition = replacedTransition,
83             oneOffAnimation = oneOffAnimation,
84         )
85 
86     animateContent(
87         layoutState = layoutState,
88         transition = transition,
89         oneOffAnimation = oneOffAnimation,
90         targetProgress = targetProgress,
91     )
92 
93     return transition
94 }
95 
96 private class OneOffShowOrHideOverlayTransition(
97     overlay: OverlayKey,
98     fromOrToScene: SceneKey,
99     fromContent: ContentKey,
100     toContent: ContentKey,
101     override val isEffectivelyShown: Boolean,
102     override val key: TransitionKey?,
103     replacedTransition: TransitionState.Transition?,
104     private val oneOffAnimation: OneOffAnimation,
105 ) :
106     TransitionState.Transition.ShowOrHideOverlay(
107         overlay,
108         fromOrToScene,
109         fromContent,
110         toContent,
111         replacedTransition,
112     ) {
113     override val progress: Float
114         get() = oneOffAnimation.progress
115 
116     override val progressVelocity: Float
117         get() = oneOffAnimation.progressVelocity
118 
119     override val isInitiatedByUserInput: Boolean = false
120     override val isUserInputOngoing: Boolean = false
121 
runnull122     override suspend fun run() {
123         oneOffAnimation.run()
124     }
125 
freezeAndAnimateToCurrentStatenull126     override fun freezeAndAnimateToCurrentState() {
127         oneOffAnimation.freezeAndAnimateToCurrentState()
128     }
129 }
130 
131 private class OneOffOverlayReplacingTransition(
132     fromOverlay: OverlayKey,
133     toOverlay: OverlayKey,
134     override val effectivelyShownOverlay: OverlayKey,
135     override val key: TransitionKey?,
136     replacedTransition: TransitionState.Transition?,
137     private val oneOffAnimation: OneOffAnimation,
138 ) : TransitionState.Transition.ReplaceOverlay(fromOverlay, toOverlay, replacedTransition) {
139     override val progress: Float
140         get() = oneOffAnimation.progress
141 
142     override val progressVelocity: Float
143         get() = oneOffAnimation.progressVelocity
144 
145     override val isInitiatedByUserInput: Boolean = false
146     override val isUserInputOngoing: Boolean = false
147 
runnull148     override suspend fun run() {
149         oneOffAnimation.run()
150     }
151 
freezeAndAnimateToCurrentStatenull152     override fun freezeAndAnimateToCurrentState() {
153         oneOffAnimation.freezeAndAnimateToCurrentState()
154     }
155 }
156