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