UiObject.java revision 1dc7d12406947faaee8454c6efb2a0631f5da573
1e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu/*
2e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Copyright (C) 2012 The Android Open Source Project
3e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
4e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Licensed under the Apache License, Version 2.0 (the "License");
5e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * you may not use this file except in compliance with the License.
6e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * You may obtain a copy of the License at
7e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
8e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *      http://www.apache.org/licenses/LICENSE-2.0
9e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
10e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Unless required by applicable law or agreed to in writing, software
11e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * distributed under the License is distributed on an "AS IS" BASIS,
12e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * See the License for the specific language governing permissions and
14e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * limitations under the License.
15e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
16e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
17e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupackage com.android.uiautomator.core;
18e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
19c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtazimport android.graphics.Point;
20e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.graphics.Rect;
21e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.SystemClock;
22e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.util.Log;
23e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.KeyEvent;
24c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtazimport android.view.MotionEvent.PointerCoords;
25e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.accessibility.AccessibilityNodeInfo;
26e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
27e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu/**
2846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * A UiObject is a representation of a UI element. It is not in any way directly bound to a
2946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * UI element as an object reference. A UiObject holds information to help it
3046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * locate a matching UI element at runtime based on the {@link UiSelector} properties specified in
3146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * its constructor. Since a UiObject is a representative for a UI element, it can
3246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * be reused for different views with matching UI elements.
33dbba713661688a285e701a006ce2d199296ac328Guang Zhu * @since API Level 16
34e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
35e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupublic class UiObject {
36e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final String LOG_TAG = UiObject.class.getSimpleName();
37dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
38dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
39dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
40e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_SELECTOR_TIMEOUT = 10 * 1000;
41dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
42dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
43dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
44e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_SELECTOR_POLL = 1000;
45e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // set a default timeout to 5.5s, since ANR threshold is 5s
46dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
47dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
48dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
49e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_WINDOW_TMEOUT = 5500;
50dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
51c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @since API Level 16
52c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     **/
53c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    protected static final int SWIPE_MARGIN_LIMIT = 5;
54c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    /**
55dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 17
56dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
576088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz    protected static final long WAIT_FOR_EVENT_TMEOUT = 3 * 1000;
58dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
59c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @since API Level 18
60dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
61c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    protected static final int FINGER_TOUCH_HALF_WIDTH = 20;
62e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
637f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz    private final UiSelector mSelector;
64ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    private final UiAutomatorBridge mUiAutomationBridge;
65e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
66e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
673d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Constructs a UiObject to represent a specific UI element matched by the specified
683d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * {@link UiSelector} selector properties.
69e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param selector
70dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
71e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
724ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject(UiSelector selector) {
73e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mUiAutomationBridge = UiDevice.getInstance().getAutomatorBridge();
74e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mSelector = selector;
75e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
76e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
77e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
783d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Debugging helper. A test can dump the properties of a selector as a string
793d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * to its logs if needed. <code>getSelector().toString();</code>
803d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
814ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @return {@link UiSelector}
82dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
83e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
844ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public final UiSelector getSelector() {
85f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
864ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiSelector(mSelector);
87e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
88e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
89e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
903d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Retrieves the {@link QueryController} to translate a {@link UiSelector} selector
913d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * into an {@link AccessibilityNodeInfo}.
923d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
93e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link QueryController}
94e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
95ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    QueryController getQueryController() {
96e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getQueryController();
97e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
98e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
99e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1003d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Retrieves the {@link InteractionController} to perform finger actions such as tapping,
1013d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * swiping or entering text.
1023d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
103e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link InteractionController}
104e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
105ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    InteractionController getInteractionController() {
106e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getInteractionController();
107e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
108e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
109e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1103d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Creates a new UiObject representing a child UI element of the element currently represented
1113d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * by this UiObject.
1123d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1133d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param selector for UI element to match
1143d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return a new UiObject representing the matched UI element
115dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
116e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
1174ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException {
118f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(selector);
1194ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiObject(getSelector().childSelector(selector));
120e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
121e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
122e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1233d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Creates a new UiObject representing a child UI element from the parent element currently
1243d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * represented by this object. Essentially this is starting the search from the parent
1253d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * element and can also be used to find sibling UI elements to the one currently represented
1263d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * by this UiObject.
1273d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1283d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param selector for the UI element to match
1293d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return a new UiObject representing the matched UI element
130e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
131dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
132e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
1334ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException {
134f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(selector);
1354ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiObject(getSelector().fromParent(selector));
136e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
137e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
138e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1393d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Counts the child UI elements immediately under the UI element currently represented by
140e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * this UiObject.
1413d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
142e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return the count of child UI elements.
143e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
144dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
145e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
146e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public int getChildCount() throws UiObjectNotFoundException {
147f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
148e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
149e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
150e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
151e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
152e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.getChildCount();
153e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
154e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
155e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1563d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Uses the member UiSelector properties to find a matching UI element reported in
1573d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * the accessibility hierarchy.
1583d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
159e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param timeout in milliseconds
160e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return AccessibilityNodeInfo if found else null
161dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
162e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
163e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout) {
164e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = null;
165e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(UiDevice.getInstance().isInWatcherContext()) {
166e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // we will NOT run watchers or do any sort of polling if the
167e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // reason we're here is because of a watcher is executing. Watchers
168e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // will not have other watchers run for them so they should not block
169e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // while they poll for items to become present. We disable polling for them.
170e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            node = getQueryController().findAccessibilityNodeInfo(getSelector());
171e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } else {
172e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            long startMills = SystemClock.uptimeMillis();
173e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            long currentMills = 0;
174e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            while (currentMills <= timeout) {
175e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                node = getQueryController().findAccessibilityNodeInfo(getSelector());
176e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if (node != null) {
177e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    break;
178e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                } else {
179e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    UiDevice.getInstance().runWatchers();
180e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
181e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                currentMills = SystemClock.uptimeMillis() - startMills;
182e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if(timeout > 0) {
183e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
184e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
185e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
186e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
187e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node;
188e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
189e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
190e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1913d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Perform the action on the UI element that is represented by this UiObject. Also see
1921893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollToBeginning(int)}, {@link UiScrollable#scrollToEnd(int)},
1931893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollBackward()}, {@link UiScrollable#scrollForward()}.
1943d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
195467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
196467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
1973d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true of successful
198e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
199dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
200e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
201e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeUp(int steps) throws UiObjectNotFoundException {
202f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(steps);
2033d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
204e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
205e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
206e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.centerX(),
207e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.bottom - SWIPE_MARGIN_LIMIT, rect.centerX(), rect.top + SWIPE_MARGIN_LIMIT,
208e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                steps);
209e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
210e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
211e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
212e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object, Also see
2131893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollToBeginning(int)}, {@link UiScrollable#scrollToEnd(int)},
2141893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollBackward()}, {@link UiScrollable#scrollForward()}. This method will
2151893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * perform the swipe gesture over any surface.  The targeted UI element does not need to have
2161893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * the attribute <code>scrollable</code> set to <code>true</code> for this operation to be
2171893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * performed.
2183d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2193d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
220467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
2213d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if successful
222e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
223dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
224e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
225e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeDown(int steps) throws UiObjectNotFoundException {
226f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(steps);
2273d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
228e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
229e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
230e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.centerX(),
231e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.top + SWIPE_MARGIN_LIMIT, rect.centerX(),
232e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.bottom - SWIPE_MARGIN_LIMIT, steps);
233e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
234e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
235e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
236e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object. Also see
2371893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollToBeginning(int)}, {@link UiScrollable#scrollToEnd(int)},
2381893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollBackward()}, {@link UiScrollable#scrollForward()}. This method will
2391893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * perform the swipe gesture over any surface. The targeted UI element does not need to have the
2401893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * attribute <code>scrollable</code> set to <code>true</code> for this operation to be
2411893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * performed.
2423d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2433d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
244467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
2453d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if successful
246e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
247dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
248e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
249e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeLeft(int steps) throws UiObjectNotFoundException {
250f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(steps);
2513d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
252e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
253e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
254e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.right - SWIPE_MARGIN_LIMIT,
255e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerY(), rect.left + SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
256e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
257e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
258e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
259e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object. Also see
2601893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollToBeginning(int)}, {@link UiScrollable#scrollToEnd(int)},
2611893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollBackward()}, {@link UiScrollable#scrollForward()}. This method will
2621893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * perform the swipe gesture over any surface. The targeted UI element does not need to have the
2631893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * attribute <code>scrollable</code> set to <code>true</code> for this operation to be
2641893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * performed.
2653d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2663d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
267467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
2683d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if successful
269e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
270dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
271e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
272e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeRight(int steps) throws UiObjectNotFoundException {
273f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(steps);
2743d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
275e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
276e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
277e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.left + SWIPE_MARGIN_LIMIT,
278e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerY(), rect.right - SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
279e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
280e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
281e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2823d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Finds the visible bounds of a partially visible UI element
2833d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
284e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param node
2857f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * @return null if node is null, else a Rect containing visible bounds
286e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
287e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private Rect getVisibleBounds(AccessibilityNodeInfo node) {
288e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (node == null) {
289e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return null;
290e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
291e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
292e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // targeted node's bounds
2937f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz        Rect nodeRect = AccessibilityNodeInfoHelper.getVisibleBoundsInScreen(node);
294e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
295e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // is the targeted node within a scrollable container?
296e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo scrollableParentNode = getScrollableParent(node);
297e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(scrollableParentNode == null) {
298e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // nothing to adjust for so return the node's Rect as is
299e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return nodeRect;
300e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
301e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
302e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // Scrollable parent's visible bounds
3037f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz        Rect parentRect = AccessibilityNodeInfoHelper
3047f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz                .getVisibleBoundsInScreen(scrollableParentNode);
305e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // adjust for partial clipping of targeted by parent node if required
306e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        nodeRect.intersect(parentRect);
307e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return nodeRect;
308e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
309e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
310e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3117f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * Walk the hierarchy up to find a scrollable parent. A scrollable parent
3127f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * indicates that this node may be in a content where it is partially
3137f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * visible due to scrolling. its clickable center maybe invisible and
3147f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * adjustments should be made to the click coordinates.
3153d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
316e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param node
3171893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * @return The accessibility node info.
318e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
319e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private AccessibilityNodeInfo getScrollableParent(AccessibilityNodeInfo node) {
320e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo parent = node;
321e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        while(parent != null) {
322e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            parent = parent.getParent();
323e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (parent != null && parent.isScrollable()) {
324e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return parent;
325e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
326e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
327e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return null;
328e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
329e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
330e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3313d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Performs a click at the center of the visible bounds of the UI element represented
33246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * by this UiObject.
3333d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
334e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true id successful else false
335e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
336dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
337e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
338e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean click() throws UiObjectNotFoundException {
339f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
340e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
341e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
342e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
343e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
344e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
3451dc7d12406947faaee8454c6efb2a0631f5da573Adam Momtaz        return getInteractionController().clickAndSync(rect.centerX(), rect.centerY());
346e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
347e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
348e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3493d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link #clickAndWaitForNewWindow(long)}
350a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * This method is intended to reliably wait for window transitions that would typically take
351a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * longer than the usual default timeouts.
3523d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
3533d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if the event was triggered, else false
3543d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @throws UiObjectNotFoundException
355dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
3563d50587be8ff021369c90554d814839335b445b0Adam Momtaz     */
357e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickAndWaitForNewWindow() throws UiObjectNotFoundException {
358f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
359e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return clickAndWaitForNewWindow(WAIT_FOR_WINDOW_TMEOUT);
360e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
361e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
362e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
36346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Performs a click at the center of the visible bounds of the UI element represented
36446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * by this UiObject and waits for window transitions.
36546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
3663d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * This method differ from {@link UiObject#click()} only in that this method waits for a
36746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * a new window transition as a result of the click. Some examples of a window transition:
368e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>launching a new activity</li>
369e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>bringing up a pop-up menu</li>
370e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>bringing up a dialog</li>
371e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
3723d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param timeout timeout before giving up on waiting for a new window
373e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the event was triggered, else false
374e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
375dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
376e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
377e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickAndWaitForNewWindow(long timeout) throws UiObjectNotFoundException {
378f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(timeout);
379e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
380e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
381e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
382e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
383e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
3841dc7d12406947faaee8454c6efb2a0631f5da573Adam Momtaz        return getInteractionController().clickAndWaitForNewWindow(rect.centerX(), rect.centerY());
385e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
386e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
387e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3883d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Clicks the top and left corner of the UI element
3893d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
390e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on success
3911893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * @throws UiObjectNotFoundException
392dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
393e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
394e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickTopLeft() throws UiObjectNotFoundException {
395f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
396e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
397e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
398e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
399e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
400e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
4011dc7d12406947faaee8454c6efb2a0631f5da573Adam Momtaz        return getInteractionController().clickNoSync(rect.left + 5, rect.top + 5);
402e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
403e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
404e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4053d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks bottom and right corner of the UI element
4063d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
407e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
408e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
409dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
410e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
411e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClickBottomRight() throws UiObjectNotFoundException  {
412f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
413e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
414e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
415e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
416e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
417e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
4181dc7d12406947faaee8454c6efb2a0631f5da573Adam Momtaz        return getInteractionController().longTapNoSync(rect.right - 5, rect.bottom - 5);
419e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
420e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
421e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4223d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Clicks the bottom and right corner of the UI element
4233d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
424e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on success
4251893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * @throws UiObjectNotFoundException
426dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
427e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
428e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickBottomRight() throws UiObjectNotFoundException {
429f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
430e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
431e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
432e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
433e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
434e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
4351dc7d12406947faaee8454c6efb2a0631f5da573Adam Momtaz        return getInteractionController().clickNoSync(rect.right - 5, rect.bottom - 5);
436e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
437e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
438e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4393d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks the center of the visible bounds of the UI element
4403d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
441e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
442e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
443dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
444e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
445e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClick() throws UiObjectNotFoundException  {
446f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
447e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
448e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
449e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
450e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
451e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
4521dc7d12406947faaee8454c6efb2a0631f5da573Adam Momtaz        return getInteractionController().longTapNoSync(rect.centerX(), rect.centerY());
453e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
454e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
455e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4563d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks on the top and left corner of the UI element
4573d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
458e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
459e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
460dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
461e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
462e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClickTopLeft() throws UiObjectNotFoundException {
463f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
464e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
465e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
466e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
467e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
468e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
4691dc7d12406947faaee8454c6efb2a0631f5da573Adam Momtaz        return getInteractionController().longTapNoSync(rect.left + 5, rect.top + 5);
470e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
471e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
472e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4733d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the <code>text</code> property of the UI element
4743d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
475e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return text value of the current node represented by this UiObject
476e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException if no match could be found
477dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
478e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
479e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getText() throws UiObjectNotFoundException {
480f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
481e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
482e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
483e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
484e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
485e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        String retVal = safeStringReturn(node.getText());
486e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Log.d(LOG_TAG, String.format("getText() = %s", retVal));
487e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return retVal;
488e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
489e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
490e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
49197835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz     * Reads the <code>className</code> property of the UI element
49297835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz     *
49397835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz     * @return class name of the current node represented by this UiObject
49497835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz     * @throws UiObjectNotFoundException if no match could be found
49597835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz     * @since API Level 18
49697835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz     */
49797835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz    public String getClassName() throws UiObjectNotFoundException {
49897835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz        Tracer.trace();
49997835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
50097835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz        if(node == null) {
50197835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz            throw new UiObjectNotFoundException(getSelector().toString());
50297835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz        }
50397835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz        String retVal = safeStringReturn(node.getClassName());
50497835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz        Log.d(LOG_TAG, String.format("getClassName() = %s", retVal));
50597835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz        return retVal;
50697835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz    }
50797835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz
50897835f3a7c80b147136c44c175eb9e6a4261fd92Adam Momtaz    /**
5093d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the <code>content_desc</code> property of the UI element
5103d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
511e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return value of node attribute "content_desc"
512e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
513dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
514e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
515e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getContentDescription() throws UiObjectNotFoundException {
516f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
517e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
518e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
519e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
520e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
521e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return safeStringReturn(node.getContentDescription());
522e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
523e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
524e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
52546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Sets the text in an editable field, after clearing the field's content.
52646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
52746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * The {@link UiSelector} selector of this object must reference a UI element that is editable.
52846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
52946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * When you call this method, the method first simulates a {@link #click()} on
53046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * editable field to set focus. The method then clears the field's contents
53146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * and injects your specified text into the field.
5323d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
53346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If you want to capture the original contents of the field, call {@link #getText()} first.
53446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * You can then modify the text and use this method to update the field.
53546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
53646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param text string to set
537e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation is successful
538e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
539dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
540e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
541e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean setText(String text) throws UiObjectNotFoundException {
542f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(text);
543e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        clearTextField();
544e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().sendText(text);
545e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
546e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
547e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
54846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Clears the existing text contents in an editable field.
54946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
55046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * The {@link UiSelector} of this object must reference a UI element that is editable.
55146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
55246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * When you call this method, the method first sets focus at the start edge of the field.
55346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * The method then simulates a long-press to select the existing text, and deletes the
55446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * selected text.
55546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
55646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If a "Select-All" option is displayed, the method will automatically attempt to use it
55746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * to ensure full text selection.
55846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
55946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Note that it is possible that not all the text in the field is selected; for example,
56046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * if the text contains separators such as spaces, slashes, at symbol etc.
56146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Also, not all editable fields support the long-press functionality.
56246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
563e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
564dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
565e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
566e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void clearTextField() throws UiObjectNotFoundException {
567f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
568e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // long click left + center
569e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
570e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
571e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
572e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
573e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
5741dc7d12406947faaee8454c6efb2a0631f5da573Adam Momtaz        getInteractionController().longTapNoSync(rect.left + 20, rect.centerY());
575e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // check if the edit menu is open
5764ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        UiObject selectAll = new UiObject(new UiSelector().descriptionContains("Select all"));
577e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(selectAll.waitForExists(50))
578e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            selectAll.click();
579e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // wait for the selection
580e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        SystemClock.sleep(250);
581e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // delete it
582e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0);
583e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
584e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
585e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5863d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>checked</code> property is currently true
5873d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
588e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
589dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
590e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
591e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isChecked() throws UiObjectNotFoundException {
592f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
593e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
594e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
595e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
596e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
597e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isChecked();
598e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
599e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
600e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6013d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>selected</code> property is currently true
6023d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
603e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
604e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
605dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
606e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
607e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isSelected() throws UiObjectNotFoundException {
608f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
609e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
610e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
611e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
612e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
613e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isSelected();
614e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
615e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
616e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6173d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>checkable</code> property is currently true
6183d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
619e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
620e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
621dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
622e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
623e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isCheckable() throws UiObjectNotFoundException {
624f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
625e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
626e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
627e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
628e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
629e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isCheckable();
630e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
631e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
632e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6333d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>enabled</code> property is currently true
6343d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
635e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
636e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
637dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
638e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
639e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isEnabled() throws UiObjectNotFoundException {
640f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
641e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
642e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
643e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
644e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
645e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isEnabled();
646e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
647e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
648e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6493d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>clickable</code> property is currently true
6503d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
651e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
652e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
653dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
654e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
655e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isClickable() throws UiObjectNotFoundException {
656f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
657e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
658e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
659e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
660e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
661e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isClickable();
662e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
663e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
664e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6653d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>focused</code> property is currently true
6663d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
667e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
668e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
669dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
670e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
671e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isFocused() throws UiObjectNotFoundException {
672f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
673e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
674e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
675e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
676e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
677e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isFocused();
678e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
679e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
680e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6813d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>focusable</code> property is currently true
6823d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
683e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
684e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
685dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
686e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
687e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isFocusable() throws UiObjectNotFoundException {
688f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
689e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
690e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
691e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
692e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
693e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isFocusable();
694e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
695e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
696e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6973d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>scrollable</code> property is currently true
6983d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
699e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
700e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
701dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
702e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
703e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isScrollable() throws UiObjectNotFoundException {
704f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
705e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
706e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
707e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
708e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
709e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isScrollable();
710e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
711e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
712e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
7133d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>long-clickable</code> property is currently true
7143d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
715e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
716e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
717dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
718e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
719e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isLongClickable() throws UiObjectNotFoundException {
720f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
721e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
722e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
723e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
724e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
725e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isLongClickable();
726e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
727e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
728e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
7293d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the UI element's <code>package</code> property
7303d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
7313d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if it is else false
732e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
733dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
734e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
735e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getPackageName() throws UiObjectNotFoundException {
736f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
737e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
738e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
739e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
740e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
741e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return safeStringReturn(node.getPackageName());
742e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
743e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
744e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
74546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Returns the visible bounds of the UI element.
74646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
74746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If a portion of the UI element is visible, only the bounds of the visible portion are
74846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * reported.
7493d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
750e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return Rect
751e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
7521893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * @see {@link #getBounds()}
75379693ede92636fe6f3a6ec4dc049a438fd9504ffGuang Zhu     * @since API Level 17
754e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
7553d50587be8ff021369c90554d814839335b445b0Adam Momtaz    public Rect getVisibleBounds() throws UiObjectNotFoundException {
756f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
757e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
758e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
759e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
760e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
761e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getVisibleBounds(node);
762e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
763e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
764e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
76546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Returns the UI element's <code>bounds</code> property. See {@link #getVisibleBounds()}
7663d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
7673d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return Rect
7683d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @throws UiObjectNotFoundException
769dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
7703d50587be8ff021369c90554d814839335b445b0Adam Momtaz     */
7713d50587be8ff021369c90554d814839335b445b0Adam Momtaz    public Rect getBounds() throws UiObjectNotFoundException {
772f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
7733d50587be8ff021369c90554d814839335b445b0Adam Momtaz        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
7743d50587be8ff021369c90554d814839335b445b0Adam Momtaz        if(node == null) {
7753d50587be8ff021369c90554d814839335b445b0Adam Momtaz            throw new UiObjectNotFoundException(getSelector().toString());
7763d50587be8ff021369c90554d814839335b445b0Adam Momtaz        }
7773d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect nodeRect = new Rect();
7783d50587be8ff021369c90554d814839335b445b0Adam Momtaz        node.getBoundsInScreen(nodeRect);
7793d50587be8ff021369c90554d814839335b445b0Adam Momtaz
7803d50587be8ff021369c90554d814839335b445b0Adam Momtaz        return nodeRect;
7813d50587be8ff021369c90554d814839335b445b0Adam Momtaz    }
7823d50587be8ff021369c90554d814839335b445b0Adam Momtaz
7833d50587be8ff021369c90554d814839335b445b0Adam Momtaz    /**
78446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Waits a specified length of time for a UI element to become visible.
78546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
78646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * This method waits until the UI element becomes visible on the display, or
78746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * until the timeout has elapsed. You can use this method in situations where
78846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * the content that you want to select is not immediately displayed.
7893d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
79046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param timeout the amount of time to wait (in milliseconds)
79146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if the UI element is displayed, else false if timeout elapsed while waiting
792dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
793e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
794e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean waitForExists(long timeout) {
795f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(timeout);
796e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(findAccessibilityNodeInfo(timeout) != null) {
797e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return true;
798e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
799e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
800e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
801e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
802e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
80346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Waits a specified length of time for a UI element to become undetectable.
8043d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
80546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * This method waits until a UI element is no longer matchable, or until the
80646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * timeout has elapsed.
80746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
80846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * A UI element becomes undetectable when the {@link UiSelector} of the object is
80946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * unable to find a match because the element has either changed its state or is no
81046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * longer displayed.
81146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
81246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * You can use this method when attempting to wait for some long operation
81346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * to compete, such as downloading a large file or connecting to a remote server.
81446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
81546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param timeout time to wait (in milliseconds)
81646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if the element is gone before timeout elapsed, else false if timeout elapsed
81746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * but a matching element is still found.
818dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
819e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
820e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean waitUntilGone(long timeout) {
821f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(timeout);
822e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        long startMills = SystemClock.uptimeMillis();
823e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        long currentMills = 0;
824e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        while (currentMills <= timeout) {
825e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(findAccessibilityNodeInfo(0) == null)
826e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return true;
827e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            currentMills = SystemClock.uptimeMillis() - startMills;
828e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(timeout > 0)
829e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
830e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
831e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
832e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
833e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
834e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
83546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Check if UI element exists.
83646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
837e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This methods performs a {@link #waitForExists(long)} with zero timeout. This
838e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * basically returns immediately whether the UI element represented by this UiObject
839e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * exists or not. If you need to wait longer for this UI element, then see
840e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #waitForExists(long)}.
8413d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
842e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the UI element represented by this UiObject does exist
843dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
844e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
845e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean exists() {
846f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
847e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return waitForExists(0);
848e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
849e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
850e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private String safeStringReturn(CharSequence cs) {
851e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(cs == null)
852e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return "";
853e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return cs.toString();
854e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
855c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
856c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    /**
857c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * PinchOut generates a 2 pointer gesture where each pointer is moving from the center out
858c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * away from each other diagonally towards the edges of the current UI element represented by
859c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * this UiObject.
860c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param percent of the object's diagonal length to use for the pinch
861c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
862c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
863c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @throws UiObjectNotFoundException
864c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @since API Level 18
865c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     */
866c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    public void pinchOut(int percent, int steps) throws UiObjectNotFoundException {
867c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // make value between 1 and 100
868c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        percent = (percent < 0) ? 1 : (percent > 100) ? 100 : percent;
869c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        float percentage = percent / 100f;
870c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
871c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
872c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        if (node == null) {
873c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            throw new UiObjectNotFoundException(getSelector().toString());
874c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        }
875c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
876c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Rect rect = getVisibleBounds(node);
877c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        if (rect.width() <= FINGER_TOUCH_HALF_WIDTH * 2)
878c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            throw new IllegalStateException("Object width is too small for operation");
879c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
880c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // start from the same point at the center of the control
881c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point startPoint1 = new Point(rect.centerX() - FINGER_TOUCH_HALF_WIDTH, rect.centerY());
882c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point startPoint2 = new Point(rect.centerX() + FINGER_TOUCH_HALF_WIDTH, rect.centerY());
883c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
884c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // End at the top-left and bottom-right corners of the control
885c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point endPoint1 = new Point(rect.centerX() - (int)((rect.width()/2) * percentage),
886c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz                rect.centerY());
887c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point endPoint2 = new Point(rect.centerX() + (int)((rect.width()/2) * percentage),
888c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz                rect.centerY());
889c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
890c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        twoPointerGesture(startPoint1, startPoint2, endPoint1, endPoint2, steps);
891c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    }
892c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
893c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    /**
894c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * PinchIn generates a 2 pointer gesture where each pointer is moving towards the other
895c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * diagonally from the edges of the current UI element represented by this UiObject, until the
896c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * center.
897c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param percent of the object's diagonal length to use for the pinch
898c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
899c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
900c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @throws UiObjectNotFoundException
901c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @since API Level 18
902c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     */
903c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    public void pinchIn(int percent, int steps) throws UiObjectNotFoundException {
904c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // make value between 1 and 100
905c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        percent = (percent < 0) ? 0 : (percent > 100) ? 100 : percent;
906c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        float percentage = percent / 100f;
907c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
908c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
909c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        if (node == null) {
910c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            throw new UiObjectNotFoundException(getSelector().toString());
911c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        }
912c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
913c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Rect rect = getVisibleBounds(node);
914c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        if (rect.width() <= FINGER_TOUCH_HALF_WIDTH * 2)
915c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            throw new IllegalStateException("Object width is too small for operation");
916c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
917c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point startPoint1 = new Point(rect.centerX() - (int)((rect.width()/2) * percentage),
918c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz                rect.centerY());
919c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point startPoint2 = new Point(rect.centerX() + (int)((rect.width()/2) * percentage),
920c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz                rect.centerY());
921c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
922c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point endPoint1 = new Point(rect.centerX() - FINGER_TOUCH_HALF_WIDTH, rect.centerY());
923c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point endPoint2 = new Point(rect.centerX() + FINGER_TOUCH_HALF_WIDTH, rect.centerY());
924c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
925c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        twoPointerGesture(startPoint1, startPoint2, endPoint1, endPoint2, steps);
926c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    }
927c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
928c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    /**
929c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * Generates a 2 pointer gesture from an arbitrary starting and ending points.
930c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *
931c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param startPoint1 start point of pointer 1
932c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param startPoint2 start point of pointer 2
933c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param endPoint1 end point of pointer 1
934c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param endPoint2 end point of pointer 2
935c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
936c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
937c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @since API Level 18
938c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     */
939c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    public void twoPointerGesture(Point startPoint1, Point startPoint2, Point endPoint1,
940c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            Point endPoint2, int steps) {
941c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
942c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // avoid a divide by zero
943c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        if(steps == 0)
944c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            steps = 1;
945c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
946c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        final float stepX1 = (endPoint1.x - startPoint1.x) / steps;
947c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        final float stepY1 = (endPoint1.y - startPoint1.y) / steps;
948c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        final float stepX2 = (endPoint2.x - startPoint2.x) / steps;
949c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        final float stepY2 = (endPoint2.y - startPoint2.y) / steps;
950c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
951c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        int eventX1, eventY1, eventX2, eventY2;
952c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        eventX1 = startPoint1.x;
953c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        eventY1 = startPoint1.y;
954c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        eventX2 = startPoint2.x;
955c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        eventY2 = startPoint2.y;
956c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
957c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // allocate for steps plus first down and last up
958c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        PointerCoords[] points1 = new PointerCoords[steps + 2];
959c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        PointerCoords[] points2 = new PointerCoords[steps + 2];
960c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
961c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // Include the first and last touch downs in the arrays of steps
962c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        for (int i = 0; i < steps + 1; i++) {
963c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            PointerCoords p1 = new PointerCoords();
964c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p1.x = eventX1;
965c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p1.y = eventY1;
966c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p1.pressure = 1;
967c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p1.size = 1;
968c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            points1[i] = p1;
969c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
970c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            PointerCoords p2 = new PointerCoords();
971c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p2.x = eventX2;
972c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p2.y = eventY2;
973c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p2.pressure = 1;
974c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p2.size = 1;
975c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            points2[i] = p2;
976c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
977c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            eventX1 += stepX1;
978c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            eventY1 += stepY1;
979c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            eventX2 += stepX2;
980c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            eventY2 += stepY2;
981c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        }
982c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
983c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // ending pointers coordinates
984c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        PointerCoords p1 = new PointerCoords();
985c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p1.x = endPoint1.x;
986c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p1.y = endPoint1.y;
987c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p1.pressure = 1;
988c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p1.size = 1;
989c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        points1[steps + 1] = p1;
990c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
991c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        PointerCoords p2 = new PointerCoords();
992c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p2.x = endPoint2.x;
993c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p2.y = endPoint2.y;
994c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p2.pressure = 1;
995c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p2.size = 1;
996c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        points2[steps + 1] = p2;
997c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
998c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        multiPointerGesture(points1, points2);
999c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    }
1000c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
1001c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    /**
1002c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * Performs a multi-touch gesture
1003c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *
1004c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * Takes a series of touch coordinates for at least 2 pointers. Each pointer must have
1005c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * all of its touch steps defined in an array of {@link PointerCoords}. By having the ability
1006c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * to specify the touch points along the path of a pointer, the caller is able to specify
1007c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * complex gestures like circles, irregular shapes etc, where each pointer may take a
1008c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * different path.
1009c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *
1010c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * To create a single point on a pointer's touch path
1011c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * <code>
1012c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *       PointerCoords p = new PointerCoords();
1013c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *       p.x = stepX;
1014c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *       p.y = stepY;
1015c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *       p.pressure = 1;
1016c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *       p.size = 1;
1017c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * </code>
1018c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param touches each array of {@link PointerCoords} constitute a single pointer's touch path.
1019c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *        Multiple {@link PointerCoords} arrays constitute multiple pointers, each with its own
1020c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *        path. Each {@link PointerCoords} in an array constitute a point on a pointer's path.
1021c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @since API Level 18
1022c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     */
1023c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    public void multiPointerGesture(PointerCoords[] ...touches) {
1024c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        getInteractionController().generateMultiPointerGesture(touches);
1025c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    }
1026c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz}