1 /*
2  * Copyright (C) 2022 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 package android.platform.test.scenario.tapl_common
17 
18 import android.platform.uiautomator_helpers.BetterSwipe
19 import android.platform.uiautomator_helpers.WaitUtils.ensureThat
20 import androidx.test.uiautomator.StaleObjectException
21 import androidx.test.uiautomator.UiObject2
22 import java.time.Duration
23 
24 /**
25  * A collection of gestures for UI objects that implements flake-proof patterns and adds
26  * diagnostics. Don't use these gestures directly from the test, this class should be used only by
27  * TAPL.
28  */
29 object Gestures {
30     private val WAIT_TIME = Duration.ofSeconds(10)
31 
waitForObjectConditionnull32     private fun waitForObjectCondition(
33         objectName: String,
34         conditionName: String,
35         condition: () -> Boolean
36     ) {
37         ensureThat(
38             condition = condition,
39             description = "UI object '$objectName' becomes $conditionName",
40             timeout = WAIT_TIME
41         )
42     }
43 
waitForObjectEnablednull44     internal fun waitForObjectEnabled(uiObject: UiObject2, objectName: String) {
45         waitForObjectCondition(objectName, "enabled") { uiObject.isEnabled() }
46     }
47 
waitForObjectClickablenull48     private fun waitForObjectClickable(uiObject: UiObject2, waitReason: String) {
49         waitForObjectCondition(waitReason, "clickable") { uiObject.isClickable() }
50     }
51 
waitForObjectLongClickablenull52     private fun waitForObjectLongClickable(uiObject: UiObject2, waitReason: String) {
53         waitForObjectCondition(waitReason, "long-clickable") { uiObject.isLongClickable() }
54     }
55 
waitForObjectScrollablenull56     internal fun waitForObjectScrollable(uiObject: UiObject2, waitReason: String) {
57         waitForObjectCondition(waitReason, "scrollable") { uiObject.isScrollable() }
58     }
59 
60     /**
61      * Wait for the object to become clickable and enabled, then clicks the object.
62      *
63      * @param [uiObject] The object to click
64      * @param [objectName] Name of the object for diags
65      */
66     @JvmStatic
clicknull67     fun click(uiObject: UiObject2, objectName: String) {
68         try {
69             waitForObjectEnabled(uiObject, objectName)
70             waitForObjectClickable(uiObject, objectName)
71             clickNow(uiObject)
72         } catch (e: StaleObjectException) {
73             throw AssertionError(
74                 "UI object '$objectName' has disappeared from the screen during the click gesture.",
75                 e
76             )
77         }
78     }
79 
80     /** The result of [longClickDown]. The caller has to call the [up] method. */
81     class LongClick internal constructor(swipe: BetterSwipe.Swipe) {
82         private val mSwipe: BetterSwipe.Swipe = swipe
83 
upnull84         fun up() {
85             mSwipe.release()
86         }
87     }
88 
89     /**
90      * Waits for the object to become long-clickable and enabled, then presses the object down.
91      *
92      * @param [uiObject] The object to click
93      * @param [objectName] Name of the object for diags
94      * @return the object with [LongClick.up] method that needs to be called.
95      */
96     @JvmStatic
longClickDownnull97     fun longClickDown(uiObject: UiObject2, objectName: String): LongClick {
98         try {
99             waitForObjectEnabled(uiObject, objectName)
100             waitForObjectLongClickable(uiObject, objectName)
101             return LongClick(BetterSwipe.from(uiObject.visibleCenter))
102         } catch (e: StaleObjectException) {
103             throw AssertionError(
104                 "UI object '$objectName' has disappeared from " +
105                     "the screen during the long click gesture.",
106                 e
107             )
108         }
109     }
110 
111     /**
112      * Click on the ui object right away without waiting for animation.
113      *
114      * [UiObject2.click] would wait for all animations finished before clicking. Not waiting for
115      * animations because in some scenarios there is a playing animations when the click is
116      * attempted.
117      */
clickNownull118     private fun clickNow(uiObject: UiObject2) {
119         BetterSwipe.from(uiObject.visibleCenter).release()
120     }
121 }
122