UiObject.java revision e54d649fb83a0a44516e5c25a9ac1992c8950e59
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/**
26e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * UiObject is designed to be a representation of displayed UI element. It is not in any way
27e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * directly bound to a specific UI element. It holds information to find any displayed UI element
28e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * that matches its selectors. This means it can be reused on any screen where a UI element
29e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * exists to match its selector criteria. This help tests define a single UiObject say for
30e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * an "OK" or tool-bar button and use it across many activities.
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
40e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected By mSelector;
41e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected final UiDevice mDevice;
42e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected final UiAutomatorBridge mUiAutomationBridge;
43e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
44e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
45e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Constructs a UiObject that references any UI element that may match the specified
46e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link By} selector. UiObject can be pre-constructed and reused across an application
47e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * where applicable. A good example is a tool bar and its buttons. A tool bar may remain
48e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * visible on the various views the application is displaying but may have different
49e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * contextual buttons displayed for each. The same UiObject that describes the tool bar
50e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * can be reused.<p/>
51e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * It is a good idea in certain cases before using any operations of this UiObject is to
52e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * call {@link #exists()} to verify if the UI element matching this object is actually
53e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * visible on the screen. This way the test can gracefully handle this situation else
54e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * a {@link UiObjectNotFoundException} will be thrown when using this object.
55e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param selector
56e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
57e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public UiObject(By selector) {
58e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mUiAutomationBridge = UiDevice.getInstance().getAutomatorBridge();
59e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mDevice = UiDevice.getInstance();
60e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mSelector = selector;
61e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
62e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
63e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
64e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper for debugging. During testing a test can dump the selector of the object into
65e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * its logs of needed. <code>getSelector().toString();</code>
66e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link By}
67e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
68e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public final By getSelector() {
69e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return By.selector(mSelector);
70e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
71e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
72e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
73e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Used in test operations to retrieve the {@link QueryController} to translate
74e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * a {@link By} selector into an {@link AccessibilityNodeInfo}.
75e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link QueryController}
76e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
77e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected QueryController getQueryController() {
78e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getQueryController();
79e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
80e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
81e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
82e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Used in test operations to retrieve the {@link InteractionController} to perform
83e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * finger actions such as tapping, swiping or entering text.
84e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link InteractionController}
85e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
86e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected InteractionController getInteractionController() {
87e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getInteractionController();
88e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
89e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
90e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
91e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Creates a new UiObject that points at a child UI element of the currently pointed
92e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * to element by this object. UI element are considered layout elements as well as UI
93e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * widgets. A layout element could have child widgets like buttons and text labels.
94e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param selector
95e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return a new UiObject with a new selector. It doesn't test if the object exists.
96e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
97e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public UiObject getChild(By selector) throws UiObjectNotFoundException {
98e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return new UiObject(By.selector(getSelector().childSelector(selector)));
99e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
100e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
101e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
102e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Creates a new UiObject that points at a child UI element of the parent of this object.
103e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Essentially this is starting the search from any one of the siblings UI element of this
104e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * element.
105e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param selector
106e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return
107e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
108e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
109e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public UiObject getFromParent(By selector) throws UiObjectNotFoundException {
110e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return new UiObject(By.selector(getSelector().fromParent(selector)));
111e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
112e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
113e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
114e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Counts the child UI elements immediately under the UI element currently referenced by
115e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * this UiObject.
116e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return the count of child UI elements.
117e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
118e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
119e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public int getChildCount() throws UiObjectNotFoundException {
120e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
121e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
122e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
123e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
124e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.getChildCount();
125e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
126e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
127e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
128e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper method to allow for a specific content to become present on the
129e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * screen before moving on to do other operations.
130e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param selector {@link By}
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    /**
162e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object. Also see
163e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
164e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollForward()}. This method will perform the swipe gesture over any
165e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * surface. The targeted UI element does not need to have the attribute
166e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
167e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param steps indicates the number of injected move steps into the system. More steps
168e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * injected the smoother the motion and slower.
169e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return
170e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
171e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
172e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeUp(int steps) throws UiObjectNotFoundException {
173e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getBounds();
174e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
175e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
176e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.centerX(),
177e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.bottom - SWIPE_MARGIN_LIMIT, rect.centerX(), rect.top + SWIPE_MARGIN_LIMIT,
178e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                steps);
179e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
180e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
181e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
182e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object, Also see
183e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
184e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollForward()}. This method will perform the swipe gesture over any
185e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * surface. The targeted UI element does not need to have the attribute
186e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
187e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param steps indicates the number of injected move steps into the system. More steps
188e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * injected the smoother the motion and slower.
189e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return
190e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
191e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
192e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeDown(int steps) throws UiObjectNotFoundException {
193e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getBounds();
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.
207e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param steps indicates the number of injected move steps into the system. More steps
208e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * injected the smoother the motion and slower.
209e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return
210e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
211e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
212e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeLeft(int steps) throws UiObjectNotFoundException {
213e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getBounds();
214e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
215e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
216e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.right - SWIPE_MARGIN_LIMIT,
217e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerY(), rect.left + SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
218e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
219e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
220e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
221e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object. Also see
222e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollToBeginning(int)}, {@link #scrollToEnd(int)}, {@link #scrollBackward()},
223e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #scrollForward()}. This method will perform the swipe gesture over any
224e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * surface. The targeted UI element does not need to have the attribute
225e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <code>scrollable</code> set to <code>true</code> for this operation to be performed.
226e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param steps indicates the number of injected move steps into the system. More steps
227e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * injected the smoother the motion and slower.
228e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return
229e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
230e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
231e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeRight(int steps) throws UiObjectNotFoundException {
232e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getBounds();
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    /**
240e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * In rare situations, the node hierarchy returned from accessibility will
241e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * return items that are slightly OFF the screen (list view contents). This method
242e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * validate that the item is visible to avoid click operation failures. It will adjust
243e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * the center of the click as much as possible to be within visible bounds to make
244e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * the click successful.
245e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param node
246e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return the same AccessibilityNodeInfo passed in as argument
247e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
248e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private Rect getVisibleBounds(AccessibilityNodeInfo node) {
249e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (node == null) {
250e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return null;
251e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
252e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
253e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // targeted node's bounds
254e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect nodeRect = new Rect();
255e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        node.getBoundsInScreen(nodeRect);
256e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
257e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // is the targeted node within a scrollable container?
258e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo scrollableParentNode = getScrollableParent(node);
259e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(scrollableParentNode == null) {
260e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // nothing to adjust for so return the node's Rect as is
261e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return nodeRect;
262e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
263e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
264e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // Scrollable parent's visible bounds
265e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect parentRect = new Rect();
266e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        scrollableParentNode.getBoundsInScreen(parentRect);
267e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // adjust for partial clipping of targeted by parent node if required
268e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        nodeRect.intersect(parentRect);
269e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return nodeRect;
270e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
271e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
272e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
273e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Walk the hierarchy up to find a scrollable parent. A scrollable parent indicates that
274e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * this node may be in a content where it is partially visible due to scrolling. its
275e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * clickable center maybe invisible and adjustments should be made to the click coordinates.
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    /**
291e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Performs a tap over the UI element this object represents. </p>
292e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Take note that the UI element directly represented by this UiObject may not have
293e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * its attribute <code>clickable</code> set to <code>true</code> and yet still perform
294e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * the click successfully. This is because all clicks are performed in the center of the
295e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * targeted UI element and if this element is a child or a parent that wraps the clickable
296e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * element the operation will still succeed. This is the reason this operation does not
297e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * not validate the targeted UI element is clickable or not before operating.
298e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true id successful else false
299e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
300e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
301e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean click() throws UiObjectNotFoundException {
302e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
303e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
304e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
305e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
306e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
307e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().tap(rect.centerX(), rect.centerY());
308e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
309e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
310e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
311e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    *
312e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    * Performs a tap over the represented UI element, and expect window transition as a result.</p>
313e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    *
314e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    * This method is similar to the plain {@link UiObject#click()}, with an important difference
315e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    * that the method goes further to expect a window transition as a result of the tap. Some
316e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    * examples of a window transition:
317e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    * <li>launching a new activity</li>
318e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    * <li>bringing up a pop-up menu</li>
319e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    * <li>bringing up a dialog</li>
320e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    * This method is intended for reliably handling window transitions that would typically lasts
321e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    * longer than the usual preset timeouts.
322e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    *
323e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    * @return true if the event was triggered, else false
324e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    * @throws UiObjectNotFoundException
325e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    */
326e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickAndWaitForNewWindow() throws UiObjectNotFoundException {
327e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return clickAndWaitForNewWindow(WAIT_FOR_WINDOW_TMEOUT);
328e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
329e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
330e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
331e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
332e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Performs a tap over the represented UI element, and expect window transition as a result.</p>
333e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
334e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This method is similar to the plain {@link UiObject#click()}, with an important difference
335e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * that the method goes further to expect a window transition as a result of the tap. Some
336e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * examples of a window transition:
337e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>launching a new activity</li>
338e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>bringing up a pop-up menu</li>
339e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>bringing up a dialog</li>
340e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This method is intended for reliably handling window transitions that would typically lasts
341e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * longer than the usual preset timeouts.
342e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
343e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param timeout timeout before giving up on waiting for new window
344e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the event was triggered, else false
345e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
346e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
347e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickAndWaitForNewWindow(long timeout) throws UiObjectNotFoundException {
348e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
349e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
350e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
351e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
352e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
353e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().tapAndWaitForNewWindow(
354e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerX(), rect.centerY(), timeout);
355e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
356e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
357e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
358e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Click the top and left corner of the UI element.
359e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on success
360e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws Exception
361e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
362e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickTopLeft() throws UiObjectNotFoundException {
363e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
364e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
365e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
366e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
367e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
368e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().tap(rect.left + 5, rect.top + 5);
369e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
370e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
371e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
372e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Long clicks a UI element pointed to by the {@link By} selector of this object at the
373e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * bottom right corner. The duration of what will be considered as a long click is dynamically
374e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * retrieved from the system and used in the operation.
375e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
376e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
377e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
378e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClickBottomRight() throws UiObjectNotFoundException  {
379e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
380e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
381e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
382e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
383e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
384e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().longTap(rect.right - 5, rect.bottom - 5);
385e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
386e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
387e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
388e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Click the bottom and right corner of the UI element.
389e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on success
390e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws Exception
391e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
392e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickBottomRight() throws UiObjectNotFoundException {
393e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
394e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
395e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
396e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
397e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
398e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().tap(rect.right - 5, rect.bottom - 5);
399e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
400e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
401e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
402e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Long clicks a UI element pointed to by the {@link By} selector of this object. The
403e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * duration of what will be considered as a long click is dynamically retrieved from the
404e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * system and used in the operation.
405e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
406e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
407e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
408e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClick() throws UiObjectNotFoundException  {
409e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
410e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
411e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
412e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
413e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
414e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().longTap(rect.centerX(), rect.centerY());
415e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
416e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
417e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
418e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Long clicks a UI element pointed to by the {@link By} selector of this object at the
419e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * top left corner. The duration of what will be considered as a long click is dynamically
420e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * retrieved from the system and used in the operation.
421e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
422e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
423e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
424e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClickTopLeft() throws UiObjectNotFoundException {
425e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
426e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
427e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
428e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
429e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
430e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().longTap(rect.left + 5, rect.top + 5);
431e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
432e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
433e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
434e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This function can be used to return the UI element's displayed text. This applies to
435e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * UI element that are displaying labels or edit fields.
436e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return text value of the current node represented by this UiObject
437e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException if no match could be found
438e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
439e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getText() throws UiObjectNotFoundException {
440e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
441e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
442e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
443e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
444e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        String retVal = safeStringReturn(node.getText());
445e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Log.d(LOG_TAG, String.format("getText() = %s", retVal));
446e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return retVal;
447e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
448e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
449e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
450e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Retrieves the content-description value set for the UI element. In Accessibility, the
451e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * spoken text to speech is usually the <code>text</code> property of the UI element. If that
452e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * is not present, then the content-description is spoken. Many UI element such as buttons on
453e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * a toolbar may be too small to incorporate a visible text on their surfaces, so in such
454e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * cases, these UI elements must have their content-description fields populated to describe
455e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * them when accessibility is active.
456e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return value of node attribute "content_desc"
457e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
458e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
459e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getContentDescription() throws UiObjectNotFoundException {
460e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
461e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
462e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
463e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
464e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return safeStringReturn(node.getContentDescription());
465e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
466e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
467e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
468e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * First this function clears the existing text from the field. If this is not the intended
469e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * behavior, do a {@link #getText()} first, modify the text and then use this function.
470e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * The {@link By} selector of this object MUST be pointing directly at a UI element that
471e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * can accept edits. The way this method works is by first performing a {@link #click()}
472e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * on the edit field to set focus then it begins injecting the content of the text param
473e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * into the system. Since the targeted field is in focus, the text contents should be
474e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * inserted into the field.<p/>
475e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param text
476e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation is successful
477e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
478e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
479e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean setText(String text) throws UiObjectNotFoundException {
480e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        clearTextField();
481e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().sendText(text);
482e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
483e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
484e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
485e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * The object targeted must be an edit field capable of performing text insert. This
486e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * method sets focus at the left edge of the field and long presses to select
487e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * existing text. It will then follow that with delete press. Note: It is possible
488e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * that not all the text is selected especially if the text contained separators
489e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * such as spaces, slashes, at signs etc... The function will attempt to use the
490e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Select-All option if one is displayed to ensure full text selection.
491e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
492e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
493e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void clearTextField() throws UiObjectNotFoundException {
494e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // long click left + center
495e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
496e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
497e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
498e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
499e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
500e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getInteractionController().longTap(rect.left + 20, rect.centerY());
501e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // check if the edit menu is open
502e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        UiObject selectAll = new UiObject(By.selector().descriptionContains("Select all"));
503e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(selectAll.waitForExists(50))
504e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            selectAll.click();
505e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // wait for the selection
506e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        SystemClock.sleep(250);
507e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // delete it
508e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0);
509e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
510e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
511e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
512e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Check if item pointed to by this object's selector is currently checked. <p/>
513e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Take note that the {@link By} selector specified for this UiObjecy must be pointing
514e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * directly at the element you wish to query for this attribute as this UiObject may be
515e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * using {@link By} selectors that may be pointing at a parent element of the element
516e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * you're querying while still providing the desired functionality in terms of coordinates
517e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * for taps and swipes.
518e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
519e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
520e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isChecked() throws UiObjectNotFoundException {
521e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
522e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
523e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
524e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
525e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isChecked();
526e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
527e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
528e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
529e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Check if item pointed to by this object's selector is currently selected.<p/>
530e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Take note that the {@link By} selector specified for this UiObjecy must be pointing
531e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * directly at the element you wish to query for this attribute as this UiObject may be
532e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * using {@link By} selectors that may be pointing at a parent element of the element
533e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * you're querying while still providing the desired functionality in terms of coordinates
534e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * for taps and swipes.
535e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
536e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
537e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
538e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isSelected() throws UiObjectNotFoundException {
539e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
540e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
541e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
542e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
543e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isSelected();
544e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
545e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
546e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
547e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Check if item pointed to by this object's selector can be checked and unchecked. <p/>
548e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Take note that the {@link By} selector specified for this UiObjecy must be pointing
549e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * directly at the element you wish to query for this attribute as this UiObject may be
550e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * using {@link By} selectors that may be pointing at a parent element of the element
551e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * you're querying while still providing the desired functionality in terms of coordinates
552e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * for taps and swipes.
553e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
554e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
555e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
556e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isCheckable() throws UiObjectNotFoundException {
557e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
558e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
559e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
560e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
561e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isCheckable();
562e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
563e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
564e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
565e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Check if item pointed to by this object's selector with text is currently
566e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * not grayed out. <p/>
567e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Take note that the {@link By} selector specified for this UiObjecy must be pointing
568e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * directly at the element you wish to query for this attribute as this UiObject may be
569e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * using {@link By} selectors that may be pointing at a parent element of the element
570e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * you're querying while still providing the desired functionality in terms of coordinates
571e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * for taps and swipes.
572e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
573e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
574e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
575e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isEnabled() throws UiObjectNotFoundException {
576e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
577e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
578e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
579e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
580e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isEnabled();
581e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
582e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
583e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
584e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Check if item pointed to by this object's selector with text responds to
585e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * clicks <p/>
586e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Take note that the {@link By} selector specified for this UiObjecy must be pointing
587e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * directly at the element you wish to query for this attribute as this UiObject may be
588e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * using {@link By} selectors that may be pointing at a parent element of the element
589e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * you're querying while still providing the desired functionality in terms of coordinates
590e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * for taps and swipes.
591e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
592e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
593e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
594e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isClickable() throws UiObjectNotFoundException {
595e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
596e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
597e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
598e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
599e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isClickable();
600e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
601e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
602e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
603e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Check if item pointed to by this object's selector with text is currently
604e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * focused. Focused objects will receive key stroke events when key events
605e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * are fired
606e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
607e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
608e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
609e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isFocused() 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.isFocused();
615e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
616e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
617e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
618e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Check if item pointed to by this object's selector with text is capable of receiving
619e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * focus. <p/>
620e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Take note that the {@link By} selector specified for this UiObjecy must be pointing
621e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * directly at the element you wish to query for this attribute as this UiObject may be
622e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * using {@link By} selectors that may be pointing at a parent element of the element
623e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * you're querying while still providing the desired functionality in terms of coordinates
624e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * for taps and swipes.
625e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
626e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
627e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
628e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isFocusable() throws UiObjectNotFoundException {
629e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
630e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
631e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
632e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
633e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isFocusable();
634e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
635e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
636e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
637e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Check if item pointed to by this object's selector with text can be scrolled.<p/>
638e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Take note that the {@link By} selector specified for this UiObjecy must be pointing
639e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * directly at the element you wish to query for this attribute as this UiObject may be
640e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * using {@link By} selectors that may be pointing at a parent element of the element
641e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * you're querying while still providing the desired functionality in terms of coordinates
642e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * for taps and swipes.
643e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
644e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
645e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
646e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isScrollable() throws UiObjectNotFoundException {
647e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
648e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
649e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
650e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
651e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isScrollable();
652e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
653e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
654e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
655e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Check if item pointed to by this object's selector responds to long clicks.<p/>
656e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Take note that the {@link By} selector specified for this UiObjecy must be pointing
657e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * directly at the element you wish to query for this attribute as this UiObject may be
658e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * using {@link By} selectors that may be pointing at a parent element of the element
659e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * you're querying while still providing the desired functionality in terms of coordinates
660e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * for taps and swipes.
661e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
662e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
663e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
664e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isLongClickable() throws UiObjectNotFoundException {
665e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
666e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
667e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
668e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
669e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isLongClickable();
670e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
671e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
672e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
673e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This method retrieves the package name of the currently displayed content on the screen.
674e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This can be helpful when verifying that the expected package is on the screen before
675e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * proceeding with further test operations.
676e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return String package name
677e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
678e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
679e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getPackageName() throws UiObjectNotFoundException {
680e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
681e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
682e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
683e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
684e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return safeStringReturn(node.getPackageName());
685e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
686e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
687e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
688e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Reports the absolute visible screen bounds of the object. If a portion of the UI element
689e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * is visible, only the bounds of the visible portion of the UI element are reported. This
690e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * becomes important when using bounds to calculate exact coordinates for tapping the element.
691e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return Rect
692e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
693e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
694e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public Rect getBounds() throws UiObjectNotFoundException {
695e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
696e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
697e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
698e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
699e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getVisibleBounds(node);
700e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
701e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
702e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
703e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This method will wait for a UI element to become visible on the display. It
704e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * can be used for situations where the content to be selected is not yet displayed
705e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * and the time it will be present is unknown.
706e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param timeout
707e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the UI element exists else false for timeout while waiting
708e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
709e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean waitForExists(long timeout) {
710e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(findAccessibilityNodeInfo(timeout) != null) {
711e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return true;
712e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
713e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
714e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
715e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
716e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
717e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Helper to wait for a specified object to no longer be detectable. This can be
718e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * useful when having to wait for a progress dialog to finish.
719e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param timeout
720e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if gone before timeout else false for still present at timeout
721e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
722e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean waitUntilGone(long timeout) {
723e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        long startMills = SystemClock.uptimeMillis();
724e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        long currentMills = 0;
725e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        while (currentMills <= timeout) {
726e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(findAccessibilityNodeInfo(0) == null)
727e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return true;
728e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            currentMills = SystemClock.uptimeMillis() - startMills;
729e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(timeout > 0)
730e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
731e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
732e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
733e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
734e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
735e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
736e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This methods performs a {@link #waitForExists(long)} with zero timeout. This
737e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * basically returns immediately whether the UI element represented by this UiObject
738e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * exists or not. If you need to wait longer for this UI element, then see
739e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #waitForExists(long)}.
740e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the UI element represented by this UiObject does exist
741e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
742e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean exists() {
743e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return waitForExists(0);
744e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
745e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
746e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private String safeStringReturn(CharSequence cs) {
747e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(cs == null)
748e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return "";
749e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return cs.toString();
750e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
751e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu}
752