UiObject.java revision 46d9444c7a39dc1c9fc60a5dcf4e79749d9b3859
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;
23e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.accessibility.AccessibilityNodeInfo;
24e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
25e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu/**
2646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * A UiObject is a representation of a UI element. It is not in any way directly bound to a
2746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * UI element as an object reference. A UiObject holds information to help it
2846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * locate a matching UI element at runtime based on the {@link UiSelector} properties specified in
2946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * its constructor. Since a UiObject is a representative for a UI element, it can
3046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * be reused for different views with matching UI elements.
31e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
32e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupublic class UiObject {
33e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final String LOG_TAG = UiObject.class.getSimpleName();
34e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_SELECTOR_TIMEOUT = 10 * 1000;
35e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_SELECTOR_POLL = 1000;
36e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // set a default timeout to 5.5s, since ANR threshold is 5s
37e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_WINDOW_TMEOUT = 5500;
38e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final int SWIPE_MARGIN_LIMIT = 5;
39e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
407f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz    private final UiSelector mSelector;
41ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    private final UiAutomatorBridge mUiAutomationBridge;
42e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
43e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
443d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Constructs a UiObject to represent a specific UI element matched by the specified
453d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * {@link UiSelector} selector properties.
463d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
47e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param selector
48e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
494ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject(UiSelector selector) {
50e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mUiAutomationBridge = UiDevice.getInstance().getAutomatorBridge();
51e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mSelector = selector;
52e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
53e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
54e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
553d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Debugging helper. A test can dump the properties of a selector as a string
563d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * to its logs if needed. <code>getSelector().toString();</code>
573d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
584ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @return {@link UiSelector}
59e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
604ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public final UiSelector getSelector() {
614ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiSelector(mSelector);
62e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
63e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
64e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
653d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Retrieves the {@link QueryController} to translate a {@link UiSelector} selector
663d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * into an {@link AccessibilityNodeInfo}.
673d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
68e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link QueryController}
69e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
70ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    QueryController getQueryController() {
71e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getQueryController();
72e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
73e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
74e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
753d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Retrieves the {@link InteractionController} to perform finger actions such as tapping,
763d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * swiping or entering text.
773d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
78e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link InteractionController}
79e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
80ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    InteractionController getInteractionController() {
81e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getInteractionController();
82e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
83e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
84e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
853d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Creates a new UiObject representing a child UI element of the element currently represented
863d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * by this UiObject.
873d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
883d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param selector for UI element to match
893d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return a new UiObject representing the matched UI element
90e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
914ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException {
924ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiObject(getSelector().childSelector(selector));
93e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
94e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
95e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
963d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Creates a new UiObject representing a child UI element from the parent element currently
973d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * represented by this object. Essentially this is starting the search from the parent
983d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * element and can also be used to find sibling UI elements to the one currently represented
993d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * by this UiObject.
1003d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1013d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param selector for the UI element to match
1023d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return a new UiObject representing the matched UI element
103e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
104e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
1054ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException {
1064ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiObject(getSelector().fromParent(selector));
107e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
108e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
109e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1103d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Counts the child UI elements immediately under the UI element currently represented by
111e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * this UiObject.
1123d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
113e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return the count of child UI elements.
114e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
115e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
116e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public int getChildCount() throws UiObjectNotFoundException {
117e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
118e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
119e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
120e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
121e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.getChildCount();
122e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
123e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
124e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1253d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Uses the member UiSelector properties to find a matching UI element reported in
1263d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * the accessibility hierarchy.
1273d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1284ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param selector {@link UiSelector}
129e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param timeout in milliseconds
130e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return AccessibilityNodeInfo if found else null
131e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
132e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout) {
133e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = null;
134e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(UiDevice.getInstance().isInWatcherContext()) {
135e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // we will NOT run watchers or do any sort of polling if the
136e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // reason we're here is because of a watcher is executing. Watchers
137e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // will not have other watchers run for them so they should not block
138e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // while they poll for items to become present. We disable polling for them.
139e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            node = getQueryController().findAccessibilityNodeInfo(getSelector());
140e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } else {
141e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            long startMills = SystemClock.uptimeMillis();
142e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            long currentMills = 0;
143e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            while (currentMills <= timeout) {
144e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                node = getQueryController().findAccessibilityNodeInfo(getSelector());
145e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if (node != null) {
146e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    break;
147e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                } else {
148e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    UiDevice.getInstance().runWatchers();
149e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
150e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                currentMills = SystemClock.uptimeMillis() - startMills;
151e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if(timeout > 0) {
152e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
153e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
154e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
155e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
156e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node;
157e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
158e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
159e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1603d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Perform the action on the UI element that is represented by this UiObject. Also see
161e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
1623d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * {@link #scrollForward()}.
1633d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
164467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
165467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
1663d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true of successful
167e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
168e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
169e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeUp(int steps) throws UiObjectNotFoundException {
1703d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
171e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
172e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
173e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.centerX(),
174e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.bottom - SWIPE_MARGIN_LIMIT, rect.centerX(), rect.top + SWIPE_MARGIN_LIMIT,
175e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                steps);
176e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
177e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
178e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
179e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object, Also see
180e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
181e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollForward()}. This method will perform the swipe gesture over any
182e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * surface. The targeted UI element does not need to have the attribute
183e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
1843d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1853d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
186467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
1873d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if successful
188e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
189e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
190e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeDown(int steps) throws UiObjectNotFoundException {
1913d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
192e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
193e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
194e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.centerX(),
195e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.top + SWIPE_MARGIN_LIMIT, rect.centerX(),
196e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.bottom - SWIPE_MARGIN_LIMIT, steps);
197e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
198e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
199e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
200e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object. Also see
201e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
202e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollForward()}. This method will perform the swipe gesture over any
203e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * surface. The targeted UI element does not need to have the attribute
204e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
2053d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2063d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
207467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
2083d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if successful
209e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
210e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
211e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeLeft(int steps) throws UiObjectNotFoundException {
2123d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
213e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
214e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
215e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.right - SWIPE_MARGIN_LIMIT,
216e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerY(), rect.left + SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
217e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
218e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
219e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
220e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object. Also see
221e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
222e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollForward()}. This method will perform the swipe gesture over any
223e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * surface. The targeted UI element does not need to have the attribute
224e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
2253d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2263d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
227467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
2283d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if successful
229e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
230e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
231e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeRight(int steps) throws UiObjectNotFoundException {
2323d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
233e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
234e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
235e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.left + SWIPE_MARGIN_LIMIT,
236e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerY(), rect.right - SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
237e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
238e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
239e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2403d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Finds the visible bounds of a partially visible UI element
2413d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
242e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param node
2437f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * @return null if node is null, else a Rect containing visible bounds
244e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
245e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private Rect getVisibleBounds(AccessibilityNodeInfo node) {
246e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (node == null) {
247e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return null;
248e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
249e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
250e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // targeted node's bounds
2517f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz        Rect nodeRect = AccessibilityNodeInfoHelper.getVisibleBoundsInScreen(node);
252e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
253e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // is the targeted node within a scrollable container?
254e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo scrollableParentNode = getScrollableParent(node);
255e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(scrollableParentNode == null) {
256e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // nothing to adjust for so return the node's Rect as is
257e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return nodeRect;
258e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
259e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
260e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // Scrollable parent's visible bounds
2617f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz        Rect parentRect = AccessibilityNodeInfoHelper
2627f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz                .getVisibleBoundsInScreen(scrollableParentNode);
263e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // adjust for partial clipping of targeted by parent node if required
264e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        nodeRect.intersect(parentRect);
265e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return nodeRect;
266e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
267e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
268e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2697f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * Walk the hierarchy up to find a scrollable parent. A scrollable parent
2707f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * indicates that this node may be in a content where it is partially
2717f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * visible due to scrolling. its clickable center maybe invisible and
2727f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * adjustments should be made to the click coordinates.
2733d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
274e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param node
275e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return
276e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
277e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private AccessibilityNodeInfo getScrollableParent(AccessibilityNodeInfo node) {
278e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo parent = node;
279e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        while(parent != null) {
280e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            parent = parent.getParent();
281e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (parent != null && parent.isScrollable()) {
282e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return parent;
283e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
284e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
285e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return null;
286e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
287e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
288e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2893d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Performs a click at the center of the visible bounds of the UI element represented
29046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * by this UiObject.
2913d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
292e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true id successful else false
293e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
294e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
295e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean click() throws UiObjectNotFoundException {
296e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
297e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
298e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
299e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
300e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
3014ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getInteractionController().click(rect.centerX(), rect.centerY());
302e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
303e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
304e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3053d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link #clickAndWaitForNewWindow(long)}
306a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * This method is intended to reliably wait for window transitions that would typically take
307a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * longer than the usual default timeouts.
3083d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
3093d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if the event was triggered, else false
3103d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @throws UiObjectNotFoundException
3113d50587be8ff021369c90554d814839335b445b0Adam Momtaz     */
312e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickAndWaitForNewWindow() throws UiObjectNotFoundException {
313e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return clickAndWaitForNewWindow(WAIT_FOR_WINDOW_TMEOUT);
314e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
315e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
316e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
31746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Performs a click at the center of the visible bounds of the UI element represented
31846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * by this UiObject and waits for window transitions.
31946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
3203d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * This method differ from {@link UiObject#click()} only in that this method waits for a
32146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * a new window transition as a result of the click. Some examples of a window transition:
322e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>launching a new activity</li>
323e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>bringing up a pop-up menu</li>
324e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>bringing up a dialog</li>
325e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
3263d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param timeout timeout before giving up on waiting for a new window
327e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the event was triggered, else false
328e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
329e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
330e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickAndWaitForNewWindow(long timeout) throws UiObjectNotFoundException {
331e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
332e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
333e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
334e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
335e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
3364ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getInteractionController().clickAndWaitForNewWindow(
337e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerX(), rect.centerY(), timeout);
338e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
339e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
340e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3413d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Clicks the top and left corner of the UI element
3423d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
343e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on success
344e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws Exception
345e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
346e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickTopLeft() throws UiObjectNotFoundException {
347e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
348e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
349e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
350e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
351e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
3524ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getInteractionController().click(rect.left + 5, rect.top + 5);
353e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
354e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
355e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3563d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks bottom and right corner of the UI element
3573d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
358e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
359e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
360e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
361e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClickBottomRight() throws UiObjectNotFoundException  {
362e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
363e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
364e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
365e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
366e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
367e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().longTap(rect.right - 5, rect.bottom - 5);
368e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
369e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
370e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3713d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Clicks the bottom and right corner of the UI element
3723d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
373e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on success
374e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws Exception
375e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
376e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickBottomRight() throws UiObjectNotFoundException {
377e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
378e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
379e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
380e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
381e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
3824ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getInteractionController().click(rect.right - 5, rect.bottom - 5);
383e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
384e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
385e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3863d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks the center of the visible bounds of the UI element
3873d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
388e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
389e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
390e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
391e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClick() throws UiObjectNotFoundException  {
392e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
393e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
394e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
395e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
396e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
397e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().longTap(rect.centerX(), rect.centerY());
398e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
399e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
400e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4013d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks on the top and left corner of the UI element
4023d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
403e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
404e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
405e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
406e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClickTopLeft() throws UiObjectNotFoundException {
407e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
408e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
409e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
410e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
411e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
412e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().longTap(rect.left + 5, rect.top + 5);
413e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
414e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
415e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4163d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the <code>text</code> property of the UI element
4173d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
418e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return text value of the current node represented by this UiObject
419e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException if no match could be found
420e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
421e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getText() throws UiObjectNotFoundException {
422e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
423e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
424e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
425e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
426e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        String retVal = safeStringReturn(node.getText());
427e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Log.d(LOG_TAG, String.format("getText() = %s", retVal));
428e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return retVal;
429e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
430e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
431e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4323d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the <code>content_desc</code> property of the UI element
4333d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
434e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return value of node attribute "content_desc"
435e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
436e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
437e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getContentDescription() throws UiObjectNotFoundException {
438e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
439e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
440e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
441e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
442e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return safeStringReturn(node.getContentDescription());
443e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
444e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
445e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
44646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Sets the text in an editable field, after clearing the field's content.
44746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
44846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * The {@link UiSelector} selector of this object must reference a UI element that is editable.
44946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
45046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * When you call this method, the method first simulates a {@link #click()} on
45146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * editable field to set focus. The method then clears the field's contents
45246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * and injects your specified text into the field.
4533d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
45446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If you want to capture the original contents of the field, call {@link #getText()} first.
45546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * You can then modify the text and use this method to update the field.
45646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
45746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param text string to set
458e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation is successful
459e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
460e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
461e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean setText(String text) throws UiObjectNotFoundException {
462e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        clearTextField();
463e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().sendText(text);
464e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
465e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
466e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
46746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Clears the existing text contents in an editable field.
46846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
46946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * The {@link UiSelector} of this object must reference a UI element that is editable.
47046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
47146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * When you call this method, the method first sets focus at the start edge of the field.
47246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * The method then simulates a long-press to select the existing text, and deletes the
47346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * selected text.
47446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
47546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If a "Select-All" option is displayed, the method will automatically attempt to use it
47646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * to ensure full text selection.
47746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
47846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Note that it is possible that not all the text in the field is selected; for example,
47946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * if the text contains separators such as spaces, slashes, at symbol etc.
48046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Also, not all editable fields support the long-press functionality.
48146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
482e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
483e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
484e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void clearTextField() throws UiObjectNotFoundException {
485e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // long click left + center
486e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
487e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
488e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
489e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
490e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
491e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getInteractionController().longTap(rect.left + 20, rect.centerY());
492e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // check if the edit menu is open
4934ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        UiObject selectAll = new UiObject(new UiSelector().descriptionContains("Select all"));
494e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(selectAll.waitForExists(50))
495e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            selectAll.click();
496e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // wait for the selection
497e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        SystemClock.sleep(250);
498e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // delete it
499e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0);
500e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
501e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
502e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5033d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>checked</code> property is currently true
5043d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
505e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
506e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
507e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isChecked() throws UiObjectNotFoundException {
508e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
509e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
510e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
511e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
512e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isChecked();
513e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
514e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
515e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5163d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>selected</code> property is currently true
5173d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
518e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
519e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
520e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
521e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isSelected() throws UiObjectNotFoundException {
522e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
523e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
524e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
525e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
526e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isSelected();
527e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
528e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
529e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5303d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>checkable</code> property is currently true
5313d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
532e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
533e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
534e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
535e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isCheckable() throws UiObjectNotFoundException {
536e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
537e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
538e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
539e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
540e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isCheckable();
541e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
542e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
543e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5443d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>enabled</code> property is currently true
5453d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
546e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
547e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
548e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
549e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isEnabled() 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.isEnabled();
555e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
556e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
557e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5583d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>clickable</code> property is currently true
5593d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
560e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
561e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
562e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
563e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isClickable() throws UiObjectNotFoundException {
564e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
565e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
566e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
567e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
568e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isClickable();
569e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
570e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
571e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5723d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>focused</code> property is currently true
5733d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
574e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
575e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
576e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
577e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isFocused() throws UiObjectNotFoundException {
578e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
579e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
580e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
581e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
582e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isFocused();
583e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
584e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
585e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5863d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>focusable</code> property is currently true
5873d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
588e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
589e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
590e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
591e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isFocusable() throws UiObjectNotFoundException {
592e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
593e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
594e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
595e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
596e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isFocusable();
597e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
598e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
599e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6003d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>scrollable</code> property is currently true
6013d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
602e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
603e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
604e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
605e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isScrollable() throws UiObjectNotFoundException {
606e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
607e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
608e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
609e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
610e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isScrollable();
611e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
612e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
613e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6143d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>long-clickable</code> property is currently true
6153d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
616e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
617e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
618e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
619e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isLongClickable() throws UiObjectNotFoundException {
620e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
621e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
622e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
623e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
624e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isLongClickable();
625e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
626e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
627e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6283d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the UI element's <code>package</code> property
6293d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
6303d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if it is else false
631e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
632e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
633e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getPackageName() throws UiObjectNotFoundException {
634e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
635e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
636e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
637e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
638e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return safeStringReturn(node.getPackageName());
639e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
640e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
641e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
64246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Returns the visible bounds of the UI element.
64346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
64446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If a portion of the UI element is visible, only the bounds of the visible portion are
64546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * reported.
6463d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
647e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return Rect
648e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
64946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @see {@link #getBound()}
650e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
6513d50587be8ff021369c90554d814839335b445b0Adam Momtaz    public Rect getVisibleBounds() throws UiObjectNotFoundException {
652e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
653e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
654e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
655e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
656e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getVisibleBounds(node);
657e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
658e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
659e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
66046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Returns the UI element's <code>bounds</code> property. See {@link #getVisibleBounds()}
6613d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
6623d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return Rect
6633d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @throws UiObjectNotFoundException
6643d50587be8ff021369c90554d814839335b445b0Adam Momtaz     */
6653d50587be8ff021369c90554d814839335b445b0Adam Momtaz    public Rect getBounds() throws UiObjectNotFoundException {
6663d50587be8ff021369c90554d814839335b445b0Adam Momtaz        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
6673d50587be8ff021369c90554d814839335b445b0Adam Momtaz        if(node == null) {
6683d50587be8ff021369c90554d814839335b445b0Adam Momtaz            throw new UiObjectNotFoundException(getSelector().toString());
6693d50587be8ff021369c90554d814839335b445b0Adam Momtaz        }
6703d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect nodeRect = new Rect();
6713d50587be8ff021369c90554d814839335b445b0Adam Momtaz        node.getBoundsInScreen(nodeRect);
6723d50587be8ff021369c90554d814839335b445b0Adam Momtaz
6733d50587be8ff021369c90554d814839335b445b0Adam Momtaz        return nodeRect;
6743d50587be8ff021369c90554d814839335b445b0Adam Momtaz    }
6753d50587be8ff021369c90554d814839335b445b0Adam Momtaz
6763d50587be8ff021369c90554d814839335b445b0Adam Momtaz    /**
67746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Waits a specified length of time for a UI element to become visible.
67846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
67946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * This method waits until the UI element becomes visible on the display, or
68046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * until the timeout has elapsed. You can use this method in situations where
68146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * the content that you want to select is not immediately displayed.
6823d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
68346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param timeout the amount of time to wait (in milliseconds)
68446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if the UI element is displayed, else false if timeout elapsed while waiting
685e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
686e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean waitForExists(long timeout) {
687e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(findAccessibilityNodeInfo(timeout) != null) {
688e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return true;
689e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
690e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
691e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
692e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
693e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
69446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Waits a specified length of time for a UI element to become undetectable.
6953d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
69646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * This method waits until a UI element is no longer matchable, or until the
69746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * timeout has elapsed.
69846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
69946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * A UI element becomes undetectable when the {@link UiSelector} of the object is
70046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * unable to find a match because the element has either changed its state or is no
70146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * longer displayed.
70246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
70346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * You can use this method when attempting to wait for some long operation
70446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * to compete, such as downloading a large file or connecting to a remote server.
70546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
70646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param timeout time to wait (in milliseconds)
70746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if the element is gone before timeout elapsed, else false if timeout elapsed
70846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * but a matching element is still found.
709e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
710e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean waitUntilGone(long timeout) {
711e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        long startMills = SystemClock.uptimeMillis();
712e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        long currentMills = 0;
713e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        while (currentMills <= timeout) {
714e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(findAccessibilityNodeInfo(0) == null)
715e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return true;
716e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            currentMills = SystemClock.uptimeMillis() - startMills;
717e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(timeout > 0)
718e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
719e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
720e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
721e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
722e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
723e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
72446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Check if UI element exists.
72546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
726e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This methods performs a {@link #waitForExists(long)} with zero timeout. This
727e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * basically returns immediately whether the UI element represented by this UiObject
728e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * exists or not. If you need to wait longer for this UI element, then see
729e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #waitForExists(long)}.
7303d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
731e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the UI element represented by this UiObject does exist
732e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
733e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean exists() {
734e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return waitForExists(0);
735e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
736e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
737e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private String safeStringReturn(CharSequence cs) {
738e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(cs == null)
739e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return "";
740e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return cs.toString();
741e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
742e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu}
743