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.os.SystemClock.sleep 19 import android.platform.uiautomatorhelpers.BetterSwipe 20 import android.platform.uiautomatorhelpers.BetterSwipe.Swipe 21 import android.platform.uiautomatorhelpers.WaitUtils.ensureThat 22 import android.provider.Settings 23 import androidx.test.platform.app.InstrumentationRegistry 24 import androidx.test.uiautomator.StaleObjectException 25 import androidx.test.uiautomator.UiObject2 26 import java.time.Duration 27 28 /** 29 * A collection of gestures for UI objects that implements flake-proof patterns and adds 30 * diagnostics. Don't use these gestures directly from the test, this class should be used only by 31 * TAPL. 32 */ 33 object Gestures { 34 private val WAIT_TIME = Duration.ofSeconds(10) 35 waitForObjectConditionnull36 private fun waitForObjectCondition( 37 objectName: String, 38 conditionName: String, 39 condition: () -> Boolean, 40 ) { 41 ensureThat( 42 condition = condition, 43 description = "UI object '$objectName' becomes $conditionName", 44 timeout = WAIT_TIME, 45 ) 46 } 47 waitForObjectEnablednull48 internal fun waitForObjectEnabled(uiObject: UiObject2, objectName: String) { 49 waitForObjectCondition(objectName, "enabled") { uiObject.isEnabled() } 50 } 51 waitForObjectClickablenull52 private fun waitForObjectClickable(uiObject: UiObject2, waitReason: String) { 53 waitForObjectCondition(waitReason, "clickable") { uiObject.isClickable() } 54 } 55 waitForObjectLongClickablenull56 private fun waitForObjectLongClickable(uiObject: UiObject2, waitReason: String) { 57 waitForObjectCondition(waitReason, "long-clickable") { uiObject.isLongClickable() } 58 } 59 waitForObjectScrollablenull60 internal fun waitForObjectScrollable(uiObject: UiObject2, waitReason: String) { 61 waitForObjectCondition(waitReason, "scrollable") { uiObject.isScrollable() } 62 } 63 64 /** 65 * Wait for the object to become clickable and enabled, then clicks the object. 66 * 67 * @param [uiObject] The object to click 68 * @param [objectName] Name of the object for diags 69 */ 70 @JvmStatic clicknull71 fun click(uiObject: UiObject2, objectName: String) { 72 try { 73 waitForObjectEnabled(uiObject, objectName) 74 waitForObjectClickable(uiObject, objectName) 75 clickNow(uiObject) 76 } catch (e: StaleObjectException) { 77 throw AssertionError( 78 "UI object '$objectName' has disappeared from the screen during the click gesture.", 79 e, 80 ) 81 } 82 } 83 84 /** 85 * Waits for the object to become long-clickable and enabled, then presses the object down. 86 * 87 * @param [uiObject] The object to click 88 * @param [objectName] Name of the object for diags 89 * @param [whileHoldingFn] lambda to call between press and release. 90 */ 91 @JvmStatic longClickDownUpnull92 fun longClickDownUp( 93 uiObject: UiObject2, 94 objectName: String, 95 whileHoldingFn: (Swipe.() -> Unit), 96 ) { 97 try { 98 waitForObjectEnabled(uiObject, objectName) 99 waitForObjectLongClickable(uiObject, objectName) 100 val context = InstrumentationRegistry.getInstrumentation().targetContext 101 BetterSwipe.swipe(uiObject.visibleCenter) { 102 103 // press for twice the long press timeout, just to make sure. 104 val longPressMsec = 105 Settings.Secure.getInt(context.contentResolver, "long_press_timeout") 106 sleep(longPressMsec.toLong() * 2) 107 whileHoldingFn() 108 } 109 } catch (e: StaleObjectException) { 110 throw AssertionError( 111 "UI object '$objectName' has disappeared from " + 112 "the screen during the long click gesture.", 113 e, 114 ) 115 } 116 } 117 118 /** 119 * Click on the ui object right away without waiting for animation. 120 * 121 * [UiObject2.click] would wait for all animations finished before clicking. Not waiting for 122 * animations because in some scenarios there is a playing animations when the click is 123 * attempted. 124 */ clickNownull125 private fun clickNow(uiObject: UiObject2) { 126 BetterSwipe.swipe(uiObject.visibleCenter) 127 } 128 } 129