1 /*
2  * Copyright (C) 2023 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.systemui.animation.back
18 
19 import android.annotation.IntRange
20 import android.util.DisplayMetrics
21 import android.view.View
22 import android.window.BackEvent
23 import android.window.OnBackAnimationCallback
24 import android.window.OnBackInvokedDispatcher
25 import android.window.OnBackInvokedDispatcher.Priority
26 import com.android.app.animation.Interpolators
27 
28 /**
29  * Generates an [OnBackAnimationCallback] given a [backAnimationSpec]. [onBackProgressed] will be
30  * called on each update passing the current [BackTransformation].
31  *
32  * Optionally, you can specify [onBackStarted], [onBackInvoked], and [onBackCancelled] callbacks.
33  *
34  * @sample com.android.systemui.util.registerAnimationOnBackInvoked
35  */
onBackAnimationCallbackFromnull36 fun onBackAnimationCallbackFrom(
37     backAnimationSpec: BackAnimationSpec,
38     displayMetrics: DisplayMetrics, // TODO(b/265060720): We could remove this
39     onBackProgressed: (BackTransformation) -> Unit,
40     onBackStarted: (BackEvent) -> Unit = {},
<lambda>null41     onBackInvoked: () -> Unit = {},
<lambda>null42     onBackCancelled: () -> Unit = {},
43 ): OnBackAnimationCallback {
44     return object : FlingOnBackAnimationCallback(progressInterpolator = Interpolators.LINEAR) {
45         private var initialY = 0f
46         private val lastTransformation = BackTransformation()
47 
onBackStartedCompatnull48         override fun onBackStartedCompat(backEvent: BackEvent) {
49             initialY = backEvent.touchY
50             onBackStarted(backEvent)
51         }
52 
onBackProgressedCompatnull53         override fun onBackProgressedCompat(backEvent: BackEvent) {
54             val progressY = (backEvent.touchY - initialY) / displayMetrics.heightPixels
55 
56             backAnimationSpec.getBackTransformation(
57                 backEvent = backEvent,
58                 progressY = progressY,
59                 result = lastTransformation,
60             )
61 
62             onBackProgressed(lastTransformation)
63         }
64 
onBackInvokedCompatnull65         override fun onBackInvokedCompat() {
66             onBackInvoked()
67         }
68 
onBackCancelledCompatnull69         override fun onBackCancelledCompat() {
70             onBackCancelled()
71         }
72     }
73 }
74 
75 /**
76  * Register [OnBackAnimationCallback] when View is attached and unregister it when View is detached
77  *
78  * @sample com.android.systemui.util.registerAnimationOnBackInvoked
79  */
registerOnBackInvokedCallbackOnViewAttachednull80 fun View.registerOnBackInvokedCallbackOnViewAttached(
81     onBackInvokedDispatcher: OnBackInvokedDispatcher,
82     onBackAnimationCallback: OnBackAnimationCallback,
83     @Priority @IntRange(from = 0) priority: Int = OnBackInvokedDispatcher.PRIORITY_DEFAULT,
84 ) {
85     addOnAttachStateChangeListener(
86         object : View.OnAttachStateChangeListener {
87             override fun onViewAttachedToWindow(v: View) {
88                 onBackInvokedDispatcher.registerOnBackInvokedCallback(
89                     priority,
90                     onBackAnimationCallback,
91                 )
92             }
93 
94             override fun onViewDetachedFromWindow(v: View) {
95                 removeOnAttachStateChangeListener(this)
96                 onBackInvokedDispatcher.unregisterOnBackInvokedCallback(onBackAnimationCallback)
97             }
98         }
99     )
100 
101     if (isAttachedToWindow) {
102         onBackInvokedDispatcher.registerOnBackInvokedCallback(priority, onBackAnimationCallback)
103     }
104 }
105