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.graphics.Rect
19 import android.platform.uiautomator_helpers.BetterFling
20 import android.platform.uiautomator_helpers.BetterScroll
21 import android.platform.uiautomator_helpers.DeviceHelpers.waitForObj
22 import androidx.test.uiautomator.By
23 import androidx.test.uiautomator.Direction
24 import androidx.test.uiautomator.UiObject2
25 
26 /**
27  * Ui object with diagnostic metadata and flake-free gestures.
28  *
29  * @param [uiObject] UI Automator object
30  * @param [name] Name of the object for diags
31  */
32 class TaplUiObject constructor(val uiObject: UiObject2, private val name: String) {
33     // Margins used for gestures (avoids touching too close to the object's edge).
34     private var mMarginLeft = 5
35     private var mMarginTop = 5
36     private var mMarginRight = 5
37     private var mMarginBottom = 5
38 
39     /** Sets the margins used for gestures in pixels. */
setGestureMarginnull40     fun setGestureMargin(margin: Int) {
41         setGestureMargins(margin, margin, margin, margin)
42     }
43 
44     /** Sets the margins used for gestures in pixels. */
setGestureMarginsnull45     fun setGestureMargins(left: Int, top: Int, right: Int, bottom: Int) {
46         mMarginLeft = left
47         mMarginTop = top
48         mMarginRight = right
49         mMarginBottom = bottom
50     }
51 
52     /** Returns this object's visible bounds with the margins removed. */
getVisibleBoundsForGesturesnull53     private fun getVisibleBoundsForGestures(): Rect {
54         val ret: Rect = uiObject.visibleBounds
55         ret.left = ret.left + mMarginLeft
56         ret.top = ret.top + mMarginTop
57         ret.right = ret.right - mMarginRight
58         ret.bottom = ret.bottom - mMarginBottom
59         return ret
60     }
61 
62     /** Wait for the object to become clickable and enabled, then clicks the object. */
clicknull63     fun click() {
64         Gestures.click(uiObject, name)
65     }
66 
67     /**
68      * Waits for a child UI object with a given resource id. Fails if the object is not visible.
69      *
70      * @param [resourceId] Resource id.
71      * @param [childObjectName] Name of the object for diags.
72      * @return The found UI object.
73      */
waitForChildObjectnull74     fun waitForChildObject(childResourceId: String, childObjectName: String): TaplUiObject {
75         val selector = By.res(uiObject.applicationPackage, childResourceId)
76         val childObject = uiObject.waitForObj(selector)
77         return TaplUiObject(childObject, childObjectName)
78     }
79 
80     /**
81      * Performs a scroll gesture on this object.
82      *
83      * @param direction The direction in which to scroll.
84      * @param percent The distance to scroll as a percentage of this object's visible size.
85      * @param verifyIsScrollable Whether to verify that the object is scrollable.
86      */
scrollnull87     fun scroll(direction: Direction, percent: Float, verifyIsScrollable: Boolean = false) {
88         if (verifyIsScrollable) {
89             Gestures.waitForObjectEnabled(uiObject, name)
90             Gestures.waitForObjectScrollable(uiObject, name)
91         }
92 
93         require(percent >= 0.0f) { "Percent must be greater than 0.0f" }
94         require(percent <= 1.0f) { "Percent must be less than 1.0f" }
95 
96         // To scroll, we swipe in the opposite direction
97         val swipeDirection: Direction = Direction.reverse(direction)
98 
99         // Scroll by performing repeated swipes
100         val bounds: Rect = getVisibleBoundsForGestures()
101         val segment = Math.min(percent, 1.0f)
102         BetterScroll.scroll(bounds, swipeDirection, segment)
103     }
104 
105     /**
106      * Performs a scroll gesture on this object via flinging.
107      *
108      * @param scrollDirection The direction in which to scroll. For example, if the direction is UP,
109      *   the gesture will start in the top center of this object and finish somewhere lower
110      *   depending on [percent].
111      * @param percent The distance to scroll as a percentage of this object's visible size.
112      * @param verifyIsScrollable Whether to verify that the object is scrollable.
113      */
scrollWithFlingnull114     fun scrollWithFling(
115         scrollDirection: Direction,
116         percent: Float,
117         verifyIsScrollable: Boolean = false
118     ) {
119         // To scroll, we fling in the opposite direction
120         fling(Direction.reverse(scrollDirection), percent, verifyIsScrollable)
121     }
122 
123     /**
124      * Performs a fling gesture on this object.
125      *
126      * @param gestureDirection The direction in which to fling. For example, if the direction is UP,
127      *   the gesture will start in the bottom center of this object and finish somewhere higher
128      *   depending on [percent].
129      * @param percent The distance to scroll as a percentage of this object's visible size.
130      * @param verifyIsScrollable Whether to verify that the object is scrollable.
131      */
flingnull132     fun fling(gestureDirection: Direction, percent: Float, verifyIsScrollable: Boolean = false) {
133         if (verifyIsScrollable) {
134             Gestures.waitForObjectEnabled(uiObject, name)
135             Gestures.waitForObjectScrollable(uiObject, name)
136         }
137 
138         require(percent >= 0.0f) { "Percent must be at least 0.0f" }
139         require(percent <= 1.0f) { "Percent must be at most 1.0f" }
140 
141         val bounds: Rect = getVisibleBoundsForGestures()
142         BetterFling.fling(bounds, gestureDirection, percentage = percent)
143     }
144 }
145