UiObject.java revision ddc1008f06fd2a875037026490ce1f848a442572
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/**
263d50587be8ff021369c90554d814839335b445b0Adam Momtaz * UiObject is a representation of UI element. It is not in any way directly bound to a
273d50587be8ff021369c90554d814839335b445b0Adam Momtaz * UI element as an object reference. A UiObject holds information to help it locate
283d50587be8ff021369c90554d814839335b445b0Adam Momtaz * at runtime a matching UI element based on its {@UiSelector} properties specified in
293d50587be8ff021369c90554d814839335b445b0Adam Momtaz * its constructor. Since a UiObject is a representative for a matching UI element, it can
303d50587be8ff021369c90554d814839335b445b0Adam Momtaz * be reused on different screens and applications with matching UI elements. Using a
313d50587be8ff021369c90554d814839335b445b0Adam Momtaz * UiObject on a screen where none of the displayed UI elements match its UiSelector's
323d50587be8ff021369c90554d814839335b445b0Adam Momtaz * properties will result in a {@UiObjectNotFoundException} to be thrown.
33e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
34e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupublic class UiObject {
35e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final String LOG_TAG = UiObject.class.getSimpleName();
36e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_SELECTOR_TIMEOUT = 10 * 1000;
37e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_SELECTOR_POLL = 1000;
38e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // set a default timeout to 5.5s, since ANR threshold is 5s
39e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_WINDOW_TMEOUT = 5500;
40e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final int SWIPE_MARGIN_LIMIT = 5;
41e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
42ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    private UiSelector mSelector;
43ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    private final UiAutomatorBridge mUiAutomationBridge;
44e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
45e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
463d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Constructs a UiObject to represent a specific UI element matched by the specified
473d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * {@link UiSelector} selector properties.
483d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
49e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param selector
50e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
514ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject(UiSelector selector) {
52e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mUiAutomationBridge = UiDevice.getInstance().getAutomatorBridge();
53e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mSelector = selector;
54e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
55e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
56e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
573d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Debugging helper. A test can dump the properties of a selector as a string
583d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * to its logs if needed. <code>getSelector().toString();</code>
593d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
604ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @return {@link UiSelector}
61e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
624ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public final UiSelector getSelector() {
634ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiSelector(mSelector);
64e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
65e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
66e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
673d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Retrieves the {@link QueryController} to translate a {@link UiSelector} selector
683d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * into an {@link AccessibilityNodeInfo}.
693d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
70e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link QueryController}
71e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
72ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    QueryController getQueryController() {
73e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getQueryController();
74e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
75e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
76e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
773d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Retrieves the {@link InteractionController} to perform finger actions such as tapping,
783d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * swiping or entering text.
793d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
80e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link InteractionController}
81e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
82ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    InteractionController getInteractionController() {
83e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getInteractionController();
84e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
85e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
86e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
873d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Creates a new UiObject representing a child UI element of the element currently represented
883d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * by this UiObject.
893d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
903d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param selector for UI element to match
913d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return a new UiObject representing the matched UI element
92e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
934ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException {
944ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiObject(getSelector().childSelector(selector));
95e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
96e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
97e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
983d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Creates a new UiObject representing a child UI element from the parent element currently
993d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * represented by this object. Essentially this is starting the search from the parent
1003d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * element and can also be used to find sibling UI elements to the one currently represented
1013d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * by this UiObject.
1023d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1033d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param selector for the UI element to match
1043d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return a new UiObject representing the matched UI element
105e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
106e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
1074ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException {
1084ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiObject(getSelector().fromParent(selector));
109e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
110e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
111e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1123d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Counts the child UI elements immediately under the UI element currently represented by
113e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * this UiObject.
1143d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
115e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return the count of child UI elements.
116e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
117e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
118e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public int getChildCount() throws UiObjectNotFoundException {
119e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
120e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
121e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
122e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
123e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.getChildCount();
124e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
125e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
126e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1273d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Uses the member UiSelector properties to find a matching UI element reported in
1283d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * the accessibility hierarchy.
1293d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1304ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param selector {@link UiSelector}
131e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param timeout in milliseconds
132e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return AccessibilityNodeInfo if found else null
133e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
134e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout) {
135e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = null;
136e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(UiDevice.getInstance().isInWatcherContext()) {
137e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // we will NOT run watchers or do any sort of polling if the
138e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // reason we're here is because of a watcher is executing. Watchers
139e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // will not have other watchers run for them so they should not block
140e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // while they poll for items to become present. We disable polling for them.
141e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            node = getQueryController().findAccessibilityNodeInfo(getSelector());
142e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } else {
143e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            long startMills = SystemClock.uptimeMillis();
144e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            long currentMills = 0;
145e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            while (currentMills <= timeout) {
146e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                node = getQueryController().findAccessibilityNodeInfo(getSelector());
147e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if (node != null) {
148e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    break;
149e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                } else {
150e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    UiDevice.getInstance().runWatchers();
151e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
152e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                currentMills = SystemClock.uptimeMillis() - startMills;
153e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if(timeout > 0) {
154e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
155e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
156e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
157e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
158e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node;
159e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
160e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
161e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1623d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Perform the action on the UI element that is represented by this UiObject. Also see
163e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
1643d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * {@link #scrollForward()}.
1653d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
166467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
167467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
1683d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true of successful
169e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
170e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
171e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeUp(int steps) throws UiObjectNotFoundException {
1723d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
173e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
174e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
175e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.centerX(),
176e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.bottom - SWIPE_MARGIN_LIMIT, rect.centerX(), rect.top + SWIPE_MARGIN_LIMIT,
177e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                steps);
178e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
179e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
180e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
181e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object, Also see
182e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
183e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollForward()}. This method will perform the swipe gesture over any
184e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * surface. The targeted UI element does not need to have the attribute
185e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
1863d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1873d50587be8ff021369c90554d814839335b445b0Adam 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 if successful
190e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
191e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
192e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeDown(int steps) throws UiObjectNotFoundException {
1933d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
194e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
195e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
196e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.centerX(),
197e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.top + SWIPE_MARGIN_LIMIT, rect.centerX(),
198e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.bottom - SWIPE_MARGIN_LIMIT, steps);
199e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
200e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
201e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
202e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object. Also see
203e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
204e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollForward()}. This method will perform the swipe gesture over any
205e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * surface. The targeted UI element does not need to have the attribute
206e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
2073d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2083d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
209467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
2103d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if successful
211e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
212e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
213e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeLeft(int steps) throws UiObjectNotFoundException {
2143d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
215e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
216e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
217e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.right - SWIPE_MARGIN_LIMIT,
218e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerY(), rect.left + SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
219e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
220e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
221e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
222e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object. Also see
223e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
224e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollForward()}. This method will perform the swipe gesture over any
225e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * surface. The targeted UI element does not need to have the attribute
226e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
2273d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2283d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
229467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
2303d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if successful
231e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
232e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
233e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeRight(int steps) throws UiObjectNotFoundException {
2343d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
235e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
236e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
237e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.left + SWIPE_MARGIN_LIMIT,
238e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerY(), rect.right - SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
239e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
240e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
241e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2423d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Finds the visible bounds of a partially visible UI element
2433d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
244e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param node
245e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return the same AccessibilityNodeInfo passed in as argument
246e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
247e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private Rect getVisibleBounds(AccessibilityNodeInfo node) {
248e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (node == null) {
249e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return null;
250e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
251e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
252e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // targeted node's bounds
253e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect nodeRect = new Rect();
254e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        node.getBoundsInScreen(nodeRect);
255e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
256e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // is the targeted node within a scrollable container?
257e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo scrollableParentNode = getScrollableParent(node);
258e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(scrollableParentNode == null) {
259e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // nothing to adjust for so return the node's Rect as is
260e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return nodeRect;
261e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
262e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
263e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // Scrollable parent's visible bounds
264e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect parentRect = new Rect();
265e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        scrollableParentNode.getBoundsInScreen(parentRect);
266e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // adjust for partial clipping of targeted by parent node if required
267e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        nodeRect.intersect(parentRect);
268e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return nodeRect;
269e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
270e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
271e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
272e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Walk the hierarchy up to find a scrollable parent. A scrollable parent indicates that
273e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * this node may be in a content where it is partially visible due to scrolling. its
274e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * clickable center maybe invisible and adjustments should be made to the click coordinates.
2753d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
276e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param node
277e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return
278e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
279e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private AccessibilityNodeInfo getScrollableParent(AccessibilityNodeInfo node) {
280e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo parent = node;
281e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        while(parent != null) {
282e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            parent = parent.getParent();
283e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (parent != null && parent.isScrollable()) {
284e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return parent;
285e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
286e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
287e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return null;
288e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
289e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
290e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2913d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Performs a click at the center of the visible bounds of the UI element represented
2923d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * by this UiObject </p>
2933d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Take note that the UI element represented by this UiObject may not have its attribute
2943d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * <code>clickable</code> set to <code>true</code> however one of its ancestor elements
2953d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * may be clickable. This is the reason this method does not check the clickable attribute.
2963d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
297e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true id successful else false
298e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
299e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
300e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean click() throws UiObjectNotFoundException {
301e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
302e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
303e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
304e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
305e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
3064ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getInteractionController().click(rect.centerX(), rect.centerY());
307e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
308e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
309e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3103d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link #clickAndWaitForNewWindow(long)}
3113d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * This method is intended for reliably wait for window transitions that would typically take
3123d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * longer than the usual deault timeouts.
3133d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
3143d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if the event was triggered, else false
3153d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @throws UiObjectNotFoundException
3163d50587be8ff021369c90554d814839335b445b0Adam Momtaz     */
317e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickAndWaitForNewWindow() throws UiObjectNotFoundException {
318e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return clickAndWaitForNewWindow(WAIT_FOR_WINDOW_TMEOUT);
319e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
320e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
321e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3223d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Performs a click at the center of the visible bounds of the UI element UI element represented
3233d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * by this UiObject </p>
3243d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * This method differ from {@link UiObject#click()} only in that this method waits for a
3253d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * a new window transition as a result of the tap. Some examples of a window transition:
326e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>launching a new activity</li>
327e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>bringing up a pop-up menu</li>
328e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>bringing up a dialog</li>
329e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
3303d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param timeout timeout before giving up on waiting for a new window
331e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the event was triggered, else false
332e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
333e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
334e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickAndWaitForNewWindow(long timeout) throws UiObjectNotFoundException {
335e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
336e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
337e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
338e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
339e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
3404ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getInteractionController().clickAndWaitForNewWindow(
341e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerX(), rect.centerY(), timeout);
342e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
343e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
344e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3453d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Clicks the top and left corner of the UI element
3463d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
347e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on success
348e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws Exception
349e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
350e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickTopLeft() throws UiObjectNotFoundException {
351e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
352e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
353e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
354e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
355e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
3564ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getInteractionController().click(rect.left + 5, rect.top + 5);
357e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
358e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
359e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3603d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks bottom and right corner of the UI element
3613d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
362e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
363e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
364e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
365e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClickBottomRight() throws UiObjectNotFoundException  {
366e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
367e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
368e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
369e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
370e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
371e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().longTap(rect.right - 5, rect.bottom - 5);
372e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
373e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
374e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3753d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Clicks the bottom and right corner of the UI element
3763d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
377e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on success
378e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws Exception
379e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
380e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickBottomRight() throws UiObjectNotFoundException {
381e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
382e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
383e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
384e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
385e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
3864ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getInteractionController().click(rect.right - 5, rect.bottom - 5);
387e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
388e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
389e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3903d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks the center of the visible bounds of the UI element
3913d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
392e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
393e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
394e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
395e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClick() 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.centerX(), rect.centerY());
402e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
403e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
404e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4053d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks on the top and left corner of the UI element
4063d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
407e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
408e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
409e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
410e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClickTopLeft() throws UiObjectNotFoundException {
411e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
412e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
413e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
414e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
415e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
416e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().longTap(rect.left + 5, rect.top + 5);
417e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
418e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
419e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4203d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the <code>text</code> property of the UI element
4213d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
422e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return text value of the current node represented by this UiObject
423e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException if no match could be found
424e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
425e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getText() throws UiObjectNotFoundException {
426e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
427e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
428e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
429e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
430e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        String retVal = safeStringReturn(node.getText());
431e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Log.d(LOG_TAG, String.format("getText() = %s", retVal));
432e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return retVal;
433e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
434e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
435e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4363d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the <code>content_desc</code> property of the UI element
4373d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
438e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return value of node attribute "content_desc"
439e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
440e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
441e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getContentDescription() throws UiObjectNotFoundException {
442e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
443e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
444e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
445e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
446e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return safeStringReturn(node.getContentDescription());
447e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
448e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
449e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
450e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * First this function clears the existing text from the field. If this is not the intended
451e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * behavior, do a {@link #getText()} first, modify the text and then use this function.
4524ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * The {@link UiSelector} selector of this object MUST be pointing directly at a UI element
4533d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * that accepts edits. The way this method works is by first performing a {@link #click()}
4543d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * on the edit field to set focus then it begins injecting the text
4553d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
456e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param text
457e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation is successful
458e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
459e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
460e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean setText(String text) throws UiObjectNotFoundException {
461e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        clearTextField();
462e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().sendText(text);
463e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
464e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
465e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
466e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * The object targeted must be an edit field capable of performing text insert. This
4673d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * method sets focus at the start edge of the field and long presses to select
4683d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * existing text. Note: It is possible that not all the text is selected especially
4693d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * if the text contains separators such as spaces, slashes, at signs etc... The function
4703d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * will attempt to use the "Select-All" option if one is displayed to ensure full text
4713d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * selection.
472e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
473e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
474e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void clearTextField() throws UiObjectNotFoundException {
475e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // long click left + center
476e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
477e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
478e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
479e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
480e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
481e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getInteractionController().longTap(rect.left + 20, rect.centerY());
482e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // check if the edit menu is open
4834ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        UiObject selectAll = new UiObject(new UiSelector().descriptionContains("Select all"));
484e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(selectAll.waitForExists(50))
485e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            selectAll.click();
486e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // wait for the selection
487e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        SystemClock.sleep(250);
488e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // delete it
489e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0);
490e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
491e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
492e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4933d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>checked</code> property is currently true
4943d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
495e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
496e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
497e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isChecked() throws UiObjectNotFoundException {
498e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
499e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
500e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
501e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
502e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isChecked();
503e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
504e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
505e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5063d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>selected</code> property is currently true
5073d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
508e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
509e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
510e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
511e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isSelected() throws UiObjectNotFoundException {
512e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
513e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
514e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
515e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
516e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isSelected();
517e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
518e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
519e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5203d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>checkable</code> property is currently true
5213d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
522e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
523e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
524e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
525e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isCheckable() throws UiObjectNotFoundException {
526e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
527e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
528e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
529e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
530e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isCheckable();
531e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
532e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
533e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5343d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>enabled</code> property is currently true
5353d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
536e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
537e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
538e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
539e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isEnabled() throws UiObjectNotFoundException {
540e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
541e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
542e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
543e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
544e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isEnabled();
545e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
546e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
547e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5483d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>clickable</code> property is currently true
5493d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
550e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
551e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
552e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
553e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isClickable() throws UiObjectNotFoundException {
554e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
555e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
556e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
557e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
558e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isClickable();
559e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
560e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
561e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5623d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>focused</code> property is currently true
5633d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
564e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
565e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
566e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
567e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isFocused() throws UiObjectNotFoundException {
568e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
569e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
570e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
571e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
572e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isFocused();
573e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
574e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
575e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5763d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>focusable</code> property is currently true
5773d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
578e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
579e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
580e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
581e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isFocusable() throws UiObjectNotFoundException {
582e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
583e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
584e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
585e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
586e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isFocusable();
587e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
588e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
589e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5903d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>scrollable</code> property is currently true
5913d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
592e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
593e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
594e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
595e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isScrollable() throws UiObjectNotFoundException {
596e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
597e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
598e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
599e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
600e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isScrollable();
601e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
602e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
603e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6043d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>long-clickable</code> property is currently true
6053d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
606e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
607e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
608e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
609e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isLongClickable() 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.isLongClickable();
615e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
616e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
617e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6183d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the UI element's <code>package</code> property
6193d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
6203d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if it is else false
621e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
622e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
623e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getPackageName() throws UiObjectNotFoundException {
624e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
625e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
626e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
627e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
628e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return safeStringReturn(node.getPackageName());
629e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
630e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
631e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6323d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reports the visible bounds of the UI element. If a portion of the UI element is
6333d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * visible, only the bounds of the visible portion are reported. see {@link #getBound()}
6343d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
635e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return Rect
636e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
637e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
6383d50587be8ff021369c90554d814839335b445b0Adam Momtaz    public Rect getVisibleBounds() throws UiObjectNotFoundException {
639e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
640e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
641e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
642e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
643e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getVisibleBounds(node);
644e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
645e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
646e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6473d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the UI element's <code>bounds</code> property. See {@link #getVisibleBounds()}
6483d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
6493d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return Rect
6503d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @throws UiObjectNotFoundException
6513d50587be8ff021369c90554d814839335b445b0Adam Momtaz     */
6523d50587be8ff021369c90554d814839335b445b0Adam Momtaz    public Rect getBounds() throws UiObjectNotFoundException {
6533d50587be8ff021369c90554d814839335b445b0Adam Momtaz        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
6543d50587be8ff021369c90554d814839335b445b0Adam Momtaz        if(node == null) {
6553d50587be8ff021369c90554d814839335b445b0Adam Momtaz            throw new UiObjectNotFoundException(getSelector().toString());
6563d50587be8ff021369c90554d814839335b445b0Adam Momtaz        }
6573d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect nodeRect = new Rect();
6583d50587be8ff021369c90554d814839335b445b0Adam Momtaz        node.getBoundsInScreen(nodeRect);
6593d50587be8ff021369c90554d814839335b445b0Adam Momtaz
6603d50587be8ff021369c90554d814839335b445b0Adam Momtaz        return nodeRect;
6613d50587be8ff021369c90554d814839335b445b0Adam Momtaz    }
6623d50587be8ff021369c90554d814839335b445b0Adam Momtaz
6633d50587be8ff021369c90554d814839335b445b0Adam Momtaz    /**
664e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This method will wait for a UI element to become visible on the display. It
665e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * can be used for situations where the content to be selected is not yet displayed
6663d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
667e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param timeout
668e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the UI element exists else false for timeout while waiting
669e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
670e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean waitForExists(long timeout) {
671e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(findAccessibilityNodeInfo(timeout) != null) {
672e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return true;
673e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
674e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
675e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
676e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
677e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6783d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Helper to wait for a UI element to no longer be matchable. An element becomes
6793d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * un-matchable when this UiObject's {@link UiSelector} no longer matches the
6803d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * UI element because it has either changed its state or is no longer displayed.
6813d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
682e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param timeout
6833d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if gone before timeout else false for still matching an element
684e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
685e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean waitUntilGone(long timeout) {
686e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        long startMills = SystemClock.uptimeMillis();
687e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        long currentMills = 0;
688e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        while (currentMills <= timeout) {
689e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(findAccessibilityNodeInfo(0) == null)
690e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return true;
691e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            currentMills = SystemClock.uptimeMillis() - startMills;
692e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(timeout > 0)
693e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
694e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
695e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
696e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
697e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
698e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
699e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This methods performs a {@link #waitForExists(long)} with zero timeout. This
700e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * basically returns immediately whether the UI element represented by this UiObject
701e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * exists or not. If you need to wait longer for this UI element, then see
702e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #waitForExists(long)}.
7033d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
704e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the UI element represented by this UiObject does exist
705e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
706e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean exists() {
707e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return waitForExists(0);
708e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
709e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
710e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private String safeStringReturn(CharSequence cs) {
711e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(cs == null)
712e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return "";
713e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return cs.toString();
714e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
715e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu}
716