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