1 /*
<lambda>null2  * 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 androidx.activity.BackEventCompat
20 import androidx.activity.compose.PredictiveBackHandler
21 import androidx.compose.animation.core.snap
22 import androidx.compose.foundation.gestures.Orientation
23 import androidx.compose.runtime.Composable
24 import com.android.compose.animation.scene.UserActionResult.ChangeScene
25 import com.android.compose.animation.scene.UserActionResult.HideOverlay
26 import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
27 import com.android.compose.animation.scene.UserActionResult.ShowOverlay
28 import com.android.compose.animation.scene.transition.animateProgress
29 import kotlinx.coroutines.flow.Flow
30 import kotlinx.coroutines.flow.first
31 import kotlinx.coroutines.flow.map
32 
33 @Composable
34 internal fun PredictiveBackHandler(
35     layoutImpl: SceneTransitionLayoutImpl,
36     result: UserActionResult?,
37 ) {
38     PredictiveBackHandler(enabled = result != null) { events: Flow<BackEventCompat> ->
39         if (result == null) {
40             // Note: We have to collect progress otherwise PredictiveBackHandler will throw.
41             events.first()
42             return@PredictiveBackHandler
43         }
44 
45         val animation =
46             createSwipeAnimation(
47                 layoutImpl,
48                 if (result.transitionKey != null) {
49                     result
50                 } else {
51                     result.copy(transitionKey = TransitionKey.PredictiveBack)
52                 },
53                 isUpOrLeft = false,
54                 // Note that the orientation does not matter here given that it's only used to
55                 // compute the distance. In our case the distance is always 1f.
56                 orientation = Orientation.Horizontal,
57                 distance = 1f,
58             )
59 
60         animateProgress(
61             state = layoutImpl.state,
62             animation = animation,
63             progress = events.map { it.progress },
64 
65             // Use the transformationSpec.progressSpec. We will lazily access it later once the
66             // transition has been started, because at this point the transformation spec of the
67             // transition is not computed yet.
68             commitSpec = null,
69 
70             // The predictive back APIs will automatically animate the progress for us in this case
71             // so there is no need to animate it.
72             cancelSpec = snap(),
73             animationScope = layoutImpl.animationScope,
74         )
75     }
76 }
77 
copynull78 private fun UserActionResult.copy(
79     transitionKey: TransitionKey? = this.transitionKey
80 ): UserActionResult {
81     return when (this) {
82         is ChangeScene -> copy(transitionKey = transitionKey)
83         is ShowOverlay -> copy(transitionKey = transitionKey)
84         is HideOverlay -> copy(transitionKey = transitionKey)
85         is ReplaceByOverlay -> copy(transitionKey = transitionKey)
86     }
87 }
88