UiObject.java revision 79693ede92636fe6f3a6ec4dc049a438fd9504ff
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
19e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.graphics.Rect;
20e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.SystemClock;
21e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.util.Log;
22e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.KeyEvent;
236088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtazimport android.view.accessibility.AccessibilityEvent;
24e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.accessibility.AccessibilityNodeInfo;
25e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
26e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu/**
2746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * A UiObject is a representation of a UI element. It is not in any way directly bound to a
2846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * UI element as an object reference. A UiObject holds information to help it
2946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * locate a matching UI element at runtime based on the {@link UiSelector} properties specified in
3046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * its constructor. Since a UiObject is a representative for a UI element, it can
3146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * be reused for different views with matching UI elements.
32dbba713661688a285e701a006ce2d199296ac328Guang Zhu * @since API Level 16
33e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
34e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupublic class UiObject {
35e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final String LOG_TAG = UiObject.class.getSimpleName();
36dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
37dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
38dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
39e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_SELECTOR_TIMEOUT = 10 * 1000;
40dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
41dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
42dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
43e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_SELECTOR_POLL = 1000;
44e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // set a default timeout to 5.5s, since ANR threshold is 5s
45dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
46dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
47dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
48e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_WINDOW_TMEOUT = 5500;
49dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
50dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 17
51dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
526088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz    protected static final long WAIT_FOR_EVENT_TMEOUT = 3 * 1000;
53dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
54dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
55dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
56e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final int SWIPE_MARGIN_LIMIT = 5;
57e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
587f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz    private final UiSelector mSelector;
59ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    private final UiAutomatorBridge mUiAutomationBridge;
60e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
61e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
623d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Constructs a UiObject to represent a specific UI element matched by the specified
633d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * {@link UiSelector} selector properties.
64e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param selector
65dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
66e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
674ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject(UiSelector selector) {
68e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mUiAutomationBridge = UiDevice.getInstance().getAutomatorBridge();
69e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mSelector = selector;
70e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
71e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
72e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
733d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Debugging helper. A test can dump the properties of a selector as a string
743d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * to its logs if needed. <code>getSelector().toString();</code>
753d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
764ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @return {@link UiSelector}
77dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
78e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
794ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public final UiSelector getSelector() {
804ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiSelector(mSelector);
81e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
82e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
83e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
843d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Retrieves the {@link QueryController} to translate a {@link UiSelector} selector
853d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * into an {@link AccessibilityNodeInfo}.
863d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
87e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link QueryController}
88e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
89ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    QueryController getQueryController() {
90e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getQueryController();
91e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
92e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
93e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
943d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Retrieves the {@link InteractionController} to perform finger actions such as tapping,
953d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * swiping or entering text.
963d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
97e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link InteractionController}
98e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
99ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    InteractionController getInteractionController() {
100e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getInteractionController();
101e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
102e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
103e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1043d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Creates a new UiObject representing a child UI element of the element currently represented
1053d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * by this UiObject.
1063d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1073d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param selector for UI element to match
1083d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return a new UiObject representing the matched UI element
109dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
110e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
1114ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException {
1124ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiObject(getSelector().childSelector(selector));
113e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
114e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
115e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1163d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Creates a new UiObject representing a child UI element from the parent element currently
1173d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * represented by this object. Essentially this is starting the search from the parent
1183d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * element and can also be used to find sibling UI elements to the one currently represented
1193d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * by this UiObject.
1203d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1213d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param selector for the UI element to match
1223d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return a new UiObject representing the matched UI element
123e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
124dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
125e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
1264ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException {
1274ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiObject(getSelector().fromParent(selector));
128e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
129e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
130e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1313d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Counts the child UI elements immediately under the UI element currently represented by
132e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * this UiObject.
1333d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
134e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return the count of child UI elements.
135e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
136dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
137e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
138e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public int getChildCount() throws UiObjectNotFoundException {
139e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
140e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
141e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
142e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
143e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.getChildCount();
144e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
145e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
146e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1473d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Uses the member UiSelector properties to find a matching UI element reported in
1483d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * the accessibility hierarchy.
1493d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1504ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param selector {@link UiSelector}
151e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param timeout in milliseconds
152e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return AccessibilityNodeInfo if found else null
153dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
154e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
155e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout) {
156e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = null;
157e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(UiDevice.getInstance().isInWatcherContext()) {
158e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // we will NOT run watchers or do any sort of polling if the
159e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // reason we're here is because of a watcher is executing. Watchers
160e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // will not have other watchers run for them so they should not block
161e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // while they poll for items to become present. We disable polling for them.
162e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            node = getQueryController().findAccessibilityNodeInfo(getSelector());
163e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } else {
164e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            long startMills = SystemClock.uptimeMillis();
165e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            long currentMills = 0;
166e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            while (currentMills <= timeout) {
167e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                node = getQueryController().findAccessibilityNodeInfo(getSelector());
168e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if (node != null) {
169e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    break;
170e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                } else {
171e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    UiDevice.getInstance().runWatchers();
172e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
173e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                currentMills = SystemClock.uptimeMillis() - startMills;
174e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if(timeout > 0) {
175e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
176e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
177e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
178e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
179e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node;
180e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
181e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
182e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1833d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Perform the action on the UI element that is represented by this UiObject. Also see
184e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
1853d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * {@link #scrollForward()}.
1863d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
187467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
188467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
1893d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true of successful
190e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
191dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
192e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
193e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeUp(int steps) throws UiObjectNotFoundException {
1943d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
195e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
196e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
197e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.centerX(),
198e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.bottom - SWIPE_MARGIN_LIMIT, rect.centerX(), rect.top + SWIPE_MARGIN_LIMIT,
199e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                steps);
200e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
201e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
202e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
203e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object, Also see
204e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
205e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollForward()}. This method will perform the swipe gesture over any
206e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * surface. The targeted UI element does not need to have the attribute
207e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
2083d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2093d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
210467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
2113d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if successful
212e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
213dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
214e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
215e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeDown(int steps) throws UiObjectNotFoundException {
2163d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
217e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
218e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
219e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.centerX(),
220e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.top + SWIPE_MARGIN_LIMIT, rect.centerX(),
221e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.bottom - SWIPE_MARGIN_LIMIT, steps);
222e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
223e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
224e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
225e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object. Also see
226e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
227e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollForward()}. This method will perform the swipe gesture over any
228e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * surface. The targeted UI element does not need to have the attribute
229e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
2303d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2313d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
232467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
2333d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if successful
234e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
235dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
236e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
237e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeLeft(int steps) throws UiObjectNotFoundException {
2383d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
239e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
240e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
241e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.right - SWIPE_MARGIN_LIMIT,
242e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerY(), rect.left + SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
243e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
244e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
245e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
246e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object. Also see
247e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
248e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollForward()}. This method will perform the swipe gesture over any
249e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * surface. The targeted UI element does not need to have the attribute
250e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
2513d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2523d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
253467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
2543d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if successful
255e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
256dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
257e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
258e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeRight(int steps) throws UiObjectNotFoundException {
2593d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
260e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
261e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
262e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.left + SWIPE_MARGIN_LIMIT,
263e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerY(), rect.right - SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
264e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
265e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
266e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2673d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Finds the visible bounds of a partially visible UI element
2683d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
269e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param node
2707f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * @return null if node is null, else a Rect containing visible bounds
271e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
272e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private Rect getVisibleBounds(AccessibilityNodeInfo node) {
273e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (node == null) {
274e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return null;
275e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
276e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
277e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // targeted node's bounds
2787f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz        Rect nodeRect = AccessibilityNodeInfoHelper.getVisibleBoundsInScreen(node);
279e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
280e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // is the targeted node within a scrollable container?
281e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo scrollableParentNode = getScrollableParent(node);
282e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(scrollableParentNode == null) {
283e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // nothing to adjust for so return the node's Rect as is
284e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return nodeRect;
285e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
286e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
287e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // Scrollable parent's visible bounds
2887f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz        Rect parentRect = AccessibilityNodeInfoHelper
2897f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz                .getVisibleBoundsInScreen(scrollableParentNode);
290e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // adjust for partial clipping of targeted by parent node if required
291e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        nodeRect.intersect(parentRect);
292e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return nodeRect;
293e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
294e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
295e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2967f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * Walk the hierarchy up to find a scrollable parent. A scrollable parent
2977f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * indicates that this node may be in a content where it is partially
2987f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * visible due to scrolling. its clickable center maybe invisible and
2997f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * adjustments should be made to the click coordinates.
3003d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
301e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param node
302e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return
303e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
304e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private AccessibilityNodeInfo getScrollableParent(AccessibilityNodeInfo node) {
305e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo parent = node;
306e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        while(parent != null) {
307e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            parent = parent.getParent();
308e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (parent != null && parent.isScrollable()) {
309e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return parent;
310e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
311e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
312e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return null;
313e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
314e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
315e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3163d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Performs a click at the center of the visible bounds of the UI element represented
31746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * by this UiObject.
3183d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
319e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true id successful else false
320e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
321dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
322e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
323e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean click() throws UiObjectNotFoundException {
324e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
325e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
326e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
327e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
328e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
32908f500a92397522c48bcdad58d37ec1be95956d1Adam Momtaz        return getInteractionController().clickAndWaitForEvents(rect.centerX(), rect.centerY(),
33008f500a92397522c48bcdad58d37ec1be95956d1Adam Momtaz                WAIT_FOR_EVENT_TMEOUT, false, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED +
33108f500a92397522c48bcdad58d37ec1be95956d1Adam Momtaz                AccessibilityEvent.TYPE_VIEW_SELECTED);
332e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
333e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
334e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3353d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link #clickAndWaitForNewWindow(long)}
336a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * This method is intended to reliably wait for window transitions that would typically take
337a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * longer than the usual default timeouts.
3383d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
3393d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if the event was triggered, else false
3403d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @throws UiObjectNotFoundException
341dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
3423d50587be8ff021369c90554d814839335b445b0Adam Momtaz     */
343e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickAndWaitForNewWindow() throws UiObjectNotFoundException {
344e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return clickAndWaitForNewWindow(WAIT_FOR_WINDOW_TMEOUT);
345e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
346e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
347e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
34846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Performs a click at the center of the visible bounds of the UI element represented
34946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * by this UiObject and waits for window transitions.
35046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
3513d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * This method differ from {@link UiObject#click()} only in that this method waits for a
35246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * a new window transition as a result of the click. Some examples of a window transition:
353e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>launching a new activity</li>
354e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>bringing up a pop-up menu</li>
355e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>bringing up a dialog</li>
356e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
3573d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param timeout timeout before giving up on waiting for a new window
358e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the event was triggered, else false
359e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
360dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
361e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
362e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickAndWaitForNewWindow(long timeout) throws UiObjectNotFoundException {
363e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
364e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
365e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
366e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
367e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
3684ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getInteractionController().clickAndWaitForNewWindow(
369e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerX(), rect.centerY(), timeout);
370e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
371e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
372e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3733d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Clicks the top and left corner of the UI element
3743d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
375e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on success
376e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws Exception
377dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
378e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
379e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickTopLeft() throws UiObjectNotFoundException {
380e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
381e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
382e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
383e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
384e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
3854ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getInteractionController().click(rect.left + 5, rect.top + 5);
386e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
387e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
388e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3893d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks bottom and right corner of the UI element
3903d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
391e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
392e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
393dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
394e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
395e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClickBottomRight() throws UiObjectNotFoundException  {
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);
401e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().longTap(rect.right - 5, rect.bottom - 5);
402e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
403e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
404e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4053d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Clicks the bottom and right corner of the UI element
4063d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
407e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on success
408e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws Exception
409dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
410e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
411e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickBottomRight() throws UiObjectNotFoundException {
412e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
413e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
414e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
415e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
416e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
4174ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getInteractionController().click(rect.right - 5, rect.bottom - 5);
418e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
419e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
420e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4213d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks the center of the visible bounds of the UI element
4223d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
423e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
424e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
425dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
426e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
427e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClick() throws UiObjectNotFoundException  {
428e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
429e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
430e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
431e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
432e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
433e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().longTap(rect.centerX(), rect.centerY());
434e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
435e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
436e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4373d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks on the top and left corner of the UI element
4383d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
439e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
440e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
441dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
442e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
443e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClickTopLeft() throws UiObjectNotFoundException {
444e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
445e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
446e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
447e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
448e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
449e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().longTap(rect.left + 5, rect.top + 5);
450e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
451e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
452e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4533d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the <code>text</code> property of the UI element
4543d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
455e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return text value of the current node represented by this UiObject
456e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException if no match could be found
457dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
458e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
459e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getText() throws UiObjectNotFoundException {
460e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
461e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
462e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
463e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
464e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        String retVal = safeStringReturn(node.getText());
465e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Log.d(LOG_TAG, String.format("getText() = %s", retVal));
466e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return retVal;
467e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
468e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
469e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4703d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the <code>content_desc</code> property of the UI element
4713d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
472e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return value of node attribute "content_desc"
473e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
474dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
475e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
476e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getContentDescription() throws UiObjectNotFoundException {
477e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
478e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
479e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
480e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
481e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return safeStringReturn(node.getContentDescription());
482e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
483e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
484e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
48546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Sets the text in an editable field, after clearing the field's content.
48646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
48746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * The {@link UiSelector} selector of this object must reference a UI element that is editable.
48846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
48946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * When you call this method, the method first simulates a {@link #click()} on
49046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * editable field to set focus. The method then clears the field's contents
49146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * and injects your specified text into the field.
4923d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
49346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If you want to capture the original contents of the field, call {@link #getText()} first.
49446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * You can then modify the text and use this method to update the field.
49546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
49646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param text string to set
497e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation is successful
498e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
499dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
500e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
501e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean setText(String text) throws UiObjectNotFoundException {
502e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        clearTextField();
503e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().sendText(text);
504e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
505e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
506e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
50746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Clears the existing text contents in an editable field.
50846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
50946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * The {@link UiSelector} of this object must reference a UI element that is editable.
51046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
51146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * When you call this method, the method first sets focus at the start edge of the field.
51246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * The method then simulates a long-press to select the existing text, and deletes the
51346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * selected text.
51446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
51546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If a "Select-All" option is displayed, the method will automatically attempt to use it
51646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * to ensure full text selection.
51746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
51846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Note that it is possible that not all the text in the field is selected; for example,
51946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * if the text contains separators such as spaces, slashes, at symbol etc.
52046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Also, not all editable fields support the long-press functionality.
52146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
522e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
523dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
524e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
525e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void clearTextField() throws UiObjectNotFoundException {
526e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // long click left + center
527e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
528e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
529e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
530e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
531e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
532e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getInteractionController().longTap(rect.left + 20, rect.centerY());
533e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // check if the edit menu is open
5344ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        UiObject selectAll = new UiObject(new UiSelector().descriptionContains("Select all"));
535e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(selectAll.waitForExists(50))
536e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            selectAll.click();
537e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // wait for the selection
538e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        SystemClock.sleep(250);
539e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // delete it
540e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0);
541e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
542e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
543e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5443d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>checked</code> property is currently true
5453d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
546e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
547dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
548e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
549e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isChecked() throws UiObjectNotFoundException {
550e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
551e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
552e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
553e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
554e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isChecked();
555e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
556e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
557e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5583d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>selected</code> property is currently true
5593d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
560e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
561e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
562dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
563e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
564e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isSelected() throws UiObjectNotFoundException {
565e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
566e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
567e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
568e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
569e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isSelected();
570e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
571e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
572e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5733d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>checkable</code> property is currently true
5743d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
575e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
576e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
577dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
578e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
579e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isCheckable() throws UiObjectNotFoundException {
580e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
581e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
582e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
583e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
584e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isCheckable();
585e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
586e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
587e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5883d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>enabled</code> property is currently true
5893d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
590e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
591e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
592dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
593e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
594e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isEnabled() throws UiObjectNotFoundException {
595e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
596e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
597e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
598e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
599e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isEnabled();
600e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
601e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
602e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6033d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>clickable</code> property is currently true
6043d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
605e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
606e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
607dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
608e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
609e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isClickable() throws UiObjectNotFoundException {
610e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
611e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
612e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
613e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
614e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isClickable();
615e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
616e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
617e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6183d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>focused</code> property is currently true
6193d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
620e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
621e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
622dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
623e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
624e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isFocused() throws UiObjectNotFoundException {
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.isFocused();
630e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
631e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
632e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6333d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>focusable</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 isFocusable() throws UiObjectNotFoundException {
640e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
641e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
642e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
643e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
644e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isFocusable();
645e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
646e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
647e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6483d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>scrollable</code> property is currently true
6493d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
650e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
651e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
652dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
653e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
654e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isScrollable() throws UiObjectNotFoundException {
655e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
656e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
657e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
658e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
659e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isScrollable();
660e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
661e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
662e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6633d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>long-clickable</code> property is currently true
6643d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
665e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
666e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
667dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
668e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
669e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isLongClickable() throws UiObjectNotFoundException {
670e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
671e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
672e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
673e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
674e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isLongClickable();
675e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
676e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
677e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6783d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the UI element's <code>package</code> property
6793d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
6803d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if it is else false
681e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
682dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
683e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
684e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getPackageName() throws UiObjectNotFoundException {
685e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
686e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
687e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
688e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
689e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return safeStringReturn(node.getPackageName());
690e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
691e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
692e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
69346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Returns the visible bounds of the UI element.
69446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
69546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If a portion of the UI element is visible, only the bounds of the visible portion are
69646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * reported.
6973d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
698e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return Rect
699e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
70046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @see {@link #getBound()}
70179693ede92636fe6f3a6ec4dc049a438fd9504ffGuang Zhu     * @since API Level 17
702e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
7033d50587be8ff021369c90554d814839335b445b0Adam Momtaz    public Rect getVisibleBounds() throws UiObjectNotFoundException {
704e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
705e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
706e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
707e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
708e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getVisibleBounds(node);
709e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
710e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
711e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
71246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Returns the UI element's <code>bounds</code> property. See {@link #getVisibleBounds()}
7133d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
7143d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return Rect
7153d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @throws UiObjectNotFoundException
716dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
7173d50587be8ff021369c90554d814839335b445b0Adam Momtaz     */
7183d50587be8ff021369c90554d814839335b445b0Adam Momtaz    public Rect getBounds() throws UiObjectNotFoundException {
7193d50587be8ff021369c90554d814839335b445b0Adam Momtaz        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
7203d50587be8ff021369c90554d814839335b445b0Adam Momtaz        if(node == null) {
7213d50587be8ff021369c90554d814839335b445b0Adam Momtaz            throw new UiObjectNotFoundException(getSelector().toString());
7223d50587be8ff021369c90554d814839335b445b0Adam Momtaz        }
7233d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect nodeRect = new Rect();
7243d50587be8ff021369c90554d814839335b445b0Adam Momtaz        node.getBoundsInScreen(nodeRect);
7253d50587be8ff021369c90554d814839335b445b0Adam Momtaz
7263d50587be8ff021369c90554d814839335b445b0Adam Momtaz        return nodeRect;
7273d50587be8ff021369c90554d814839335b445b0Adam Momtaz    }
7283d50587be8ff021369c90554d814839335b445b0Adam Momtaz
7293d50587be8ff021369c90554d814839335b445b0Adam Momtaz    /**
73046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Waits a specified length of time for a UI element to become visible.
73146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
73246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * This method waits until the UI element becomes visible on the display, or
73346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * until the timeout has elapsed. You can use this method in situations where
73446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * the content that you want to select is not immediately displayed.
7353d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
73646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param timeout the amount of time to wait (in milliseconds)
73746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if the UI element is displayed, else false if timeout elapsed while waiting
738dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
739e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
740e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean waitForExists(long timeout) {
741e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(findAccessibilityNodeInfo(timeout) != null) {
742e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return true;
743e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
744e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
745e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
746e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
747e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
74846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Waits a specified length of time for a UI element to become undetectable.
7493d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
75046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * This method waits until a UI element is no longer matchable, or until the
75146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * timeout has elapsed.
75246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
75346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * A UI element becomes undetectable when the {@link UiSelector} of the object is
75446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * unable to find a match because the element has either changed its state or is no
75546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * longer displayed.
75646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
75746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * You can use this method when attempting to wait for some long operation
75846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * to compete, such as downloading a large file or connecting to a remote server.
75946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
76046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param timeout time to wait (in milliseconds)
76146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if the element is gone before timeout elapsed, else false if timeout elapsed
76246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * but a matching element is still found.
763dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
764e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
765e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean waitUntilGone(long timeout) {
766e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        long startMills = SystemClock.uptimeMillis();
767e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        long currentMills = 0;
768e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        while (currentMills <= timeout) {
769e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(findAccessibilityNodeInfo(0) == null)
770e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return true;
771e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            currentMills = SystemClock.uptimeMillis() - startMills;
772e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(timeout > 0)
773e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
774e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
775e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
776e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
777e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
778e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
77946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Check if UI element exists.
78046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
781e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This methods performs a {@link #waitForExists(long)} with zero timeout. This
782e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * basically returns immediately whether the UI element represented by this UiObject
783e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * exists or not. If you need to wait longer for this UI element, then see
784e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #waitForExists(long)}.
7853d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
786e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the UI element represented by this UiObject does exist
787dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
788e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
789e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean exists() {
790e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return waitForExists(0);
791e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
792e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
793e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private String safeStringReturn(CharSequence cs) {
794e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(cs == null)
795e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return "";
796e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return cs.toString();
797e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
798e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu}
799