Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.uiautomator.core;
     18 
     19 import android.graphics.Point;
     20 import android.graphics.Rect;
     21 import android.os.SystemClock;
     22 import android.util.Log;
     23 import android.view.KeyEvent;
     24 import android.view.MotionEvent.PointerCoords;
     25 import android.view.accessibility.AccessibilityNodeInfo;
     26 
     27 /**
     28  * A UiObject is a representation of a view. It is not in any way directly bound to a
     29  * view as an object reference. A UiObject contains information to help it
     30  * locate a matching view at runtime based on the {@link UiSelector} properties specified in
     31  * its constructor. Once you create an instance of a UiObject, it can
     32  * be reused for different views that match the selector criteria.
     33  * @since API Level 16
     34  * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the
     35  * Android Testing Support Library.
     36  */
     37 @Deprecated
     38 public class UiObject {
     39     private static final String LOG_TAG = UiObject.class.getSimpleName();
     40     /**
     41      * @since API Level 16
     42      * @deprecated use {@link Configurator#setWaitForSelectorTimeout(long)}
     43      **/
     44     @Deprecated
     45     protected static final long WAIT_FOR_SELECTOR_TIMEOUT = 10 * 1000;
     46     /**
     47      * @since API Level 16
     48      **/
     49     protected static final long WAIT_FOR_SELECTOR_POLL = 1000;
     50     // set a default timeout to 5.5s, since ANR threshold is 5s
     51     /**
     52      * @since API Level 16
     53      **/
     54     protected static final long WAIT_FOR_WINDOW_TMEOUT = 5500;
     55     /**
     56      * @since API Level 16
     57      **/
     58     protected static final int SWIPE_MARGIN_LIMIT = 5;
     59     /**
     60      * @since API Level 17
     61      * @deprecated use {@link Configurator#setScrollAcknowledgmentTimeout(long)}
     62      **/
     63     @Deprecated
     64     protected static final long WAIT_FOR_EVENT_TMEOUT = 3 * 1000;
     65     /**
     66      * @since API Level 18
     67      **/
     68     protected static final int FINGER_TOUCH_HALF_WIDTH = 20;
     69 
     70     private final UiSelector mSelector;
     71 
     72     private final Configurator mConfig = Configurator.getInstance();
     73 
     74     /**
     75      * Constructs a UiObject to represent a view that matches the specified
     76      * selector criteria.
     77      * @param selector
     78      * @since API Level 16
     79      */
     80     public UiObject(UiSelector selector) {
     81         mSelector = selector;
     82     }
     83 
     84     /**
     85      * Debugging helper. A test can dump the properties of a selector as a string
     86      * to its logs if needed. <code>getSelector().toString();</code>
     87      *
     88      * @return {@link UiSelector}
     89      * @since API Level 16
     90      */
     91     public final UiSelector getSelector() {
     92         Tracer.trace();
     93         return new UiSelector(mSelector);
     94     }
     95 
     96     /**
     97      * Retrieves the {@link QueryController} to translate a {@link UiSelector} selector
     98      * into an {@link AccessibilityNodeInfo}.
     99      *
    100      * @return {@link QueryController}
    101      */
    102     QueryController getQueryController() {
    103         return UiDevice.getInstance().getAutomatorBridge().getQueryController();
    104     }
    105 
    106     /**
    107      * Retrieves the {@link InteractionController} to perform finger actions such as tapping,
    108      * swiping, or entering text.
    109      *
    110      * @return {@link InteractionController}
    111      */
    112     InteractionController getInteractionController() {
    113         return UiDevice.getInstance().getAutomatorBridge().getInteractionController();
    114     }
    115 
    116     /**
    117      * Creates a new UiObject for a child view that is under the present UiObject.
    118      *
    119      * @param selector for child view to match
    120      * @return a new UiObject representing the child view
    121      * @since API Level 16
    122      */
    123     public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException {
    124         Tracer.trace(selector);
    125         return new UiObject(getSelector().childSelector(selector));
    126     }
    127 
    128     /**
    129      * Creates a new UiObject for a sibling view or a child of the sibling view,
    130      * relative to the present UiObject.
    131      *
    132      * @param selector for a sibling view or children of the sibling view
    133      * @return a new UiObject representing the matched view
    134      * @throws UiObjectNotFoundException
    135      * @since API Level 16
    136      */
    137     public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException {
    138         Tracer.trace(selector);
    139         return new UiObject(getSelector().fromParent(selector));
    140     }
    141 
    142     /**
    143      * Counts the child views immediately under the present UiObject.
    144      *
    145      * @return the count of child views.
    146      * @throws UiObjectNotFoundException
    147      * @since API Level 16
    148      */
    149     public int getChildCount() throws UiObjectNotFoundException {
    150         Tracer.trace();
    151         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    152         if(node == null) {
    153             throw new UiObjectNotFoundException(getSelector().toString());
    154         }
    155         return node.getChildCount();
    156     }
    157 
    158     /**
    159      * Finds a matching UI element in the accessibility hierarchy, by
    160      * using the selector for this UiObject.
    161      *
    162      * @param timeout in milliseconds
    163      * @return AccessibilityNodeInfo if found else null
    164      * @since API Level 16
    165      */
    166     protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout) {
    167         AccessibilityNodeInfo node = null;
    168         long startMills = SystemClock.uptimeMillis();
    169         long currentMills = 0;
    170         while (currentMills <= timeout) {
    171             node = getQueryController().findAccessibilityNodeInfo(getSelector());
    172             if (node != null) {
    173                 break;
    174             } else {
    175                 // does nothing if we're reentering another runWatchers()
    176                 UiDevice.getInstance().runWatchers();
    177             }
    178             currentMills = SystemClock.uptimeMillis() - startMills;
    179             if(timeout > 0) {
    180                 SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
    181             }
    182         }
    183         return node;
    184     }
    185 
    186     /**
    187      * Drags this object to a destination UiObject.
    188      * The number of steps specified in your input parameter can influence the
    189      * drag speed, and varying speeds may impact the results. Consider
    190      * evaluating different speeds when using this method in your tests.
    191      *
    192      * @param destObj the destination UiObject.
    193      * @param steps usually 40 steps. You can increase or decrease the steps to change the speed.
    194      * @return true if successful
    195      * @throws UiObjectNotFoundException
    196      * @since API Level 18
    197      */
    198     public boolean dragTo(UiObject destObj, int steps) throws UiObjectNotFoundException {
    199         Rect srcRect = getVisibleBounds();
    200         Rect dstRect = destObj.getVisibleBounds();
    201         return getInteractionController().swipe(srcRect.centerX(), srcRect.centerY(),
    202                 dstRect.centerX(), dstRect.centerY(), steps, true);
    203     }
    204 
    205     /**
    206      * Drags this object to arbitrary coordinates.
    207      * The number of steps specified in your input parameter can influence the
    208      * drag speed, and varying speeds may impact the results. Consider
    209      * evaluating different speeds when using this method in your tests.
    210      *
    211      * @param destX the X-axis coordinate.
    212      * @param destY the Y-axis coordinate.
    213      * @param steps usually 40 steps. You can increase or decrease the steps to change the speed.
    214      * @return true if successful
    215      * @throws UiObjectNotFoundException
    216      * @since API Level 18
    217      */
    218     public boolean dragTo(int destX, int destY, int steps) throws UiObjectNotFoundException {
    219         Rect srcRect = getVisibleBounds();
    220         return getInteractionController().swipe(srcRect.centerX(), srcRect.centerY(), destX, destY,
    221                 steps, true);
    222     }
    223 
    224     /**
    225      * Performs the swipe up action on the UiObject.
    226      * See also:
    227      * <ul>
    228      * <li>{@link UiScrollable#scrollToBeginning(int)}</li>
    229      * <li>{@link UiScrollable#scrollToEnd(int)}</li>
    230      * <li>{@link UiScrollable#scrollBackward()}</li>
    231      * <li>{@link UiScrollable#scrollForward()}</li>
    232      * </ul>
    233      *
    234      * @param steps indicates the number of injected move steps into the system. Steps are
    235      * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
    236      * @return true of successful
    237      * @throws UiObjectNotFoundException
    238      * @since API Level 16
    239      */
    240     public boolean swipeUp(int steps) throws UiObjectNotFoundException {
    241         Tracer.trace(steps);
    242         Rect rect = getVisibleBounds();
    243         if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
    244             return false; // too small to swipe
    245         return getInteractionController().swipe(rect.centerX(),
    246                 rect.bottom - SWIPE_MARGIN_LIMIT, rect.centerX(), rect.top + SWIPE_MARGIN_LIMIT,
    247                 steps);
    248     }
    249 
    250     /**
    251      * Performs the swipe down action on the UiObject.
    252      * The swipe gesture can be performed over any surface. The targeted
    253      * UI element does not need to be scrollable.
    254      * See also:
    255      * <ul>
    256      * <li>{@link UiScrollable#scrollToBeginning(int)}</li>
    257      * <li>{@link UiScrollable#scrollToEnd(int)}</li>
    258      * <li>{@link UiScrollable#scrollBackward()}</li>
    259      * <li>{@link UiScrollable#scrollForward()}</li>
    260      * </ul>
    261      *
    262      * @param steps indicates the number of injected move steps into the system. Steps are
    263      * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
    264      * @return true if successful
    265      * @throws UiObjectNotFoundException
    266      * @since API Level 16
    267      */
    268     public boolean swipeDown(int steps) throws UiObjectNotFoundException {
    269         Tracer.trace(steps);
    270         Rect rect = getVisibleBounds();
    271         if(rect.height() <= SWIPE_MARGIN_LIMIT * 2)
    272             return false; // too small to swipe
    273         return getInteractionController().swipe(rect.centerX(),
    274                 rect.top + SWIPE_MARGIN_LIMIT, rect.centerX(),
    275                 rect.bottom - SWIPE_MARGIN_LIMIT, steps);
    276     }
    277 
    278     /**
    279      * Performs the swipe left action on the UiObject.
    280      * The swipe gesture can be performed over any surface. The targeted
    281      * UI element does not need to be scrollable.
    282      * See also:
    283      * <ul>
    284      * <li>{@link UiScrollable#scrollToBeginning(int)}</li>
    285      * <li>{@link UiScrollable#scrollToEnd(int)}</li>
    286      * <li>{@link UiScrollable#scrollBackward()}</li>
    287      * <li>{@link UiScrollable#scrollForward()}</li>
    288      * </ul>
    289      *
    290      * @param steps indicates the number of injected move steps into the system. Steps are
    291      * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
    292      * @return true if successful
    293      * @throws UiObjectNotFoundException
    294      * @since API Level 16
    295      */
    296     public boolean swipeLeft(int steps) throws UiObjectNotFoundException {
    297         Tracer.trace(steps);
    298         Rect rect = getVisibleBounds();
    299         if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
    300             return false; // too small to swipe
    301         return getInteractionController().swipe(rect.right - SWIPE_MARGIN_LIMIT,
    302                 rect.centerY(), rect.left + SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
    303     }
    304 
    305     /**
    306      * Performs the swipe right action on the UiObject.
    307      * The swipe gesture can be performed over any surface. The targeted
    308      * UI element does not need to be scrollable.
    309      * See also:
    310      * <ul>
    311      * <li>{@link UiScrollable#scrollToBeginning(int)}</li>
    312      * <li>{@link UiScrollable#scrollToEnd(int)}</li>
    313      * <li>{@link UiScrollable#scrollBackward()}</li>
    314      * <li>{@link UiScrollable#scrollForward()}</li>
    315      * </ul>
    316      *
    317      * @param steps indicates the number of injected move steps into the system. Steps are
    318      * injected about 5ms apart. So a 100 steps may take about 1/2 second to complete.
    319      * @return true if successful
    320      * @throws UiObjectNotFoundException
    321      * @since API Level 16
    322      */
    323     public boolean swipeRight(int steps) throws UiObjectNotFoundException {
    324         Tracer.trace(steps);
    325         Rect rect = getVisibleBounds();
    326         if(rect.width() <= SWIPE_MARGIN_LIMIT * 2)
    327             return false; // too small to swipe
    328         return getInteractionController().swipe(rect.left + SWIPE_MARGIN_LIMIT,
    329                 rect.centerY(), rect.right - SWIPE_MARGIN_LIMIT, rect.centerY(), steps);
    330     }
    331 
    332     /**
    333      * Finds the visible bounds of a partially visible UI element
    334      *
    335      * @param node
    336      * @return null if node is null, else a Rect containing visible bounds
    337      */
    338     private Rect getVisibleBounds(AccessibilityNodeInfo node) {
    339         if (node == null) {
    340             return null;
    341         }
    342 
    343         // targeted node's bounds
    344         int w = UiDevice.getInstance().getDisplayWidth();
    345         int h = UiDevice.getInstance().getDisplayHeight();
    346         Rect nodeRect = AccessibilityNodeInfoHelper.getVisibleBoundsInScreen(node, w, h);
    347 
    348         // is the targeted node within a scrollable container?
    349         AccessibilityNodeInfo scrollableParentNode = getScrollableParent(node);
    350         if(scrollableParentNode == null) {
    351             // nothing to adjust for so return the node's Rect as is
    352             return nodeRect;
    353         }
    354 
    355         // Scrollable parent's visible bounds
    356         Rect parentRect = AccessibilityNodeInfoHelper
    357                 .getVisibleBoundsInScreen(scrollableParentNode, w, h);
    358         // adjust for partial clipping of targeted by parent node if required
    359         if (nodeRect.intersect(parentRect)) {
    360             return nodeRect;
    361         } else {
    362             // Node rect has no intersection with parent Rect
    363             return new Rect();
    364         }
    365     }
    366 
    367     /**
    368      * Walks up the layout hierarchy to find a scrollable parent. A scrollable parent
    369      * indicates that this node might be in a container where it is partially
    370      * visible due to scrolling. In this case, its clickable center might not be visible and
    371      * the click coordinates should be adjusted.
    372      *
    373      * @param node
    374      * @return The accessibility node info.
    375      */
    376     private AccessibilityNodeInfo getScrollableParent(AccessibilityNodeInfo node) {
    377         AccessibilityNodeInfo parent = node;
    378         while(parent != null) {
    379             parent = parent.getParent();
    380             if (parent != null && parent.isScrollable()) {
    381                 return parent;
    382             }
    383         }
    384         return null;
    385     }
    386 
    387     /**
    388      * Performs a click at the center of the visible bounds of the UI element represented
    389      * by this UiObject.
    390      *
    391      * @return true id successful else false
    392      * @throws UiObjectNotFoundException
    393      * @since API Level 16
    394      */
    395     public boolean click() throws UiObjectNotFoundException {
    396         Tracer.trace();
    397         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    398         if(node == null) {
    399             throw new UiObjectNotFoundException(getSelector().toString());
    400         }
    401         Rect rect = getVisibleBounds(node);
    402         return getInteractionController().clickAndSync(rect.centerX(), rect.centerY(),
    403                 mConfig.getActionAcknowledgmentTimeout());
    404     }
    405 
    406     /**
    407      * Waits for window transitions that would typically take longer than the
    408      * usual default timeouts.
    409      * See {@link #clickAndWaitForNewWindow(long)}
    410      *
    411      * @return true if the event was triggered, else false
    412      * @throws UiObjectNotFoundException
    413      * @since API Level 16
    414      */
    415     public boolean clickAndWaitForNewWindow() throws UiObjectNotFoundException {
    416         Tracer.trace();
    417         return clickAndWaitForNewWindow(WAIT_FOR_WINDOW_TMEOUT);
    418     }
    419 
    420     /**
    421      * Performs a click at the center of the visible bounds of the UI element represented
    422      * by this UiObject and waits for window transitions.
    423      *
    424      * This method differ from {@link UiObject#click()} only in that this method waits for a
    425      * a new window transition as a result of the click. Some examples of a window transition:
    426      * <li>launching a new activity</li>
    427      * <li>bringing up a pop-up menu</li>
    428      * <li>bringing up a dialog</li>
    429      *
    430      * @param timeout timeout before giving up on waiting for a new window
    431      * @return true if the event was triggered, else false
    432      * @throws UiObjectNotFoundException
    433      * @since API Level 16
    434      */
    435     public boolean clickAndWaitForNewWindow(long timeout) throws UiObjectNotFoundException {
    436         Tracer.trace(timeout);
    437         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    438         if(node == null) {
    439             throw new UiObjectNotFoundException(getSelector().toString());
    440         }
    441         Rect rect = getVisibleBounds(node);
    442         return getInteractionController().clickAndWaitForNewWindow(rect.centerX(), rect.centerY(),
    443                 mConfig.getActionAcknowledgmentTimeout());
    444     }
    445 
    446     /**
    447      * Clicks the top and left corner of the UI element
    448      *
    449      * @return true on success
    450      * @throws UiObjectNotFoundException
    451      * @since API Level 16
    452      */
    453     public boolean clickTopLeft() throws UiObjectNotFoundException {
    454         Tracer.trace();
    455         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    456         if(node == null) {
    457             throw new UiObjectNotFoundException(getSelector().toString());
    458         }
    459         Rect rect = getVisibleBounds(node);
    460         return getInteractionController().clickNoSync(rect.left + 5, rect.top + 5);
    461     }
    462 
    463     /**
    464      * Long clicks bottom and right corner of the UI element
    465      *
    466      * @return true if operation was successful
    467      * @throws UiObjectNotFoundException
    468      * @since API Level 16
    469      */
    470     public boolean longClickBottomRight() throws UiObjectNotFoundException  {
    471         Tracer.trace();
    472         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    473         if(node == null) {
    474             throw new UiObjectNotFoundException(getSelector().toString());
    475         }
    476         Rect rect = getVisibleBounds(node);
    477         return getInteractionController().longTapNoSync(rect.right - 5, rect.bottom - 5);
    478     }
    479 
    480     /**
    481      * Clicks the bottom and right corner of the UI element
    482      *
    483      * @return true on success
    484      * @throws UiObjectNotFoundException
    485      * @since API Level 16
    486      */
    487     public boolean clickBottomRight() throws UiObjectNotFoundException {
    488         Tracer.trace();
    489         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    490         if(node == null) {
    491             throw new UiObjectNotFoundException(getSelector().toString());
    492         }
    493         Rect rect = getVisibleBounds(node);
    494         return getInteractionController().clickNoSync(rect.right - 5, rect.bottom - 5);
    495     }
    496 
    497     /**
    498      * Long clicks the center of the visible bounds of the UI element
    499      *
    500      * @return true if operation was successful
    501      * @throws UiObjectNotFoundException
    502      * @since API Level 16
    503      */
    504     public boolean longClick() throws UiObjectNotFoundException  {
    505         Tracer.trace();
    506         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    507         if(node == null) {
    508             throw new UiObjectNotFoundException(getSelector().toString());
    509         }
    510         Rect rect = getVisibleBounds(node);
    511         return getInteractionController().longTapNoSync(rect.centerX(), rect.centerY());
    512     }
    513 
    514     /**
    515      * Long clicks on the top and left corner of the UI element
    516      *
    517      * @return true if operation was successful
    518      * @throws UiObjectNotFoundException
    519      * @since API Level 16
    520      */
    521     public boolean longClickTopLeft() throws UiObjectNotFoundException {
    522         Tracer.trace();
    523         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    524         if(node == null) {
    525             throw new UiObjectNotFoundException(getSelector().toString());
    526         }
    527         Rect rect = getVisibleBounds(node);
    528         return getInteractionController().longTapNoSync(rect.left + 5, rect.top + 5);
    529     }
    530 
    531     /**
    532      * Reads the <code>text</code> property of the UI element
    533      *
    534      * @return text value of the current node represented by this UiObject
    535      * @throws UiObjectNotFoundException if no match could be found
    536      * @since API Level 16
    537      */
    538     public String getText() throws UiObjectNotFoundException {
    539         Tracer.trace();
    540         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    541         if(node == null) {
    542             throw new UiObjectNotFoundException(getSelector().toString());
    543         }
    544         String retVal = safeStringReturn(node.getText());
    545         Log.d(LOG_TAG, String.format("getText() = %s", retVal));
    546         return retVal;
    547     }
    548 
    549     /**
    550      * Retrieves the <code>className</code> property of the UI element.
    551      *
    552      * @return class name of the current node represented by this UiObject
    553      * @throws UiObjectNotFoundException if no match was found
    554      * @since API Level 18
    555      */
    556     public String getClassName() throws UiObjectNotFoundException {
    557         Tracer.trace();
    558         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    559         if(node == null) {
    560             throw new UiObjectNotFoundException(getSelector().toString());
    561         }
    562         String retVal = safeStringReturn(node.getClassName());
    563         Log.d(LOG_TAG, String.format("getClassName() = %s", retVal));
    564         return retVal;
    565     }
    566 
    567     /**
    568      * Reads the <code>content_desc</code> property of the UI element
    569      *
    570      * @return value of node attribute "content_desc"
    571      * @throws UiObjectNotFoundException
    572      * @since API Level 16
    573      */
    574     public String getContentDescription() throws UiObjectNotFoundException {
    575         Tracer.trace();
    576         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    577         if(node == null) {
    578             throw new UiObjectNotFoundException(getSelector().toString());
    579         }
    580         return safeStringReturn(node.getContentDescription());
    581     }
    582 
    583     /**
    584      * Sets the text in an editable field, after clearing the field's content.
    585      *
    586      * The {@link UiSelector} selector of this object must reference a UI element that is editable.
    587      *
    588      * When you call this method, the method first simulates a {@link #click()} on
    589      * editable field to set focus. The method then clears the field's contents
    590      * and injects your specified text into the field.
    591      *
    592      * If you want to capture the original contents of the field, call {@link #getText()} first.
    593      * You can then modify the text and use this method to update the field.
    594      *
    595      * @param text string to set
    596      * @return true if operation is successful
    597      * @throws UiObjectNotFoundException
    598      * @since API Level 16
    599      */
    600     public boolean setText(String text) throws UiObjectNotFoundException {
    601         Tracer.trace(text);
    602         clearTextField();
    603         return getInteractionController().sendText(text);
    604     }
    605 
    606     /**
    607      * Clears the existing text contents in an editable field.
    608      *
    609      * The {@link UiSelector} of this object must reference a UI element that is editable.
    610      *
    611      * When you call this method, the method first sets focus at the start edge of the field.
    612      * The method then simulates a long-press to select the existing text, and deletes the
    613      * selected text.
    614      *
    615      * If a "Select-All" option is displayed, the method will automatically attempt to use it
    616      * to ensure full text selection.
    617      *
    618      * Note that it is possible that not all the text in the field is selected; for example,
    619      * if the text contains separators such as spaces, slashes, at symbol etc.
    620      * Also, not all editable fields support the long-press functionality.
    621      *
    622      * @throws UiObjectNotFoundException
    623      * @since API Level 16
    624      */
    625     public void clearTextField() throws UiObjectNotFoundException {
    626         Tracer.trace();
    627         // long click left + center
    628         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    629         if(node == null) {
    630             throw new UiObjectNotFoundException(getSelector().toString());
    631         }
    632         Rect rect = getVisibleBounds(node);
    633         getInteractionController().longTapNoSync(rect.left + 20, rect.centerY());
    634         // check if the edit menu is open
    635         UiObject selectAll = new UiObject(new UiSelector().descriptionContains("Select all"));
    636         if(selectAll.waitForExists(50))
    637             selectAll.click();
    638         // wait for the selection
    639         SystemClock.sleep(250);
    640         // delete it
    641         getInteractionController().sendKey(KeyEvent.KEYCODE_DEL, 0);
    642     }
    643 
    644     /**
    645      * Check if the UI element's <code>checked</code> property is currently true
    646      *
    647      * @return true if it is else false
    648      * @since API Level 16
    649      */
    650     public boolean isChecked() throws UiObjectNotFoundException {
    651         Tracer.trace();
    652         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    653         if(node == null) {
    654             throw new UiObjectNotFoundException(getSelector().toString());
    655         }
    656         return node.isChecked();
    657     }
    658 
    659     /**
    660      * Checks if the UI element's <code>selected</code> property is currently true.
    661      *
    662      * @return true if it is else false
    663      * @throws UiObjectNotFoundException
    664      * @since API Level 16
    665      */
    666     public boolean isSelected() throws UiObjectNotFoundException {
    667         Tracer.trace();
    668         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    669         if(node == null) {
    670             throw new UiObjectNotFoundException(getSelector().toString());
    671         }
    672         return node.isSelected();
    673     }
    674 
    675     /**
    676      * Checks if the UI element's <code>checkable</code> property is currently true.
    677      *
    678      * @return true if it is else false
    679      * @throws UiObjectNotFoundException
    680      * @since API Level 16
    681      */
    682     public boolean isCheckable() throws UiObjectNotFoundException {
    683         Tracer.trace();
    684         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    685         if(node == null) {
    686             throw new UiObjectNotFoundException(getSelector().toString());
    687         }
    688         return node.isCheckable();
    689     }
    690 
    691     /**
    692      * Checks if the UI element's <code>enabled</code> property is currently true.
    693      *
    694      * @return true if it is else false
    695      * @throws UiObjectNotFoundException
    696      * @since API Level 16
    697      */
    698     public boolean isEnabled() throws UiObjectNotFoundException {
    699         Tracer.trace();
    700         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    701         if(node == null) {
    702             throw new UiObjectNotFoundException(getSelector().toString());
    703         }
    704         return node.isEnabled();
    705     }
    706 
    707     /**
    708      * Checks if the UI element's <code>clickable</code> property is currently true.
    709      *
    710      * @return true if it is else false
    711      * @throws UiObjectNotFoundException
    712      * @since API Level 16
    713      */
    714     public boolean isClickable() throws UiObjectNotFoundException {
    715         Tracer.trace();
    716         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    717         if(node == null) {
    718             throw new UiObjectNotFoundException(getSelector().toString());
    719         }
    720         return node.isClickable();
    721     }
    722 
    723     /**
    724      * Check if the UI element's <code>focused</code> property is currently true
    725      *
    726      * @return true if it is else false
    727      * @throws UiObjectNotFoundException
    728      * @since API Level 16
    729      */
    730     public boolean isFocused() throws UiObjectNotFoundException {
    731         Tracer.trace();
    732         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    733         if(node == null) {
    734             throw new UiObjectNotFoundException(getSelector().toString());
    735         }
    736         return node.isFocused();
    737     }
    738 
    739     /**
    740      * Check if the UI element's <code>focusable</code> property is currently true.
    741      *
    742      * @return true if it is else false
    743      * @throws UiObjectNotFoundException
    744      * @since API Level 16
    745      */
    746     public boolean isFocusable() throws UiObjectNotFoundException {
    747         Tracer.trace();
    748         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    749         if(node == null) {
    750             throw new UiObjectNotFoundException(getSelector().toString());
    751         }
    752         return node.isFocusable();
    753     }
    754 
    755     /**
    756      * Check if the view's <code>scrollable</code> property is currently true
    757      *
    758      * @return true if it is else false
    759      * @throws UiObjectNotFoundException
    760      * @since API Level 16
    761      */
    762     public boolean isScrollable() throws UiObjectNotFoundException {
    763         Tracer.trace();
    764         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    765         if(node == null) {
    766             throw new UiObjectNotFoundException(getSelector().toString());
    767         }
    768         return node.isScrollable();
    769     }
    770 
    771     /**
    772      * Check if the view's <code>long-clickable</code> property is currently true
    773      *
    774      * @return true if it is else false
    775      * @throws UiObjectNotFoundException
    776      * @since API Level 16
    777      */
    778     public boolean isLongClickable() throws UiObjectNotFoundException {
    779         Tracer.trace();
    780         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    781         if(node == null) {
    782             throw new UiObjectNotFoundException(getSelector().toString());
    783         }
    784         return node.isLongClickable();
    785     }
    786 
    787     /**
    788      * Reads the view's <code>package</code> property
    789      *
    790      * @return true if it is else false
    791      * @throws UiObjectNotFoundException
    792      * @since API Level 16
    793      */
    794     public String getPackageName() throws UiObjectNotFoundException {
    795         Tracer.trace();
    796         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    797         if(node == null) {
    798             throw new UiObjectNotFoundException(getSelector().toString());
    799         }
    800         return safeStringReturn(node.getPackageName());
    801     }
    802 
    803     /**
    804      * Returns the visible bounds of the view.
    805      *
    806      * If a portion of the view is visible, only the bounds of the visible portion are
    807      * reported.
    808      *
    809      * @return Rect
    810      * @throws UiObjectNotFoundException
    811      * @see #getBounds()
    812      * @since API Level 17
    813      */
    814     public Rect getVisibleBounds() throws UiObjectNotFoundException {
    815         Tracer.trace();
    816         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    817         if(node == null) {
    818             throw new UiObjectNotFoundException(getSelector().toString());
    819         }
    820         return getVisibleBounds(node);
    821     }
    822 
    823     /**
    824      * Returns the view's <code>bounds</code> property. See {@link #getVisibleBounds()}
    825      *
    826      * @return Rect
    827      * @throws UiObjectNotFoundException
    828      * @since API Level 16
    829      */
    830     public Rect getBounds() throws UiObjectNotFoundException {
    831         Tracer.trace();
    832         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    833         if(node == null) {
    834             throw new UiObjectNotFoundException(getSelector().toString());
    835         }
    836         Rect nodeRect = new Rect();
    837         node.getBoundsInScreen(nodeRect);
    838 
    839         return nodeRect;
    840     }
    841 
    842     /**
    843      * Waits a specified length of time for a view to become visible.
    844      *
    845      * This method waits until the view becomes visible on the display, or
    846      * until the timeout has elapsed. You can use this method in situations where
    847      * the content that you want to select is not immediately displayed.
    848      *
    849      * @param timeout the amount of time to wait (in milliseconds)
    850      * @return true if the view is displayed, else false if timeout elapsed while waiting
    851      * @since API Level 16
    852      */
    853     public boolean waitForExists(long timeout) {
    854         Tracer.trace(timeout);
    855         if(findAccessibilityNodeInfo(timeout) != null) {
    856             return true;
    857         }
    858         return false;
    859     }
    860 
    861     /**
    862      * Waits a specified length of time for a view to become undetectable.
    863      *
    864      * This method waits until a view is no longer matchable, or until the
    865      * timeout has elapsed.
    866      *
    867      * A view becomes undetectable when the {@link UiSelector} of the object is
    868      * unable to find a match because the element has either changed its state or is no
    869      * longer displayed.
    870      *
    871      * You can use this method when attempting to wait for some long operation
    872      * to compete, such as downloading a large file or connecting to a remote server.
    873      *
    874      * @param timeout time to wait (in milliseconds)
    875      * @return true if the element is gone before timeout elapsed, else false if timeout elapsed
    876      * but a matching element is still found.
    877      * @since API Level 16
    878      */
    879     public boolean waitUntilGone(long timeout) {
    880         Tracer.trace(timeout);
    881         long startMills = SystemClock.uptimeMillis();
    882         long currentMills = 0;
    883         while (currentMills <= timeout) {
    884             if(findAccessibilityNodeInfo(0) == null)
    885                 return true;
    886             currentMills = SystemClock.uptimeMillis() - startMills;
    887             if(timeout > 0)
    888                 SystemClock.sleep(WAIT_FOR_SELECTOR_POLL);
    889         }
    890         return false;
    891     }
    892 
    893     /**
    894      * Check if view exists.
    895      *
    896      * This methods performs a {@link #waitForExists(long)} with zero timeout. This
    897      * basically returns immediately whether the view represented by this UiObject
    898      * exists or not. If you need to wait longer for this view, then see
    899      * {@link #waitForExists(long)}.
    900      *
    901      * @return true if the view represented by this UiObject does exist
    902      * @since API Level 16
    903      */
    904     public boolean exists() {
    905         Tracer.trace();
    906         return waitForExists(0);
    907     }
    908 
    909     private String safeStringReturn(CharSequence cs) {
    910         if(cs == null)
    911             return "";
    912         return cs.toString();
    913     }
    914 
    915     /**
    916      * Performs a two-pointer gesture, where each pointer moves diagonally
    917      * opposite across the other, from the center out towards the edges of the
    918      * this UiObject.
    919      * @param percent percentage of the object's diagonal length for the pinch gesture
    920      * @param steps the number of steps for the gesture. Steps are injected
    921      * about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
    922      * @return <code>true</code> if all touch events for this gesture are injected successfully,
    923      *         <code>false</code> otherwise
    924      * @throws UiObjectNotFoundException
    925      * @since API Level 18
    926      */
    927     public boolean pinchOut(int percent, int steps) throws UiObjectNotFoundException {
    928         // make value between 1 and 100
    929         percent = (percent < 0) ? 1 : (percent > 100) ? 100 : percent;
    930         float percentage = percent / 100f;
    931 
    932         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    933         if (node == null) {
    934             throw new UiObjectNotFoundException(getSelector().toString());
    935         }
    936 
    937         Rect rect = getVisibleBounds(node);
    938         if (rect.width() <= FINGER_TOUCH_HALF_WIDTH * 2)
    939             throw new IllegalStateException("Object width is too small for operation");
    940 
    941         // start from the same point at the center of the control
    942         Point startPoint1 = new Point(rect.centerX() - FINGER_TOUCH_HALF_WIDTH, rect.centerY());
    943         Point startPoint2 = new Point(rect.centerX() + FINGER_TOUCH_HALF_WIDTH, rect.centerY());
    944 
    945         // End at the top-left and bottom-right corners of the control
    946         Point endPoint1 = new Point(rect.centerX() - (int)((rect.width()/2) * percentage),
    947                 rect.centerY());
    948         Point endPoint2 = new Point(rect.centerX() + (int)((rect.width()/2) * percentage),
    949                 rect.centerY());
    950 
    951         return performTwoPointerGesture(startPoint1, startPoint2, endPoint1, endPoint2, steps);
    952     }
    953 
    954     /**
    955      * Performs a two-pointer gesture, where each pointer moves diagonally
    956      * toward the other, from the edges to the center of this UiObject .
    957      * @param percent percentage of the object's diagonal length for the pinch gesture
    958      * @param steps the number of steps for the gesture. Steps are injected
    959      * about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
    960      * @return <code>true</code> if all touch events for this gesture are injected successfully,
    961      *         <code>false</code> otherwise
    962      * @throws UiObjectNotFoundException
    963      * @since API Level 18
    964      */
    965     public boolean pinchIn(int percent, int steps) throws UiObjectNotFoundException {
    966         // make value between 1 and 100
    967         percent = (percent < 0) ? 0 : (percent > 100) ? 100 : percent;
    968         float percentage = percent / 100f;
    969 
    970         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
    971         if (node == null) {
    972             throw new UiObjectNotFoundException(getSelector().toString());
    973         }
    974 
    975         Rect rect = getVisibleBounds(node);
    976         if (rect.width() <= FINGER_TOUCH_HALF_WIDTH * 2)
    977             throw new IllegalStateException("Object width is too small for operation");
    978 
    979         Point startPoint1 = new Point(rect.centerX() - (int)((rect.width()/2) * percentage),
    980                 rect.centerY());
    981         Point startPoint2 = new Point(rect.centerX() + (int)((rect.width()/2) * percentage),
    982                 rect.centerY());
    983 
    984         Point endPoint1 = new Point(rect.centerX() - FINGER_TOUCH_HALF_WIDTH, rect.centerY());
    985         Point endPoint2 = new Point(rect.centerX() + FINGER_TOUCH_HALF_WIDTH, rect.centerY());
    986 
    987         return performTwoPointerGesture(startPoint1, startPoint2, endPoint1, endPoint2, steps);
    988     }
    989 
    990     /**
    991      * Generates a two-pointer gesture with arbitrary starting and ending points.
    992      *
    993      * @param startPoint1 start point of pointer 1
    994      * @param startPoint2 start point of pointer 2
    995      * @param endPoint1 end point of pointer 1
    996      * @param endPoint2 end point of pointer 2
    997      * @param steps the number of steps for the gesture. Steps are injected
    998      * about 5 milliseconds apart, so 100 steps may take around 0.5 seconds to complete.
    999      * @return <code>true</code> if all touch events for this gesture are injected successfully,
   1000      *         <code>false</code> otherwise
   1001      * @since API Level 18
   1002      */
   1003     public boolean performTwoPointerGesture(Point startPoint1, Point startPoint2, Point endPoint1,
   1004             Point endPoint2, int steps) {
   1005 
   1006         // avoid a divide by zero
   1007         if(steps == 0)
   1008             steps = 1;
   1009 
   1010         final float stepX1 = (endPoint1.x - startPoint1.x) / steps;
   1011         final float stepY1 = (endPoint1.y - startPoint1.y) / steps;
   1012         final float stepX2 = (endPoint2.x - startPoint2.x) / steps;
   1013         final float stepY2 = (endPoint2.y - startPoint2.y) / steps;
   1014 
   1015         int eventX1, eventY1, eventX2, eventY2;
   1016         eventX1 = startPoint1.x;
   1017         eventY1 = startPoint1.y;
   1018         eventX2 = startPoint2.x;
   1019         eventY2 = startPoint2.y;
   1020 
   1021         // allocate for steps plus first down and last up
   1022         PointerCoords[] points1 = new PointerCoords[steps + 2];
   1023         PointerCoords[] points2 = new PointerCoords[steps + 2];
   1024 
   1025         // Include the first and last touch downs in the arrays of steps
   1026         for (int i = 0; i < steps + 1; i++) {
   1027             PointerCoords p1 = new PointerCoords();
   1028             p1.x = eventX1;
   1029             p1.y = eventY1;
   1030             p1.pressure = 1;
   1031             p1.size = 1;
   1032             points1[i] = p1;
   1033 
   1034             PointerCoords p2 = new PointerCoords();
   1035             p2.x = eventX2;
   1036             p2.y = eventY2;
   1037             p2.pressure = 1;
   1038             p2.size = 1;
   1039             points2[i] = p2;
   1040 
   1041             eventX1 += stepX1;
   1042             eventY1 += stepY1;
   1043             eventX2 += stepX2;
   1044             eventY2 += stepY2;
   1045         }
   1046 
   1047         // ending pointers coordinates
   1048         PointerCoords p1 = new PointerCoords();
   1049         p1.x = endPoint1.x;
   1050         p1.y = endPoint1.y;
   1051         p1.pressure = 1;
   1052         p1.size = 1;
   1053         points1[steps + 1] = p1;
   1054 
   1055         PointerCoords p2 = new PointerCoords();
   1056         p2.x = endPoint2.x;
   1057         p2.y = endPoint2.y;
   1058         p2.pressure = 1;
   1059         p2.size = 1;
   1060         points2[steps + 1] = p2;
   1061 
   1062         return performMultiPointerGesture(points1, points2);
   1063     }
   1064 
   1065     /**
   1066      * Performs a multi-touch gesture. You must specify touch coordinates for
   1067      * at least 2 pointers. Each pointer must have all of its touch steps
   1068      * defined in an array of {@link PointerCoords}. You can use this method to
   1069      * specify complex gestures, like circles and irregular shapes, where each
   1070      * pointer may take a different path.
   1071      *
   1072      * To create a single point on a pointer's touch path:
   1073      * <code>
   1074      *       PointerCoords p = new PointerCoords();
   1075      *       p.x = stepX;
   1076      *       p.y = stepY;
   1077      *       p.pressure = 1;
   1078      *       p.size = 1;
   1079      * </code>
   1080      * @param touches represents the pointers' paths. Each {@link PointerCoords}
   1081      * array represents a different pointer. Each {@link PointerCoords} in an
   1082      * array element represents a touch point on a pointer's path.
   1083      * @return <code>true</code> if all touch events for this gesture are injected successfully,
   1084      *         <code>false</code> otherwise
   1085      * @since API Level 18
   1086      */
   1087     public boolean performMultiPointerGesture(PointerCoords[] ...touches) {
   1088         return getInteractionController().performMultiPointerGesture(touches);
   1089     }
   1090 }
   1091