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.systemui.animation.back
18 
19 import android.platform.test.annotations.RequiresFlagsDisabled
20 import android.platform.test.annotations.RequiresFlagsEnabled
21 import android.platform.test.flag.junit.CheckFlagsRule
22 import android.platform.test.flag.junit.DeviceFlagsValueProvider
23 import android.view.animation.Interpolator
24 import android.window.BackEvent
25 import androidx.test.ext.junit.runners.AndroidJUnit4
26 import androidx.test.filters.SmallTest
27 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
28 import com.android.app.animation.Interpolators
29 import com.android.systemui.SysuiTestCase
30 import com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_TIMESTAMP_API
31 import java.util.concurrent.CountDownLatch
32 import java.util.concurrent.TimeUnit
33 import org.junit.Assert.assertEquals
34 import org.junit.Assert.assertFalse
35 import org.junit.Assert.assertTrue
36 import org.junit.Rule
37 import org.junit.Test
38 import org.junit.runner.RunWith
39 import org.mockito.Mockito
40 
41 @SmallTest
42 @RunWith(AndroidJUnit4::class)
43 class FlingOnBackAnimationCallbackTest : SysuiTestCase() {
44 
45     @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
46 
47     @Test
testProgressInterpolationnull48     fun testProgressInterpolation() {
49         val mockInterpolator = Mockito.mock(Interpolator::class.java)
50         val backEvent = backEventOf(0.5f)
51         Mockito.`when`(mockInterpolator.getInterpolation(0.5f)).thenReturn(0.8f)
52         val callback = TestFlingOnBackAnimationCallback(mockInterpolator)
53         callback.onBackStarted(backEvent)
54         assertTrue("Assert onBackStartedCompat called", callback.backStartedCalled)
55         callback.onBackProgressed(backEvent)
56         assertTrue("Assert onBackProgressedCompat called", callback.backProgressedCalled)
57         assertEquals("Assert interpolated progress", 0.8f, callback.progressEvent?.progress)
58     }
59 
60     @Test
61     @RequiresFlagsEnabled(FLAG_PREDICTIVE_BACK_TIMESTAMP_API)
testFlingnull62     fun testFling() {
63         val callback = TestFlingOnBackAnimationCallback(Interpolators.LINEAR)
64         callback.onBackStarted(backEventOf(progress = 0f, frameTime = 0))
65         assertTrue("Assert onBackStartedCompat called", callback.backStartedCalled)
66         callback.onBackProgressed(backEventOf(0f, 8))
67         callback.onBackProgressed(backEventOf(0.2f, 16))
68         callback.onBackProgressed(backEventOf(0.4f, 24))
69         callback.onBackProgressed(backEventOf(0.6f, 32))
70         assertTrue("Assert onBackProgressedCompat called", callback.backProgressedCalled)
71         assertEquals("Assert interpolated progress", 0.6f, callback.progressEvent?.progress)
72         getInstrumentation().runOnMainSync { callback.onBackInvoked() }
73         // Assert that onBackInvoked is not called immediately...
74         assertFalse(callback.backInvokedCalled)
75         // Instead the fling animation is played and eventually onBackInvoked is called.
76         callback.backInvokedLatch.await(1000, TimeUnit.MILLISECONDS)
77         assertTrue(callback.backInvokedCalled)
78     }
79 
80     @Test
81     @RequiresFlagsDisabled(FLAG_PREDICTIVE_BACK_TIMESTAMP_API)
testCallbackWithoutTimestampApinull82     fun testCallbackWithoutTimestampApi() {
83         // Assert that all callback methods are immediately forwarded
84         val callback = TestFlingOnBackAnimationCallback(Interpolators.LINEAR)
85         callback.onBackStarted(backEventOf(progress = 0f, frameTime = 0))
86         assertTrue("Assert onBackStartedCompat called", callback.backStartedCalled)
87         callback.onBackProgressed(backEventOf(0f, 8))
88         assertTrue("Assert onBackProgressedCompat called", callback.backProgressedCalled)
89         callback.onBackInvoked()
90         assertTrue("Assert onBackInvoked called", callback.backInvokedCalled)
91         callback.onBackCancelled()
92         assertTrue("Assert onBackCancelled called", callback.backCancelledCalled)
93     }
94 
backEventOfnull95     private fun backEventOf(progress: Float, frameTime: Long = 0): BackEvent {
96         return BackEvent(10f, 10f, progress, 0, frameTime)
97     }
98 
99     /** Helper class to expose the compat functions for testing */
100     private class TestFlingOnBackAnimationCallback(progressInterpolator: Interpolator) :
101         FlingOnBackAnimationCallback(progressInterpolator) {
102         var backStartedCalled = false
103         var backProgressedCalled = false
104         var backInvokedCalled = false
105         val backInvokedLatch = CountDownLatch(1)
106         var backCancelledCalled = false
107         var progressEvent: BackEvent? = null
108 
onBackStartedCompatnull109         override fun onBackStartedCompat(backEvent: BackEvent) {
110             backStartedCalled = true
111         }
112 
onBackProgressedCompatnull113         override fun onBackProgressedCompat(backEvent: BackEvent) {
114             backProgressedCalled = true
115             progressEvent = backEvent
116         }
117 
onBackInvokedCompatnull118         override fun onBackInvokedCompat() {
119             backInvokedCalled = true
120             backInvokedLatch.countDown()
121         }
122 
onBackCancelledCompatnull123         override fun onBackCancelledCompat() {
124             backCancelledCalled = true
125         }
126     }
127 }
128