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 
21 /**
22  * A handler to specify how a transition should be interrupted.
23  *
24  * @see DefaultInterruptionHandler
25  * @see SceneTransitionsBuilder.interruptionHandler
26  */
27 interface InterruptionHandler {
28     /**
29      * This function is called when [interrupted] is interrupted: it is currently animating between
30      * [interrupted.fromScene] and [interrupted.toScene], and we will now animate to
31      * [newTargetScene].
32      *
33      * If this returns `null`, then the [default behavior][DefaultInterruptionHandler] will be used:
34      * we will animate from [interrupted.currentScene] and chaining will be enabled (see
35      * [InterruptionResult] for more information about chaining).
36      *
37      * @see InterruptionResult
38      */
onInterruptionnull39     fun onInterruption(
40         interrupted: TransitionState.Transition.ChangeScene,
41         newTargetScene: SceneKey,
42     ): InterruptionResult?
43 }
44 
45 /**
46  * The result of an interruption that specifies how we should handle a transition A => B now that we
47  * have to animate to C.
48  *
49  * For instance, if the interrupted transition was A => B and currentScene = B:
50  * - animateFrom = B && chain = true => there will be 2 transitions running in parallel, A => B and
51  *   B => C.
52  * - animateFrom = A && chain = true => there will be 2 transitions running in parallel, B => A and
53  *   A => C.
54  * - animateFrom = B && chain = false => there will be 1 transition running, B => C.
55  * - animateFrom = A && chain = false => there will be 1 transition running, A => C.
56  */
57 class InterruptionResult(
58     /**
59      * The scene we should animate from when transitioning to C.
60      *
61      * Important: This **must** be either [TransitionState.Transition.fromScene] or
62      * [TransitionState.Transition.toScene] of the transition that was interrupted.
63      */
64     val animateFrom: SceneKey,
65 
66     /**
67      * Whether chaining is enabled, i.e. if the new transition to C should run in parallel with the
68      * previous one(s) or if it should be the only remaining transition that is running.
69      */
70     val chain: Boolean = true,
71 )
72 
73 /**
74  * The default interruption handler: we animate from [TransitionState.Transition.currentScene] and
75  * chaining is enabled.
76  */
77 object DefaultInterruptionHandler : InterruptionHandler {
78     override fun onInterruption(
79         interrupted: TransitionState.Transition.ChangeScene,
80         newTargetScene: SceneKey,
81     ): InterruptionResult {
82         return InterruptionResult(animateFrom = interrupted.currentScene, chain = true)
83     }
84 }
85