UiObject.java revision c344be11dbfd56e95d076f46b870108fdaa662f0
1e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu/*
2e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Copyright (C) 2012 The Android Open Source Project
3e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
4e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Licensed under the Apache License, Version 2.0 (the "License");
5e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * you may not use this file except in compliance with the License.
6e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * You may obtain a copy of the License at
7e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
8e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *      http://www.apache.org/licenses/LICENSE-2.0
9e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
10e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Unless required by applicable law or agreed to in writing, software
11e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * distributed under the License is distributed on an "AS IS" BASIS,
12e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * See the License for the specific language governing permissions and
14e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * limitations under the License.
15e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
16e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
17e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupackage com.android.uiautomator.core;
18e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
19c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtazimport android.graphics.Point;
20e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.graphics.Rect;
21e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.os.SystemClock;
22e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.util.Log;
23e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.KeyEvent;
24c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtazimport android.view.MotionEvent.PointerCoords;
256088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtazimport android.view.accessibility.AccessibilityEvent;
26e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.accessibility.AccessibilityNodeInfo;
27e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
28e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu/**
2946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * A UiObject is a representation of a UI element. It is not in any way directly bound to a
3046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * UI element as an object reference. A UiObject holds information to help it
3146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * locate a matching UI element at runtime based on the {@link UiSelector} properties specified in
3246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * its constructor. Since a UiObject is a representative for a UI element, it can
3346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz * be reused for different views with matching UI elements.
34dbba713661688a285e701a006ce2d199296ac328Guang Zhu * @since API Level 16
35e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
36e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupublic class UiObject {
37e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final String LOG_TAG = UiObject.class.getSimpleName();
38dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
39dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
40dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
41e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_SELECTOR_TIMEOUT = 10 * 1000;
42dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
43dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
44dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
45e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_SELECTOR_POLL = 1000;
46e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // set a default timeout to 5.5s, since ANR threshold is 5s
47dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
48dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
49dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
50e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected static final long WAIT_FOR_WINDOW_TMEOUT = 5500;
51dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
52c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @since API Level 16
53c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     **/
54c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    protected static final int SWIPE_MARGIN_LIMIT = 5;
55c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    /**
56dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 17
57dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
586088c8f9e34e34a4958b1601ed7c1bb34c95da21Adam Momtaz    protected static final long WAIT_FOR_EVENT_TMEOUT = 3 * 1000;
59dbba713661688a285e701a006ce2d199296ac328Guang Zhu    /**
60c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @since API Level 18
61dbba713661688a285e701a006ce2d199296ac328Guang Zhu     **/
62c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    protected static final int FINGER_TOUCH_HALF_WIDTH = 20;
63e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
647f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz    private final UiSelector mSelector;
65ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    private final UiAutomatorBridge mUiAutomationBridge;
66e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
67e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
683d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Constructs a UiObject to represent a specific UI element matched by the specified
693d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * {@link UiSelector} selector properties.
70e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param selector
71dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
72e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
734ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject(UiSelector selector) {
74e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mUiAutomationBridge = UiDevice.getInstance().getAutomatorBridge();
75e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mSelector = selector;
76e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
77e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
78e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
793d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Debugging helper. A test can dump the properties of a selector as a string
803d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * to its logs if needed. <code>getSelector().toString();</code>
813d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
824ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @return {@link UiSelector}
83dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
84e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
854ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public final UiSelector getSelector() {
86f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
874ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiSelector(mSelector);
88e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
89e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
90e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
913d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Retrieves the {@link QueryController} to translate a {@link UiSelector} selector
923d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * into an {@link AccessibilityNodeInfo}.
933d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
94e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link QueryController}
95e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
96ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    QueryController getQueryController() {
97e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getQueryController();
98e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
99e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
100e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1013d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Retrieves the {@link InteractionController} to perform finger actions such as tapping,
1023d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * swiping or entering text.
1033d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
104e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link InteractionController}
105e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
106ddc1008f06fd2a875037026490ce1f848a442572Guang Zhu    InteractionController getInteractionController() {
107e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mUiAutomationBridge.getInteractionController();
108e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
109e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
110e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1113d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Creates a new UiObject representing a child UI element of the element currently represented
1123d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * by this UiObject.
1133d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1143d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param selector for UI element to match
1153d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return a new UiObject representing the matched UI element
116dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
117e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
1184ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException {
119f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(selector);
1204ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiObject(getSelector().childSelector(selector));
121e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
122e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
123e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1243d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Creates a new UiObject representing a child UI element from the parent element currently
1253d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * represented by this object. Essentially this is starting the search from the parent
1263d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * element and can also be used to find sibling UI elements to the one currently represented
1273d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * by this UiObject.
1283d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1293d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param selector for the UI element to match
1303d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return a new UiObject representing the matched UI element
131e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
132dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
133e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
1344ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException {
135f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(selector);
1364ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return new UiObject(getSelector().fromParent(selector));
137e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
138e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
139e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1403d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Counts the child UI elements immediately under the UI element currently represented by
141e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * this UiObject.
1423d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
143e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return the count of child UI elements.
144e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
145dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
146e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
147e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public int getChildCount() throws UiObjectNotFoundException {
148f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
149e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
150e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
151e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
152e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
153e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.getChildCount();
154e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
155e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
156e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1573d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Uses the member UiSelector properties to find a matching UI element reported in
1583d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * the accessibility hierarchy.
1593d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
160e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param timeout in milliseconds
161e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return AccessibilityNodeInfo if found else null
162dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
163e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
164e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout) {
165e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = null;
166e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(UiDevice.getInstance().isInWatcherContext()) {
167e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // we will NOT run watchers or do any sort of polling if the
168e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // reason we're here is because of a watcher is executing. Watchers
169e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // will not have other watchers run for them so they should not block
170e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // while they poll for items to become present. We disable polling for them.
171e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            node = getQueryController().findAccessibilityNodeInfo(getSelector());
172e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } else {
173e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            long startMills = SystemClock.uptimeMillis();
174e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            long currentMills = 0;
175e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            while (currentMills <= timeout) {
176e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                node = getQueryController().findAccessibilityNodeInfo(getSelector());
177e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if (node != null) {
178e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    break;
179e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                } else {
180e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    UiDevice.getInstance().runWatchers();
181e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
182e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                currentMills = SystemClock.uptimeMillis() - startMills;
183e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if(timeout > 0) {
184e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
185e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
186e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
187e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
188e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node;
189e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
190e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
191e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1923d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Perform the action on the UI element that is represented by this UiObject. Also see
1931893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollToBeginning(int)}, {@link UiScrollable#scrollToEnd(int)},
1941893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollBackward()}, {@link UiScrollable#scrollForward()}.
1953d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
196467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
197467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
1983d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true of successful
199e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
200dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
201e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
202e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeUp(int steps) throws UiObjectNotFoundException {
203f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(steps);
2043d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
205e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
206e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
207e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.centerX(),
208e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.bottom - SWIPE_MARGIN_LIMIT, rect.centerX(), rect.top + SWIPE_MARGIN_LIMIT,
209e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                steps);
210e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
211e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
212e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
213e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object, Also see
2141893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollToBeginning(int)}, {@link UiScrollable#scrollToEnd(int)},
2151893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollBackward()}, {@link UiScrollable#scrollForward()}. This method will
2161893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * perform the swipe gesture over any surface.  The targeted UI element does not need to have
2171893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * the attribute <code>scrollable</code> set to <code>true</code> for this operation to be
2181893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * performed.
2193d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2203d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
221467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
2223d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if successful
223e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
224dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
225e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
226e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeDown(int steps) throws UiObjectNotFoundException {
227f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(steps);
2283d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
229e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
230e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
231e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.centerX(),
232e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.top + SWIPE_MARGIN_LIMIT, rect.centerX(),
233e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.bottom - SWIPE_MARGIN_LIMIT, steps);
234e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
235e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
236e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
237e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object. Also see
2381893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollToBeginning(int)}, {@link UiScrollable#scrollToEnd(int)},
2391893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollBackward()}, {@link UiScrollable#scrollForward()}. This method will
2401893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * perform the swipe gesture over any surface. The targeted UI element does not need to have the
2411893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * attribute <code>scrollable</code> set to <code>true</code> for this operation to be
2421893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * performed.
2433d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2443d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
245467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
2463d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if successful
247e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
248dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
249e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
250e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeLeft(int steps) throws UiObjectNotFoundException {
251f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(steps);
2523d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
253e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
254e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
255e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.right - SWIPE_MARGIN_LIMIT,
256e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerY(), rect.left + SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
257e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
258e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
259e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
260e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform the action on the UI element that is represented by this object. Also see
2611893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollToBeginning(int)}, {@link UiScrollable#scrollToEnd(int)},
2621893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link UiScrollable#scrollBackward()}, {@link UiScrollable#scrollForward()}. This method will
2631893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * perform the swipe gesture over any surface. The targeted UI element does not need to have the
2641893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * attribute <code>scrollable</code> set to <code>true</code> for this operation to be
2651893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * performed.
2663d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2673d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
268467cca7d25dbbf13c14cfd8c2ad38ab8eaf56bdaAdam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
2693d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if successful
270e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
271dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
272e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
273e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean swipeRight(int steps) throws UiObjectNotFoundException {
274f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(steps);
2753d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect rect = getVisibleBounds();
276e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
277e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return false; // too small to swipe
278e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().swipe(rect.left + SWIPE_MARGIN_LIMIT,
279e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerY(), rect.right - SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
280e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
281e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
282e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2833d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Finds the visible bounds of a partially visible UI element
2843d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
285e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param node
2867f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * @return null if node is null, else a Rect containing visible bounds
287e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
288e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private Rect getVisibleBounds(AccessibilityNodeInfo node) {
289e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (node == null) {
290e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return null;
291e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
292e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
293e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // targeted node's bounds
2947f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz        Rect nodeRect = AccessibilityNodeInfoHelper.getVisibleBoundsInScreen(node);
295e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
296e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // is the targeted node within a scrollable container?
297e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo scrollableParentNode = getScrollableParent(node);
298e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(scrollableParentNode == null) {
299e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // nothing to adjust for so return the node's Rect as is
300e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return nodeRect;
301e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
302e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
303e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // Scrollable parent's visible bounds
3047f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz        Rect parentRect = AccessibilityNodeInfoHelper
3057f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz                .getVisibleBoundsInScreen(scrollableParentNode);
306e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // adjust for partial clipping of targeted by parent node if required
307e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        nodeRect.intersect(parentRect);
308e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return nodeRect;
309e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
310e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
311e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3127f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * Walk the hierarchy up to find a scrollable parent. A scrollable parent
3137f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * indicates that this node may be in a content where it is partially
3147f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * visible due to scrolling. its clickable center maybe invisible and
3157f38ef08d07295967b905a61b2356ff6cdf31159Adam Momtaz     * adjustments should be made to the click coordinates.
3163d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
317e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param node
3181893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * @return The accessibility node info.
319e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
320e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private AccessibilityNodeInfo getScrollableParent(AccessibilityNodeInfo node) {
321e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo parent = node;
322e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        while(parent != null) {
323e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            parent = parent.getParent();
324e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (parent != null && parent.isScrollable()) {
325e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return parent;
326e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
327e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
328e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return null;
329e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
330e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
331e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3323d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Performs a click at the center of the visible bounds of the UI element represented
33346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * by this UiObject.
3343d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
335e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true id successful else false
336e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
337dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
338e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
339e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean click() throws UiObjectNotFoundException {
340f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
341e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
342e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
343e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
344e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
345e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
34608f500a92397522c48bcdad58d37ec1be95956d1Adam Momtaz        return getInteractionController().clickAndWaitForEvents(rect.centerX(), rect.centerY(),
347c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz                WAIT_FOR_EVENT_TMEOUT, false, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED |
34808f500a92397522c48bcdad58d37ec1be95956d1Adam Momtaz                AccessibilityEvent.TYPE_VIEW_SELECTED);
349e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
350e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
351e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3523d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link #clickAndWaitForNewWindow(long)}
353a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * This method is intended to reliably wait for window transitions that would typically take
354a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * longer than the usual default timeouts.
3553d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
3563d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if the event was triggered, else false
3573d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @throws UiObjectNotFoundException
358dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
3593d50587be8ff021369c90554d814839335b445b0Adam Momtaz     */
360e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickAndWaitForNewWindow() throws UiObjectNotFoundException {
361f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
362e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return clickAndWaitForNewWindow(WAIT_FOR_WINDOW_TMEOUT);
363e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
364e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
365e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
36646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Performs a click at the center of the visible bounds of the UI element represented
36746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * by this UiObject and waits for window transitions.
36846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
3693d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * This method differ from {@link UiObject#click()} only in that this method waits for a
37046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * a new window transition as a result of the click. Some examples of a window transition:
371e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>launching a new activity</li>
372e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>bringing up a pop-up menu</li>
373e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <li>bringing up a dialog</li>
374e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
3753d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param timeout timeout before giving up on waiting for a new window
376e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the event was triggered, else false
377e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
378dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
379e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
380e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickAndWaitForNewWindow(long timeout) throws UiObjectNotFoundException {
381f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(timeout);
382e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
383e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
384e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
385e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
386e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
3874ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getInteractionController().clickAndWaitForNewWindow(
388e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                rect.centerX(), rect.centerY(), timeout);
389e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
390e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
391e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3923d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Clicks the top and left corner of the UI element
3933d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
394e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on success
3951893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * @throws UiObjectNotFoundException
396dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
397e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
398e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickTopLeft() throws UiObjectNotFoundException {
399f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
400e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
401e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
402e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
403e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
404e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
4054ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getInteractionController().click(rect.left + 5, rect.top + 5);
406e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
407e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
408e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4093d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks bottom and right corner of the UI element
4103d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
411e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
412e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
413dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
414e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
415e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClickBottomRight() throws UiObjectNotFoundException  {
416f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
417e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
418e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
419e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
420e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
421e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
422e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().longTap(rect.right - 5, rect.bottom - 5);
423e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
424e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
425e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4263d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Clicks the bottom and right corner of the UI element
4273d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
428e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on success
4291893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * @throws UiObjectNotFoundException
430dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
431e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
432e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean clickBottomRight() throws UiObjectNotFoundException {
433f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
434e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
435e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
436e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
437e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
438e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
4394ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return getInteractionController().click(rect.right - 5, rect.bottom - 5);
440e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
441e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
442e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4433d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks the center of the visible bounds of the UI element
4443d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
445e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
446e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
447dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
448e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
449e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClick() throws UiObjectNotFoundException  {
450f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
451e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
452e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
453e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
454e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
455e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
456e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().longTap(rect.centerX(), rect.centerY());
457e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
458e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
459e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4603d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Long clicks on the top and left corner of the UI element
4613d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
462e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation was successful
463e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
464dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
465e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
466e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean longClickTopLeft() throws UiObjectNotFoundException {
467f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
468e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
469e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
470e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
471e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
472e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
473e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().longTap(rect.left + 5, rect.top + 5);
474e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
475e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
476e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4773d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the <code>text</code> property of the UI element
4783d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
479e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return text value of the current node represented by this UiObject
480e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException if no match could be found
481dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
482e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
483e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getText() throws UiObjectNotFoundException {
484f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
485e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
486e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
487e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
488e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
489e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        String retVal = safeStringReturn(node.getText());
490e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Log.d(LOG_TAG, String.format("getText() = %s", retVal));
491e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return retVal;
492e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
493e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
494e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4953d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the <code>content_desc</code> property of the UI element
4963d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
497e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return value of node attribute "content_desc"
498e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
499dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
500e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
501e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getContentDescription() throws UiObjectNotFoundException {
502f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
503e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
504e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
505e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
506e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
507e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return safeStringReturn(node.getContentDescription());
508e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
509e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
510e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
51146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Sets the text in an editable field, after clearing the field's content.
51246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
51346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * The {@link UiSelector} selector of this object must reference a UI element that is editable.
51446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
51546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * When you call this method, the method first simulates a {@link #click()} on
51646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * editable field to set focus. The method then clears the field's contents
51746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * and injects your specified text into the field.
5183d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
51946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If you want to capture the original contents of the field, call {@link #getText()} first.
52046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * You can then modify the text and use this method to update the field.
52146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
52246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param text string to set
523e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if operation is successful
524e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
525dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
526e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
527e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean setText(String text) throws UiObjectNotFoundException {
528f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(text);
529e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        clearTextField();
530e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().sendText(text);
531e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
532e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
533e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
53446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Clears the existing text contents in an editable field.
53546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
53646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * The {@link UiSelector} of this object must reference a UI element that is editable.
53746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
53846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * When you call this method, the method first sets focus at the start edge of the field.
53946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * The method then simulates a long-press to select the existing text, and deletes the
54046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * selected text.
54146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
54246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If a "Select-All" option is displayed, the method will automatically attempt to use it
54346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * to ensure full text selection.
54446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
54546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Note that it is possible that not all the text in the field is selected; for example,
54646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * if the text contains separators such as spaces, slashes, at symbol etc.
54746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Also, not all editable fields support the long-press functionality.
54846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
549e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
550dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
551e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
552e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public void clearTextField() throws UiObjectNotFoundException {
553f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
554e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // long click left + center
555e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
556e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
557e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
558e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
559e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = getVisibleBounds(node);
560e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getInteractionController().longTap(rect.left + 20, rect.centerY());
561e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // check if the edit menu is open
5624ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        UiObject selectAll = new UiObject(new UiSelector().descriptionContains("Select all"));
563e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(selectAll.waitForExists(50))
564e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            selectAll.click();
565e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // wait for the selection
566e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        SystemClock.sleep(250);
567e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // delete it
568e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0);
569e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
570e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
571e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5723d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>checked</code> property is currently true
5733d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
574e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
575dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
576e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
577e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isChecked() throws UiObjectNotFoundException {
578f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
579e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
580e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
581e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
582e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
583e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isChecked();
584e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
585e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
586e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5873d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>selected</code> property is currently true
5883d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
589e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
590e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
591dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
592e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
593e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isSelected() throws UiObjectNotFoundException {
594f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
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.isSelected();
600e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
601e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
602e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6033d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>checkable</code> property is currently true
6043d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
605e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
606e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
607dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
608e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
609e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isCheckable() throws UiObjectNotFoundException {
610f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
611e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
612e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
613e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
614e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
615e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isCheckable();
616e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
617e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
618e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6193d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>enabled</code> property is currently true
6203d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
621e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
622e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
623dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
624e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
625e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isEnabled() throws UiObjectNotFoundException {
626f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
627e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
628e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
629e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
630e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
631e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isEnabled();
632e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
633e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
634e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6353d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>clickable</code> property is currently true
6363d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
637e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
638e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
639dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
640e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
641e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isClickable() throws UiObjectNotFoundException {
642f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
643e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
644e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
645e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
646e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
647e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isClickable();
648e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
649e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
650e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6513d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>focused</code> property is currently true
6523d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
653e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
654e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
655dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
656e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
657e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isFocused() throws UiObjectNotFoundException {
658f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
659e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
660e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
661e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
662e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
663e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isFocused();
664e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
665e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
666e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6673d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>focusable</code> property is currently true
6683d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
669e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
670e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
671dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
672e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
673e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isFocusable() throws UiObjectNotFoundException {
674f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
675e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
676e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
677e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
678e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
679e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isFocusable();
680e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
681e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
682e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6833d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>scrollable</code> property is currently true
6843d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
685e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
686e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
687dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
688e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
689e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isScrollable() throws UiObjectNotFoundException {
690f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
691e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
692e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
693e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
694e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
695e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isScrollable();
696e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
697e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
698e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
6993d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Check if the UI element's <code>long-clickable</code> property is currently true
7003d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
701e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if it is else false
702e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
703dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
704e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
705e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean isLongClickable() throws UiObjectNotFoundException {
706f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
707e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
708e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
709e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
710e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
711e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return node.isLongClickable();
712e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
713e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
714e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
7153d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Reads the UI element's <code>package</code> property
7163d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
7173d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return true if it is else false
718e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
719dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
720e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
721e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public String getPackageName() throws UiObjectNotFoundException {
722f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
723e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
724e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
725e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
726e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
727e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return safeStringReturn(node.getPackageName());
728e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
729e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
730e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
73146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Returns the visible bounds of the UI element.
73246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
73346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * If a portion of the UI element is visible, only the bounds of the visible portion are
73446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * reported.
7353d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
736e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return Rect
737e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
7381893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * @see {@link #getBounds()}
73979693ede92636fe6f3a6ec4dc049a438fd9504ffGuang Zhu     * @since API Level 17
740e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
7413d50587be8ff021369c90554d814839335b445b0Adam Momtaz    public Rect getVisibleBounds() throws UiObjectNotFoundException {
742f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
743e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
744e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
745e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
746e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
747e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getVisibleBounds(node);
748e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
749e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
750e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
75146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Returns the UI element's <code>bounds</code> property. See {@link #getVisibleBounds()}
7523d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
7533d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @return Rect
7543d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @throws UiObjectNotFoundException
755dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
7563d50587be8ff021369c90554d814839335b445b0Adam Momtaz     */
7573d50587be8ff021369c90554d814839335b445b0Adam Momtaz    public Rect getBounds() throws UiObjectNotFoundException {
758f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
7593d50587be8ff021369c90554d814839335b445b0Adam Momtaz        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
7603d50587be8ff021369c90554d814839335b445b0Adam Momtaz        if(node == null) {
7613d50587be8ff021369c90554d814839335b445b0Adam Momtaz            throw new UiObjectNotFoundException(getSelector().toString());
7623d50587be8ff021369c90554d814839335b445b0Adam Momtaz        }
7633d50587be8ff021369c90554d814839335b445b0Adam Momtaz        Rect nodeRect = new Rect();
7643d50587be8ff021369c90554d814839335b445b0Adam Momtaz        node.getBoundsInScreen(nodeRect);
7653d50587be8ff021369c90554d814839335b445b0Adam Momtaz
7663d50587be8ff021369c90554d814839335b445b0Adam Momtaz        return nodeRect;
7673d50587be8ff021369c90554d814839335b445b0Adam Momtaz    }
7683d50587be8ff021369c90554d814839335b445b0Adam Momtaz
7693d50587be8ff021369c90554d814839335b445b0Adam Momtaz    /**
77046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Waits a specified length of time for a UI element to become visible.
77146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
77246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * This method waits until the UI element becomes visible on the display, or
77346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * until the timeout has elapsed. You can use this method in situations where
77446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * the content that you want to select is not immediately displayed.
7753d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
77646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param timeout the amount of time to wait (in milliseconds)
77746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if the UI element is displayed, else false if timeout elapsed while waiting
778dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
779e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
780e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean waitForExists(long timeout) {
781f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(timeout);
782e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(findAccessibilityNodeInfo(timeout) != null) {
783e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return true;
784e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
785e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
786e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
787e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
788e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
78946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Waits a specified length of time for a UI element to become undetectable.
7903d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
79146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * This method waits until a UI element is no longer matchable, or until the
79246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * timeout has elapsed.
79346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
79446d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * A UI element becomes undetectable when the {@link UiSelector} of the object is
79546d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * unable to find a match because the element has either changed its state or is no
79646d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * longer displayed.
79746d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
79846d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * You can use this method when attempting to wait for some long operation
79946d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * to compete, such as downloading a large file or connecting to a remote server.
80046d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
80146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @param timeout time to wait (in milliseconds)
80246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * @return true if the element is gone before timeout elapsed, else false if timeout elapsed
80346d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * but a matching element is still found.
804dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
805e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
806e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean waitUntilGone(long timeout) {
807f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(timeout);
808e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        long startMills = SystemClock.uptimeMillis();
809e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        long currentMills = 0;
810e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        while (currentMills <= timeout) {
811e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(findAccessibilityNodeInfo(0) == null)
812e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return true;
813e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            currentMills = SystemClock.uptimeMillis() - startMills;
814e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(timeout > 0)
815e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
816e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
817e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
818e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
819e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
820e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
82146d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     * Check if UI element exists.
82246d9444c7a39dc1c9fc60a5dcf4e79749d9b3859Adam Momtaz     *
823e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * This methods performs a {@link #waitForExists(long)} with zero timeout. This
824e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * basically returns immediately whether the UI element represented by this UiObject
825e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * exists or not. If you need to wait longer for this UI element, then see
826e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #waitForExists(long)}.
8273d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
828e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the UI element represented by this UiObject does exist
829dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
830e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
831e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public boolean exists() {
832f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
833e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return waitForExists(0);
834e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
835e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
836e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private String safeStringReturn(CharSequence cs) {
837e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(cs == null)
838e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return "";
839e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return cs.toString();
840e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
841c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
842c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    /**
843c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * PinchOut generates a 2 pointer gesture where each pointer is moving from the center out
844c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * away from each other diagonally towards the edges of the current UI element represented by
845c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * this UiObject.
846c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param percent of the object's diagonal length to use for the pinch
847c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
848c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
849c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @throws UiObjectNotFoundException
850c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @since API Level 18
851c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     */
852c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    public void pinchOut(int percent, int steps) throws UiObjectNotFoundException {
853c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // make value between 1 and 100
854c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        percent = (percent < 0) ? 1 : (percent > 100) ? 100 : percent;
855c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        float percentage = percent / 100f;
856c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
857c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
858c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        if (node == null) {
859c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            throw new UiObjectNotFoundException(getSelector().toString());
860c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        }
861c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
862c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Rect rect = getVisibleBounds(node);
863c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        if (rect.width() <= FINGER_TOUCH_HALF_WIDTH * 2)
864c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            throw new IllegalStateException("Object width is too small for operation");
865c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
866c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // start from the same point at the center of the control
867c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point startPoint1 = new Point(rect.centerX() - FINGER_TOUCH_HALF_WIDTH, rect.centerY());
868c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point startPoint2 = new Point(rect.centerX() + FINGER_TOUCH_HALF_WIDTH, rect.centerY());
869c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
870c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // End at the top-left and bottom-right corners of the control
871c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point endPoint1 = new Point(rect.centerX() - (int)((rect.width()/2) * percentage),
872c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz                rect.centerY());
873c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point endPoint2 = new Point(rect.centerX() + (int)((rect.width()/2) * percentage),
874c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz                rect.centerY());
875c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
876c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        twoPointerGesture(startPoint1, startPoint2, endPoint1, endPoint2, steps);
877c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    }
878c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
879c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    /**
880c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * PinchIn generates a 2 pointer gesture where each pointer is moving towards the other
881c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * diagonally from the edges of the current UI element represented by this UiObject, until the
882c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * center.
883c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param percent of the object's diagonal length to use for the pinch
884c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
885c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
886c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @throws UiObjectNotFoundException
887c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @since API Level 18
888c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     */
889c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    public void pinchIn(int percent, int steps) throws UiObjectNotFoundException {
890c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // make value between 1 and 100
891c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        percent = (percent < 0) ? 0 : (percent > 100) ? 100 : percent;
892c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        float percentage = percent / 100f;
893c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
894c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
895c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        if (node == null) {
896c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            throw new UiObjectNotFoundException(getSelector().toString());
897c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        }
898c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
899c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Rect rect = getVisibleBounds(node);
900c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        if (rect.width() <= FINGER_TOUCH_HALF_WIDTH * 2)
901c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            throw new IllegalStateException("Object width is too small for operation");
902c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
903c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point startPoint1 = new Point(rect.centerX() - (int)((rect.width()/2) * percentage),
904c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz                rect.centerY());
905c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point startPoint2 = new Point(rect.centerX() + (int)((rect.width()/2) * percentage),
906c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz                rect.centerY());
907c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
908c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point endPoint1 = new Point(rect.centerX() - FINGER_TOUCH_HALF_WIDTH, rect.centerY());
909c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        Point endPoint2 = new Point(rect.centerX() + FINGER_TOUCH_HALF_WIDTH, rect.centerY());
910c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
911c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        twoPointerGesture(startPoint1, startPoint2, endPoint1, endPoint2, steps);
912c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    }
913c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
914c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    /**
915c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * Generates a 2 pointer gesture from an arbitrary starting and ending points.
916c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *
917c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param startPoint1 start point of pointer 1
918c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param startPoint2 start point of pointer 2
919c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param endPoint1 end point of pointer 1
920c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param endPoint2 end point of pointer 2
921c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param steps indicates the number of injected move steps into the system. Steps are
922c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
923c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @since API Level 18
924c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     */
925c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    public void twoPointerGesture(Point startPoint1, Point startPoint2, Point endPoint1,
926c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            Point endPoint2, int steps) {
927c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
928c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // avoid a divide by zero
929c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        if(steps == 0)
930c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            steps = 1;
931c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
932c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        final float stepX1 = (endPoint1.x - startPoint1.x) / steps;
933c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        final float stepY1 = (endPoint1.y - startPoint1.y) / steps;
934c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        final float stepX2 = (endPoint2.x - startPoint2.x) / steps;
935c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        final float stepY2 = (endPoint2.y - startPoint2.y) / steps;
936c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
937c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        int eventX1, eventY1, eventX2, eventY2;
938c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        eventX1 = startPoint1.x;
939c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        eventY1 = startPoint1.y;
940c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        eventX2 = startPoint2.x;
941c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        eventY2 = startPoint2.y;
942c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
943c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // allocate for steps plus first down and last up
944c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        PointerCoords[] points1 = new PointerCoords[steps + 2];
945c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        PointerCoords[] points2 = new PointerCoords[steps + 2];
946c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
947c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // Include the first and last touch downs in the arrays of steps
948c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        for (int i = 0; i < steps + 1; i++) {
949c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            PointerCoords p1 = new PointerCoords();
950c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p1.x = eventX1;
951c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p1.y = eventY1;
952c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p1.pressure = 1;
953c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p1.size = 1;
954c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            points1[i] = p1;
955c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
956c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            PointerCoords p2 = new PointerCoords();
957c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p2.x = eventX2;
958c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p2.y = eventY2;
959c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p2.pressure = 1;
960c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            p2.size = 1;
961c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            points2[i] = p2;
962c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
963c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            eventX1 += stepX1;
964c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            eventY1 += stepY1;
965c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            eventX2 += stepX2;
966c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz            eventY2 += stepY2;
967c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        }
968c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
969c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        // ending pointers coordinates
970c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        PointerCoords p1 = new PointerCoords();
971c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p1.x = endPoint1.x;
972c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p1.y = endPoint1.y;
973c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p1.pressure = 1;
974c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p1.size = 1;
975c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        points1[steps + 1] = p1;
976c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
977c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        PointerCoords p2 = new PointerCoords();
978c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p2.x = endPoint2.x;
979c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p2.y = endPoint2.y;
980c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p2.pressure = 1;
981c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        p2.size = 1;
982c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        points2[steps + 1] = p2;
983c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
984c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        multiPointerGesture(points1, points2);
985c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    }
986c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz
987c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    /**
988c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * Performs a multi-touch gesture
989c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *
990c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * Takes a series of touch coordinates for at least 2 pointers. Each pointer must have
991c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * all of its touch steps defined in an array of {@link PointerCoords}. By having the ability
992c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * to specify the touch points along the path of a pointer, the caller is able to specify
993c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * complex gestures like circles, irregular shapes etc, where each pointer may take a
994c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * different path.
995c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *
996c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * To create a single point on a pointer's touch path
997c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * <code>
998c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *       PointerCoords p = new PointerCoords();
999c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *       p.x = stepX;
1000c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *       p.y = stepY;
1001c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *       p.pressure = 1;
1002c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *       p.size = 1;
1003c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * </code>
1004c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @param touches each array of {@link PointerCoords} constitute a single pointer's touch path.
1005c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *        Multiple {@link PointerCoords} arrays constitute multiple pointers, each with its own
1006c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     *        path. Each {@link PointerCoords} in an array constitute a point on a pointer's path.
1007c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     * @since API Level 18
1008c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz     */
1009c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    public void multiPointerGesture(PointerCoords[] ...touches) {
1010c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz        getInteractionController().generateMultiPointerGesture(touches);
1011c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz    }
1012c344be11dbfd56e95d076f46b870108fdaa662f0Adam Momtaz}