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