UiScrollable.java revision 18b892c723e812a7e111f102d2b0c0782b116bb6
168d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi/*
268d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi * Copyright (C) 2012 The Android Open Source Project
368d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi *
468d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi * Licensed under the Apache License, Version 2.0 (the "License");
568d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi * you may not use this file except in compliance with the License.
668d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi * You may obtain a copy of the License at
768d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi *
868d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi *      http://www.apache.org/licenses/LICENSE-2.0
968d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi *
1068d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi * Unless required by applicable law or agreed to in writing, software
1168d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi * distributed under the License is distributed on an "AS IS" BASIS,
1268d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1368d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi * See the License for the specific language governing permissions and
1468d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi * limitations under the License.
1568d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi */
1668d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivipackage com.android.uiautomator.core;
17de015407a89018f9422254624e1b75703f38defdGlenn Kasten
1868d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Triviimport android.graphics.Rect;
1968d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Triviimport android.util.Log;
204ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Triviimport android.view.accessibility.AccessibilityNodeInfo;
214ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi
2201e4a8ff63523bba5c8f919a72e0adb66daf4b98Mathias Agopian/**
2301e4a8ff63523bba5c8f919a72e0adb66daf4b98Mathias Agopian * UiScrollable is a {@link UiCollection} and provides support for searching
2468d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi * for items in scrollable layout elements. This class can be used with
254ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi * horizontally or vertically scrollable controls.
260384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten * @since API Level 16
2768d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi */
284ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivipublic class UiScrollable extends UiCollection {
294ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    private static final String LOG_TAG = UiScrollable.class.getSimpleName();
304ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi
314ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    // More steps slows the swipe and prevents contents from being flung too far
32833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten    private static final int SCROLL_STEPS = 55;
33833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten
344ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    private static final int FLING_STEPS = 5;
354ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi
36833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten    // Restrict a swipe's starting and ending points inside a 10% margin of the target
37833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten    private static final double DEFAULT_SWIPE_DEADZONE_PCT = 0.1;
38833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten
39833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten    // Limits the number of swipes/scrolls performed during a search
40833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten    private static int mMaxSearchSwipes = 30;
41833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten
42833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten    // Used in ScrollForward() and ScrollBackward() to determine swipe direction
43833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten    private boolean mIsVerticalList = true;
44833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten
45833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten    private double mSwipeDeadZonePercentage = DEFAULT_SWIPE_DEADZONE_PCT;
46833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten
47833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten    /**
48833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten     * Constructor.
4968d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     *
5037dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi     * @param container a {@link UiSelector} selector to identify the scrollable
5137dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi     *     layout element.
5249935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     * @since API Level 16
5368d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     */
54b2549c73290f1955f3a7731bf98446a45f295dfaGlenn Kasten    public UiScrollable(UiSelector container) {
5568d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi        // wrap the container selector with container so that QueryController can handle
5668d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi        // this type of enumeration search accordingly
5768d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi        super(container);
58b2549c73290f1955f3a7731bf98446a45f295dfaGlenn Kasten    }
59b2549c73290f1955f3a7731bf98446a45f295dfaGlenn Kasten
6068d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    /**
610384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * Set the direction of swipes to be vertical when performing scroll actions.
620384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * @return reference to itself
63b2549c73290f1955f3a7731bf98446a45f295dfaGlenn Kasten     * @since API Level 16
64b2549c73290f1955f3a7731bf98446a45f295dfaGlenn Kasten     */
650384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten    public UiScrollable setAsVerticalList() {
660384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        Tracer.trace();
670384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        mIsVerticalList = true;
680384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        return this;
690384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten    }
700384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten
710384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten    /**
720384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * Set the direction of swipes to be horizontal when performing scroll actions.
730384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * @return reference to itself
740384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * @since API Level 16
750384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     */
760384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten    public UiScrollable setAsHorizontalList() {
770384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        Tracer.trace();
780384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        mIsVerticalList = false;
790384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        return this;
800384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten    }
810384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten
820384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten    /**
830384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * Used privately when performing swipe searches to decide if an element has become
840384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * visible or not.
850384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     *
860384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * @param selector
870384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * @return true if found else false
880384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * @since API Level 16
890384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     */
900384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten    protected boolean exists(UiSelector selector) {
910384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        if(getQueryController().findAccessibilityNodeInfo(selector) != null) {
920384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten            return true;
930384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        }
940384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        return false;
950384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten    }
960384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten
970384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten    /**
980384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * Searches for a child element in the present scrollable container.
990384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * The search first looks for a child element that matches the selector
1000384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * you provided, then looks for the content-description in its children elements.
1010384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * If both search conditions are fulfilled, the method returns a {@ link UiObject}
1020384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * representing the element matching the selector (not the child element in its
1030384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * subhierarchy containing the content-description). By default, this method performs a
1040384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * scroll search.
1050384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * See {@link #getChildByDescription(UiSelector, String, boolean)}
1060384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     *
1070384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * @param childPattern {@link UiSelector} for a child in a scollable layout element
108b2549c73290f1955f3a7731bf98446a45f295dfaGlenn Kasten     * @param text Content-description to find in the children of
109b2549c73290f1955f3a7731bf98446a45f295dfaGlenn Kasten     * the <code>childPattern</code> match
110b2549c73290f1955f3a7731bf98446a45f295dfaGlenn Kasten     * @return {@link UiObject} representing the child element that matches the search conditions
111b2549c73290f1955f3a7731bf98446a45f295dfaGlenn Kasten     * @throws UiObjectNotFoundException
11268d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * @since API Level 16
11368d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     */
11468d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    @Override
11568d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    public UiObject getChildByDescription(UiSelector childPattern, String text)
1162f6d462d89356cdaa77299ca27b60c5cca198d98Gloria Wang            throws UiObjectNotFoundException {
117b2549c73290f1955f3a7731bf98446a45f295dfaGlenn Kasten        Tracer.trace(childPattern, text);
1180384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        return getChildByDescription(childPattern, text, true);
11968d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    }
12091ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten
12191ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten    /**
12291ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten     * Searches for a child element in the present scrollable container.
12391ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten     * The search first looks for a child element that matches the selector
12491ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten     * you provided, then looks for the content-description in its children elements.
12591ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten     * If both search conditions are fulfilled, the method returns a {@ link UiObject}
1260384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * representing the element matching the selector (not the child element in its
12737dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi     * subhierarchy containing the content-description).
12891ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten     *
12991ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten     * @param childPattern {@link UiSelector} for a child in a scollable layout element
1300d1c7e2ccd98bf7e2285c3db98ea263c79b24978Glenn Kasten     * @param text Content-description to find in the children of
1310d1c7e2ccd98bf7e2285c3db98ea263c79b24978Glenn Kasten     * the <code>childPattern</code> match (may be a partial match)
1320d1c7e2ccd98bf7e2285c3db98ea263c79b24978Glenn Kasten     * @param allowScrollSearch set to true if scrolling is allowed
1330d1c7e2ccd98bf7e2285c3db98ea263c79b24978Glenn Kasten     * @return {@link UiObject} representing the child element that matches the search conditions
1340d1c7e2ccd98bf7e2285c3db98ea263c79b24978Glenn Kasten     * @throws UiObjectNotFoundException
1350d1c7e2ccd98bf7e2285c3db98ea263c79b24978Glenn Kasten     * @since API Level 16
13691ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten     */
13737dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi    public UiObject getChildByDescription(UiSelector childPattern, String text,
13837dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi            boolean allowScrollSearch) throws UiObjectNotFoundException {
13937dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        Tracer.trace(childPattern, text, allowScrollSearch);
1404ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        if (text != null) {
1414ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            if (allowScrollSearch) {
14291ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten                scrollIntoView(new UiSelector().descriptionContains(text));
14391ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten            }
14491ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten            return super.getChildByDescription(childPattern, text);
14591ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten        }
1460384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        throw new UiObjectNotFoundException("for description= \"" + text + "\"");
14791ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten    }
1480384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten
1494ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    /**
1504ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * Searches for a child element in the present scrollable container that
1514ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * matches the selector you provided. The search is performed without
1524ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * scrolling and only on visible elements.
15391ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten     *
1544ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * @param childPattern {@link UiSelector} for a child in a scollable layout element
1554ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * @param instance int number representing the occurance of
1564ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * a <code>childPattern</code> match
15791ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten     * @return {@link UiObject} representing the child element that matches the search conditions
1584ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * @since API Level 16
1594ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     */
1604ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    @Override
1610384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten    public UiObject getChildByInstance(UiSelector childPattern, int instance)
1620384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten            throws UiObjectNotFoundException {
1630384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        Tracer.trace(childPattern, instance);
1640384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        UiSelector patternSelector = UiSelector.patternBuilder(getSelector(),
1650384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten                UiSelector.patternBuilder(childPattern).instance(instance));
1660384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        return new UiObject(patternSelector);
1670384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten    }
1680384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten
1694ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    /**
1704ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * Searches for a child element in the present scrollable
17191ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten     * container. The search first looks for a child element that matches the
17237dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi     * selector you provided, then looks for the text in its children elements.
17337dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi     * If both search conditions are fulfilled, the method returns a {@ link UiObject}
17481e917a2605e14901b8f5e6cac7eafb5667aad0dGlenn Kasten     * representing the element matching the selector (not the child element in its
17591ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten     * subhierarchy containing the text). By default, this method performs a
1760384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * scroll search.
1770384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * See {@link #getChildByText(UiSelector, String, boolean)}
17891ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten     *
1790d1c7e2ccd98bf7e2285c3db98ea263c79b24978Glenn Kasten     * @param childPattern {@link UiSelector} selector for a child in a scrollable layout element
1800d1c7e2ccd98bf7e2285c3db98ea263c79b24978Glenn Kasten     * @param text String to find in the children of the <code>childPattern</code> match
1810d1c7e2ccd98bf7e2285c3db98ea263c79b24978Glenn Kasten     * @return {@link UiObject} representing the child element that matches the search conditions
1820d1c7e2ccd98bf7e2285c3db98ea263c79b24978Glenn Kasten     * @throws UiObjectNotFoundException
1835e4d65e369f28746767aba11b618dee314bb8197Glenn Kasten     * @since API Level 16
1845e4d65e369f28746767aba11b618dee314bb8197Glenn Kasten     */
1850d1c7e2ccd98bf7e2285c3db98ea263c79b24978Glenn Kasten    @Override
18691ff087fb814063f9faa23ab37a61e8fe4e38f45Glenn Kasten    public UiObject getChildByText(UiSelector childPattern, String text)
18749935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten            throws UiObjectNotFoundException {
18849935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten        Tracer.trace(childPattern, text);
18981e917a2605e14901b8f5e6cac7eafb5667aad0dGlenn Kasten        return getChildByText(childPattern, text, true);
19081e917a2605e14901b8f5e6cac7eafb5667aad0dGlenn Kasten    }
1910384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten
1920384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten    /**
19381e917a2605e14901b8f5e6cac7eafb5667aad0dGlenn Kasten     * Searches for a child element in the present scrollable container. The
1940384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * search first looks for a child element that matches the
1950384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * selector you provided, then looks for the text in its children elements.
1960384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * If both search conditions are fulfilled, the method returns a {@ link UiObject}
1970384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * representing the element matching the selector (not the child element in its
1980384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * subhierarchy containing the text).
1990384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     *
2000384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * @param childPattern {@link UiSelector} selector for a child in a scrollable layout element
2010384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * @param text String to find in the children of the <code>childPattern</code> match
2020384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * @param allowScrollSearch set to true if scrolling is allowed
2030384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * @return {@link UiObject} representing the child element that matches the search conditions
2040384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * @throws UiObjectNotFoundException
2050384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     * @since API Level 16
2060384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten     */
2070384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten    public UiObject getChildByText(UiSelector childPattern, String text, boolean allowScrollSearch)
2080384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten            throws UiObjectNotFoundException {
2090384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        Tracer.trace(childPattern, text, allowScrollSearch);
2100384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        if (text != null) {
2110384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten            if (allowScrollSearch) {
21281e917a2605e14901b8f5e6cac7eafb5667aad0dGlenn Kasten                scrollIntoView(new UiSelector().text(text));
21381e917a2605e14901b8f5e6cac7eafb5667aad0dGlenn Kasten            }
2140384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten            return super.getChildByText(childPattern, text);
2150384250ce4221e4a6a16db2725e1232c71a60965Glenn Kasten        }
21668d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi        throw new UiObjectNotFoundException("for text= \"" + text + "\"");
21781e917a2605e14901b8f5e6cac7eafb5667aad0dGlenn Kasten    }
21868d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi
21968d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    /**
22068d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * Performs a forward scroll action on the scrollable layout element until
22149935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     * the content-description is found, or until swipe attempts have been exhausted.
22249935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     * See {@link #setMaxSearchSwipes(int)}
22349935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     *
22449935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     * @param text content-description to find within the contents of this scrollable layout element.
22549935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     * @return true if item is found; else, false
22649935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     * @since API Level 16
22749935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     */
22849935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten    public boolean scrollDescriptionIntoView(String text) throws UiObjectNotFoundException {
22949935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten        Tracer.trace(text);
23068d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi        return scrollIntoView(new UiSelector().description(text));
23149935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten    }
23249935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten
23368d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    /**
23468d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * Perform a forward scroll action to move through the scrollable layout element until
23549935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     * a visible item that matches the {@link UiObject} is found.
23649935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     *
23749935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     * @param obj {@link UiObject}
23868d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * @return true if the item was found and now is in view else false
23968d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * @since API Level 16
24068d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     */
24168d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    public boolean scrollIntoView(UiObject obj) throws UiObjectNotFoundException {
24268d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi        Tracer.trace(obj.getSelector());
24368d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi        return scrollIntoView(obj.getSelector());
2447ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi    }
245ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten
24668d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    /**
247513222822545c3e954176476b263df52a47f43a4Glenn Kasten     * Perform a scroll forward action to move through the scrollable layout
248513222822545c3e954176476b263df52a47f43a4Glenn Kasten     * element until a visible item that matches the selector is found.
24968d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     *
250e9236d046fdb5cac0696c42e03443a2439188146Jean-Michel Trivi     * See {@link #scrollDescriptionIntoView(String)} and {@link #scrollTextIntoView(String)}.
25168d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     *
25268d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * @param selector {@link UiSelector} selector
25368d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * @return true if the item was found and now is in view; else, false
25468d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * @since API Level 16
255e9236d046fdb5cac0696c42e03443a2439188146Jean-Michel Trivi     */
2564b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi    public boolean scrollIntoView(UiSelector selector) throws UiObjectNotFoundException {
2574b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi        Tracer.trace(selector);
2584b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi        // if we happen to be on top of the text we want then return here
259a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten        UiSelector childSelector = getSelector().childSelector(selector);
260a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten        if (exists(childSelector)) {
261a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten            return (true);
262a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten        } else {
263a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten            // we will need to reset the search from the beginning to start search
264a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten            scrollToBeginning(mMaxSearchSwipes);
2657c40d3b78c609b2a84acd0dd6e874ab24a73f8d7Glenn Kasten            if (exists(childSelector)) {
2667c40d3b78c609b2a84acd0dd6e874ab24a73f8d7Glenn Kasten                return (true);
267241b9c06493479dc632a8851097c193b724a2b41Andreas Huber            }
268a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten            for (int x = 0; x < mMaxSearchSwipes; x++) {
269a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten                boolean scrolled = scrollForward();
270a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten                if(exists(childSelector)) {
271a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten                    return true;
272a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten                }
273a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten                if (!scrolled) {
274a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten                    return false;
275a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten                }
276485a038f9f0f898227b8ab4218e94c5d56b6ed0bGlenn Kasten            }
2774b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi        }
2784b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi        return false;
27968d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    }
2804b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi
2814b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi    /**
2824b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi     * Scrolls forward until the UiObject is fully visible in the scrollable container.
2834b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi     * Use this method to make sure that the child item's edges are not offscreen.
2844b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi     *
2855933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten     * @param childObject {@link UiObject} representing the child element
2864b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi     * @return true if the child element is already fully visible, or
2874b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi     * if the method scrolled successfully until the child became fully visible;
2884b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi     * otherwise, false if the attempt to scroll failed.
2894b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi     * @throws UiObjectNotFoundException
2905933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten     * @hide
291a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten     */
2925933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten    public boolean ensureFullyVisible(UiObject childObject) throws UiObjectNotFoundException {
2935933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten        Rect actual = childObject.getBounds();
2945933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten        Rect visible = childObject.getVisibleBounds();
2954b0e0b2860ffd5e246b42c8a434833cca2f068b3Jean-Michel Trivi        if (visible.width() * visible.height() == actual.width() * actual.height()) {
29668d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi            // area match, item fully visible
29768d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi            return true;
29868d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi        }
299ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten        boolean shouldSwipeForward = false;
300b2549c73290f1955f3a7731bf98446a45f295dfaGlenn Kasten        if (mIsVerticalList) {
30135ac702ee1ad91e5c8748c12450222d50b366a52Glenn Kasten            // if list is vertical, matching top edge implies obscured bottom edge
30235ac702ee1ad91e5c8748c12450222d50b366a52Glenn Kasten            // so we need to scroll list forward
30335ac702ee1ad91e5c8748c12450222d50b366a52Glenn Kasten            shouldSwipeForward = actual.top == visible.top;
30435ac702ee1ad91e5c8748c12450222d50b366a52Glenn Kasten        } else {
30535ac702ee1ad91e5c8748c12450222d50b366a52Glenn Kasten            // if list is horizontal, matching left edge implies obscured right edge,
30635ac702ee1ad91e5c8748c12450222d50b366a52Glenn Kasten            // so we need to scroll list forward
30735ac702ee1ad91e5c8748c12450222d50b366a52Glenn Kasten            shouldSwipeForward = actual.left == visible.left;
308ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten        }
30968d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi        if (mIsVerticalList) {
31068d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi            if (shouldSwipeForward) {
31191145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi                return swipeUp(10);
31291145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi            } else {
31391145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi                return swipeDown(10);
31491145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi            }
31591145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi        } else {
31691145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi            if (shouldSwipeForward) {
31791145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi                return swipeLeft(10);
31891145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi            } else {
31991145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi                return swipeRight(10);
32091145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi            }
32191145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi        }
32291145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi    }
32391145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi
32491145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi    /**
32570c49ae2867094072a4365423417ea452bf82231Jean-Michel Trivi     * Performs a forward scroll action on the scrollable layout element until
32668d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * the text you provided is visible, or until swipe attempts have been exhausted.
32768d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * See {@link #setMaxSearchSwipes(int)}
3284ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     *
32985edd878a30caa535b0267d8d6e61b4ccc0d5fd0Glenn Kasten     * @param text test to look for
33068d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * @return true if item is found; else, false
331e9236d046fdb5cac0696c42e03443a2439188146Jean-Michel Trivi     * @since API Level 16
33249935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     */
33349935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten    public boolean scrollTextIntoView(String text) throws UiObjectNotFoundException {
334ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten        Tracer.trace(text);
335241b9c06493479dc632a8851097c193b724a2b41Andreas Huber        return scrollIntoView(new UiSelector().text(text));
336ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten    }
337ad1ab1d13a9b043202b9d5cdc1d8c4ef66cbbca8Glenn Kasten
33868d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    /**
33968d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * Sets the maximum number of scrolls allowed when performing a
34049935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     * scroll action in search of a child element.
34168d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * See {@link #getChildByDescription(UiSelector, String)} and
34206059e5ee1eaf907589c7f8d1320253f92211348Glenn Kasten     * {@link #getChildByText(UiSelector, String)}.
34306059e5ee1eaf907589c7f8d1320253f92211348Glenn Kasten     *
34406059e5ee1eaf907589c7f8d1320253f92211348Glenn Kasten     * @param swipes the number of search swipes to perform until giving up
34506059e5ee1eaf907589c7f8d1320253f92211348Glenn Kasten     * @return reference to itself
34606059e5ee1eaf907589c7f8d1320253f92211348Glenn Kasten     * @since API Level 16
34706059e5ee1eaf907589c7f8d1320253f92211348Glenn Kasten     */
34868d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    public UiScrollable setMaxSearchSwipes(int swipes) {
34985edd878a30caa535b0267d8d6e61b4ccc0d5fd0Glenn Kasten        Tracer.trace(swipes);
350e9236d046fdb5cac0696c42e03443a2439188146Jean-Michel Trivi        mMaxSearchSwipes = swipes;
35168d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi        return this;
35268d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    }
3534ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi
35468d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    /**
355e9236d046fdb5cac0696c42e03443a2439188146Jean-Michel Trivi     * Gets the maximum number of scrolls allowed when performing a
3565933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten     * scroll action in search of a child element.
35768d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * See {@link #getChildByDescription(UiSelector, String)} and
35868d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * {@link #getChildByText(UiSelector, String)}.
3595933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten     *
36068d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * @return max the number of search swipes to perform until giving up
36168d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * @since API Level 16
3624ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     */
36368d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    public int getMaxSearchSwipes() {
364e9236d046fdb5cac0696c42e03443a2439188146Jean-Michel Trivi        Tracer.trace();
3655933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten        return mMaxSearchSwipes;
36668d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi    }
36768d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi
3685933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten    /**
36937dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi     * Performs a forward fling with the default number of fling steps (5).
37037dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi     * If the swipe direction is set to vertical, then the swipes will be
371f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten     * performed from bottom to top. If the swipe
372f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten     * direction is set to horizontal, then the swipes will be performed from
373f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten     * right to left. Make sure to take into account devices configured with
374f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten     * right-to-left languages like Arabic and Hebrew.
375f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten     *
376f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten     * @return true if scrolled, false if can't scroll anymore
377f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten     * @since API Level 16
378f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten     */
379f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten    public boolean flingForward() throws UiObjectNotFoundException {
380f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten        Tracer.trace();
381f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten        return scrollForward(FLING_STEPS);
382f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten    }
383f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten
384f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten    /**
385f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten     * Performs a forward scroll with the default number of scroll steps (55).
386f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten     * If the swipe direction is set to vertical,
387f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten     * then the swipes will be performed from bottom to top. If the swipe
388f6445d330c05ccc57d1adcc6ee05735a33f78881Glenn Kasten     * direction is set to horizontal, then the swipes will be performed from
3897ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi     * right to left. Make sure to take into account devices configured with
3907ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi     * right-to-left languages like Arabic and Hebrew.
3917ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi     *
3924ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * @return true if scrolled, false if can't scroll anymore
3934ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * @since API Level 16
3947ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi     */
3957ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi    public boolean scrollForward() throws UiObjectNotFoundException {
3967ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi        Tracer.trace();
3977ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi        return scrollForward(SCROLL_STEPS);
3987ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi    }
399a9f22e6f5f53e90daa779e38b22f88e4faa35c95Glenn Kasten
400a9f22e6f5f53e90daa779e38b22f88e4faa35c95Glenn Kasten    /**
401a9f22e6f5f53e90daa779e38b22f88e4faa35c95Glenn Kasten     * Performs a forward scroll. If the swipe direction is set to vertical,
4027ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi     * then the swipes will be performed from bottom to top. If the swipe
40349935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     * direction is set to horizontal, then the swipes will be performed from
40449935c51fddcd0caa0030e2aac0c3a7ba3339e3dGlenn Kasten     * right to left. Make sure to take into account devices configured with
4057ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi     * right-to-left languages like Arabic and Hebrew.
4064ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     *
4074ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * @param steps number of steps. Use this to control the speed of the scroll action
4084ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * @return true if scrolled, false if can't scroll anymore
409c0a40f3efef1706f861777ff68003fe344730055Glenn Kasten     * @since API Level 16
4107ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi     */
4117ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi    public boolean scrollForward(int steps) throws UiObjectNotFoundException {
412a9f22e6f5f53e90daa779e38b22f88e4faa35c95Glenn Kasten        Tracer.trace(steps);
413a9f22e6f5f53e90daa779e38b22f88e4faa35c95Glenn Kasten        Log.d(LOG_TAG, "scrollForward() on selector = " + getSelector());
414a9f22e6f5f53e90daa779e38b22f88e4faa35c95Glenn Kasten        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
415a9f22e6f5f53e90daa779e38b22f88e4faa35c95Glenn Kasten        if(node == null) {
416a9f22e6f5f53e90daa779e38b22f88e4faa35c95Glenn Kasten            throw new UiObjectNotFoundException(getSelector().toString());
417a9f22e6f5f53e90daa779e38b22f88e4faa35c95Glenn Kasten        }
4187ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi        Rect rect = new Rect();
4197ef5526a7bd12eccfa777cc8bc167794634f405aJean-Michel Trivi        node.getBoundsInScreen(rect);
420e2e8fa36bd7448b59fbcdf141e0b6d21e5401d91Glenn Kasten
4214ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        int downX = 0;
4224ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        int downY = 0;
4234ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        int upX = 0;
4244ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        int upY = 0;
4254ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi
4264ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        // scrolling is by default assumed vertically unless the object is explicitly
4274ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        // set otherwise by setAsHorizontalContainer()
4284ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        if(mIsVerticalList) {
4294ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            int swipeAreaAdjust = (int)(rect.height() * getSwipeDeadZonePercentage());
4304ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            // scroll vertically: swipe down -> up
4319f07ea788f57654acf29d1321b40162e41eb122bGlenn Kasten            downX = rect.centerX();
4329f07ea788f57654acf29d1321b40162e41eb122bGlenn Kasten            downY = rect.bottom - swipeAreaAdjust;
4339f07ea788f57654acf29d1321b40162e41eb122bGlenn Kasten            upX = rect.centerX();
4349f07ea788f57654acf29d1321b40162e41eb122bGlenn Kasten            upY = rect.top + swipeAreaAdjust;
4359f07ea788f57654acf29d1321b40162e41eb122bGlenn Kasten        } else {
4369f07ea788f57654acf29d1321b40162e41eb122bGlenn Kasten            int swipeAreaAdjust = (int)(rect.width() * getSwipeDeadZonePercentage());
4379f07ea788f57654acf29d1321b40162e41eb122bGlenn Kasten            // scroll horizontally: swipe right -> left
4389f07ea788f57654acf29d1321b40162e41eb122bGlenn Kasten            // TODO: Assuming device is not in right to left language
4394ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            downX = rect.right - swipeAreaAdjust;
4404ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            downY = rect.centerY();
4414ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            upX = rect.left + swipeAreaAdjust;
4424ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            upY = rect.centerY();
4434ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        }
44437dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi        return getInteractionController().scrollSwipe(downX, downY, upX, upY, steps);
4454ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    }
4464ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi
447e2e8fa36bd7448b59fbcdf141e0b6d21e5401d91Glenn Kasten    /**
448e2e8fa36bd7448b59fbcdf141e0b6d21e5401d91Glenn Kasten     * Performs a backwards fling action with the default number of fling
449fa2bd93c3a9852a1f879663eeff598d13cf8fa81Glenn Kasten     * steps (5). If the swipe direction is set to vertical,
450fa2bd93c3a9852a1f879663eeff598d13cf8fa81Glenn Kasten     * then the swipe will be performed from top to bottom. If the swipe
45137dc2fccf3f122b79ebd554de209d0a3c94ae161Jean-Michel Trivi     * direction is set to horizontal, then the swipes will be performed from
4524ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * left to right. Make sure to take into account devices configured with
4534ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * right-to-left languages like Arabic and Hebrew.
4544ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     *
4553610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     * @return true if scrolled, and false if can't scroll anymore
4563610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     * @since API Level 16
4573610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     */
4583610785fa93586ce84a27a27530feb77b8035229Glenn Kasten    public boolean flingBackward() throws UiObjectNotFoundException {
4593610785fa93586ce84a27a27530feb77b8035229Glenn Kasten        Tracer.trace();
4603610785fa93586ce84a27a27530feb77b8035229Glenn Kasten        return scrollBackward(FLING_STEPS);
4613610785fa93586ce84a27a27530feb77b8035229Glenn Kasten    }
4623610785fa93586ce84a27a27530feb77b8035229Glenn Kasten
4633610785fa93586ce84a27a27530feb77b8035229Glenn Kasten    /**
4643610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     * Performs a backward scroll with the default number of scroll steps (55).
4653610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     * If the swipe direction is set to vertical,
4663610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     * then the swipes will be performed from top to bottom. If the swipe
4673610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     * direction is set to horizontal, then the swipes will be performed from
4683610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     * left to right. Make sure to take into account devices configured with
4693610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     * right-to-left languages like Arabic and Hebrew.
4703610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     *
4713610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     * @return true if scrolled, and false if can't scroll anymore
4723610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     * @since API Level 16
4733610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     */
4743610785fa93586ce84a27a27530feb77b8035229Glenn Kasten    public boolean scrollBackward() throws UiObjectNotFoundException {
4753610785fa93586ce84a27a27530feb77b8035229Glenn Kasten        Tracer.trace();
4763610785fa93586ce84a27a27530feb77b8035229Glenn Kasten        return scrollBackward(SCROLL_STEPS);
4773610785fa93586ce84a27a27530feb77b8035229Glenn Kasten    }
4783610785fa93586ce84a27a27530feb77b8035229Glenn Kasten
4793610785fa93586ce84a27a27530feb77b8035229Glenn Kasten    /**
4803610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     * Performs a backward scroll. If the swipe direction is set to vertical,
4813610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     * then the swipes will be performed from top to bottom. If the swipe
4823610785fa93586ce84a27a27530feb77b8035229Glenn Kasten     * direction is set to horizontal, then the swipes will be performed from
4834ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * left to right. Make sure to take into account devices configured with
4844ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * right-to-left languages like Arabic and Hebrew.
4854ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     *
4864ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * @param steps number of steps. Use this to control the speed of the scroll action.
48768d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * @return true if scrolled, false if can't scroll anymore
4884ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * @since API Level 16
4894ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     */
4904ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    public boolean scrollBackward(int steps) throws UiObjectNotFoundException {
4914ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        Tracer.trace(steps);
4924ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        Log.d(LOG_TAG, "scrollBackward() on selector = " + getSelector());
4934ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
4944ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        if (node == null) {
4954ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            throw new UiObjectNotFoundException(getSelector().toString());
4964ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        }
4974ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        Rect rect = new Rect();
4984ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        node.getBoundsInScreen(rect);
4994ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi
5004ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        int downX = 0;
5014ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        int downY = 0;
5024ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        int upX = 0;
5034ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        int upY = 0;
5044ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi
5054ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        // scrolling is by default assumed vertically unless the object is explicitly
5064ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        // set otherwise by setAsHorizontalContainer()
5074ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        if(mIsVerticalList) {
5084ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            int swipeAreaAdjust = (int)(rect.height() * getSwipeDeadZonePercentage());
5094ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            Log.d(LOG_TAG, "scrollToBegining() using vertical scroll");
5104ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            // scroll vertically: swipe up -> down
5114ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            downX = rect.centerX();
5124ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            downY = rect.top + swipeAreaAdjust;
5134ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            upX = rect.centerX();
5144ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            upY = rect.bottom - swipeAreaAdjust;
5154ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        } else {
5164ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            int swipeAreaAdjust = (int)(rect.width() * getSwipeDeadZonePercentage());
5174ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            Log.d(LOG_TAG, "scrollToBegining() using hotizontal scroll");
5184ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            // scroll horizontally: swipe left -> right
5194ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            // TODO: Assuming device is not in right to left language
5204ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            downX = rect.left + swipeAreaAdjust;
5214ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi            downY = rect.centerY();
522b2549c73290f1955f3a7731bf98446a45f295dfaGlenn Kasten            upX = rect.right - swipeAreaAdjust;
523b2549c73290f1955f3a7731bf98446a45f295dfaGlenn Kasten            upY = rect.centerY();
5244ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        }
5254ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        return getInteractionController().scrollSwipe(downX, downY, upX, upY, steps);
5264ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    }
5274ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi
5284ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    /**
5294ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * Scrolls to the beginning of a scrollable layout element. The beginning
53006059e5ee1eaf907589c7f8d1320253f92211348Glenn Kasten     * can be at the  top-most edge in the case of vertical controls, or the
531fa2bd93c3a9852a1f879663eeff598d13cf8fa81Glenn Kasten     * left-most edge for horizontal controls. Make sure to take into account
532fa2bd93c3a9852a1f879663eeff598d13cf8fa81Glenn Kasten     * devices configured with right-to-left languages like Arabic and Hebrew.
53306059e5ee1eaf907589c7f8d1320253f92211348Glenn Kasten     *
5344ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * @param steps use steps to control the speed, so that it may be a scroll, or fling
53506059e5ee1eaf907589c7f8d1320253f92211348Glenn Kasten     * @return true on scrolled else false
53606059e5ee1eaf907589c7f8d1320253f92211348Glenn Kasten     * @since API Level 16
537fa2bd93c3a9852a1f879663eeff598d13cf8fa81Glenn Kasten     */
53806059e5ee1eaf907589c7f8d1320253f92211348Glenn Kasten    public boolean scrollToBeginning(int maxSwipes, int steps) throws UiObjectNotFoundException {
5395933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten        Tracer.trace(maxSwipes, steps);
5405933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten        Log.d(LOG_TAG, "scrollToBeginning() on selector = " + getSelector());
541a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten        // protect against potential hanging and return after preset attempts
542a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten        for(int x = 0; x < maxSwipes; x++) {
543a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten            if(!scrollBackward(steps)) {
5445933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten                break;
545fa2bd93c3a9852a1f879663eeff598d13cf8fa81Glenn Kasten            }
546b4393ef4ef3edb785746c37fd7b68950e85283aeGlenn Kasten        }
547fa2bd93c3a9852a1f879663eeff598d13cf8fa81Glenn Kasten        return true;
548fa2bd93c3a9852a1f879663eeff598d13cf8fa81Glenn Kasten    }
549fa2bd93c3a9852a1f879663eeff598d13cf8fa81Glenn Kasten
550b4393ef4ef3edb785746c37fd7b68950e85283aeGlenn Kasten    /**
55199b927751677abfb60a388d65dfeed1fed1db12cGlenn Kasten     * Scrolls to the beginning of a scrollable layout element. The beginning
55299b927751677abfb60a388d65dfeed1fed1db12cGlenn Kasten     * can be at the  top-most edge in the case of vertical controls, or the
553b4393ef4ef3edb785746c37fd7b68950e85283aeGlenn Kasten     * left-most edge for horizontal controls. Make sure to take into account
55499b927751677abfb60a388d65dfeed1fed1db12cGlenn Kasten     * devices configured with right-to-left languages like Arabic and Hebrew.
555b4393ef4ef3edb785746c37fd7b68950e85283aeGlenn Kasten     *
55699b927751677abfb60a388d65dfeed1fed1db12cGlenn Kasten     * @param maxSwipes
557b4393ef4ef3edb785746c37fd7b68950e85283aeGlenn Kasten     * @return true on scrolled else false
55899b927751677abfb60a388d65dfeed1fed1db12cGlenn Kasten     * @since API Level 16
559a6c69c7e1665b38da8d6784e65210acbe501b92cSteve Block     */
560fa2bd93c3a9852a1f879663eeff598d13cf8fa81Glenn Kasten    public boolean scrollToBeginning(int maxSwipes) throws UiObjectNotFoundException {
561fa2bd93c3a9852a1f879663eeff598d13cf8fa81Glenn Kasten        Tracer.trace(maxSwipes);
562fa2bd93c3a9852a1f879663eeff598d13cf8fa81Glenn Kasten        return scrollToBeginning(maxSwipes, SCROLL_STEPS);
5634ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    }
5644ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi
5654ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    /**
5661fa5c3206d06bbebdea2dc92f378ce6b8a211e23Glenn Kasten     * Performs a fling gesture to reach the beginning of a scrollable layout element.
5674ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * The beginning can be at the  top-most edge in the case of vertical controls, or
5684ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * the left-most edge for horizontal controls. Make sure to take into
5694ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * account devices configured with right-to-left languages like Arabic and Hebrew.
5709f07ea788f57654acf29d1321b40162e41eb122bGlenn Kasten     *
5719f07ea788f57654acf29d1321b40162e41eb122bGlenn Kasten     * @param maxSwipes
5729f07ea788f57654acf29d1321b40162e41eb122bGlenn Kasten     * @return true on scrolled else false
5739f07ea788f57654acf29d1321b40162e41eb122bGlenn Kasten     * @since API Level 16
5744ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     */
5754ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    public boolean flingToBeginning(int maxSwipes) throws UiObjectNotFoundException {
5764ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        Tracer.trace(maxSwipes);
5774ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        return scrollToBeginning(maxSwipes, FLING_STEPS);
578833251ab9e5e59a6ea5ac325122cf3abdf7cd944Glenn Kasten    }
5794ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi
5804ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    /**
5814ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * Scrolls to the end of a scrollable layout element. The end can be at the
5824ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * bottom-most edge in the case of vertical controls, or the right-most edge for
5834ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * horizontal controls. Make sure to take into account devices configured with
5844ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * right-to-left languages like Arabic and Hebrew.
5854ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     *
5864ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * @param steps use steps to control the speed, so that it may be a scroll, or fling
5874ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * @return true on scrolled else false
5884ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     * @since API Level 16
5894ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi     */
5904ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi    public boolean scrollToEnd(int maxSwipes, int steps) throws UiObjectNotFoundException {
5914ee246c55533bdab8ab5fa0f0581744fe58e7c91Jean-Michel Trivi        Tracer.trace(maxSwipes, steps);
59291145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi        // protect against potential hanging and return after preset attempts
59391145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi        for(int x = 0; x < maxSwipes; x++) {
59491145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi            if(!scrollForward(steps)) {
59591145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi                break;
59691145ef159d3e165a461cbd76341ff8ed3d72baeJean-Michel Trivi            }
59768d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi        }
59868d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi        return true;
5995933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten    }
6005933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten
6015933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten    /**
602a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten     * Scrolls to the end of a scrollable layout element. The end can be at the
6035933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten     * bottom-most edge in the case of vertical controls, or the right-most edge for
604a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten     * horizontal controls. Make sure to take into account devices configured with
605a0fa47f72f47fffb80ab2ae791739ce73de1e8f4Glenn Kasten     * right-to-left languages like Arabic and Hebrew.
6065933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten     *
6075933f3d5e532aaac31ce0e6551c59f0197c0ae3cGlenn Kasten     * @param maxSwipes
60868d56b8ebaf60184a3aef988e3d2b09ed8b88c05Jean-Michel Trivi     * @return true on scrolled, else false
609     * @since API Level 16
610     */
611    public boolean scrollToEnd(int maxSwipes) throws UiObjectNotFoundException {
612        Tracer.trace(maxSwipes);
613        return scrollToEnd(maxSwipes, SCROLL_STEPS);
614    }
615
616    /**
617     * Performs a fling gesture to reach the end of a scrollable layout element.
618     * The end can be at the  bottom-most edge in the case of vertical controls, or
619     * the right-most edge for horizontal controls. Make sure to take into
620     * account devices configured with right-to-left languages like Arabic and Hebrew.
621     *
622     * @param maxSwipes
623     * @return true on scrolled, else false
624     * @since API Level 16
625     */
626    public boolean flingToEnd(int maxSwipes) throws UiObjectNotFoundException {
627        Tracer.trace(maxSwipes);
628        return scrollToEnd(maxSwipes, FLING_STEPS);
629    }
630
631    /**
632     * Returns the percentage of a widget's size that's considered as a no-touch
633     * zone when swiping. The no-touch zone is set as a percentage of a widget's total
634     * width or height, denoting a margin around the swipable area of the widget.
635     * Swipes must start and end inside this margin. This is important when the
636     * widget being swiped may not respond to the swipe if started at a point
637     * too near to the edge. The default is 10% from either edge.
638     *
639     * @return a value between 0 and 1
640     * @since API Level 16
641     */
642    public double getSwipeDeadZonePercentage() {
643        Tracer.trace();
644        return mSwipeDeadZonePercentage;
645    }
646
647    /**
648     * Sets the percentage of a widget's size that's considered as no-touch
649     * zone when swiping.
650     * The no-touch zone is set as percentage of a widget's total width or height,
651     * denoting a margin around the swipable area of the widget. Swipes must
652     * always start and end inside this margin. This is important when the
653     * widget being swiped may not respond to the swipe if started at a point
654     * too near to the edge. The default is 10% from either edge.
655     *
656     * @param swipeDeadZonePercentage is a value between 0 and 1
657     * @return reference to itself
658     * @since API Level 16
659     */
660    public UiScrollable setSwipeDeadZonePercentage(double swipeDeadZonePercentage) {
661        Tracer.trace(swipeDeadZonePercentage);
662        mSwipeDeadZonePercentage = swipeDeadZonePercentage;
663        return this;
664    }
665}
666