1e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu/*
2e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Copyright (C) 2012 The Android Open Source Project
3e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
4e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Licensed under the Apache License, Version 2.0 (the "License");
5e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * you may not use this file except in compliance with the License.
6e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * You may obtain a copy of the License at
7e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
8e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *      http://www.apache.org/licenses/LICENSE-2.0
9e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu *
10e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * Unless required by applicable law or agreed to in writing, software
11e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * distributed under the License is distributed on an "AS IS" BASIS,
12e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * See the License for the specific language governing permissions and
14e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu * limitations under the License.
15e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
16e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupackage com.android.uiautomator.core;
17e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
18e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.graphics.Rect;
19e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.util.Log;
20e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhuimport android.view.accessibility.AccessibilityNodeInfo;
21e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
22e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu/**
233d50587be8ff021369c90554d814839335b445b0Adam Momtaz * UiScrollable is a {@link UiCollection} and provides support for searching for items in a
243d50587be8ff021369c90554d814839335b445b0Adam Momtaz * scrollable UI elements. Used with horizontally or vertically scrollable UI.
25e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
26e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupublic class UiScrollable extends UiCollection {
27e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final String LOG_TAG = UiScrollable.class.getSimpleName();
28e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
29e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // More steps slows the swipe and prevents contents from being flung too far
30e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final int SCROLL_STEPS = 55;
31e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
32e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final int FLING_STEPS = 5;
33e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
34e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // Restrict a swipe's starting and ending points inside a 10% margin of the target
35e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final double DEFAULT_SWIPE_DEADZONE_PCT = 0.1;
36e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
37e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // Limits the number of swipes/scrolls performed during a search
38e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static int mMaxSearchSwipes = 30;
39e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
40e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // Used in ScrollForward() and ScrollBackward() to determine swipe direction
41f612e6a05f47b28ae0f5715545658c08dd759dd7Guang Zhu    private boolean mIsVerticalList = true;
42e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
43e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private double mSwipeDeadZonePercentage = DEFAULT_SWIPE_DEADZONE_PCT;
44e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
45e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
463d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * UiScrollable is a {@link UiCollection} and as such requires a {@link UiSelector} to
473d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * identify the container UI element of the scrollable collection. Further operations on
483d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * the items in the container will require specifying UiSelector as an item selector.
493d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
504ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param container a {@link UiSelector} selector
51e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
524ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiScrollable(UiSelector container) {
53e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // wrap the container selector with container so that QueryController can handle
54e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // this type of enumeration search accordingly
55e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        super(container);
56e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
57e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
58e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
59e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Set the direction of swipes when performing scroll search
60ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * @return reference to itself
61e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
62ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz    public UiScrollable setAsVerticalList() {
63e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mIsVerticalList = true;
64ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz        return this;
65e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
66e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
67e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
68e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Set the direction of swipes when performing scroll search
69ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * @return reference to itself
70e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
71ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz    public UiScrollable setAsHorizontalList() {
72e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mIsVerticalList = false;
73ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz        return this;
74e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
75e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
76e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
77e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Used privately when performing swipe searches to decide if an element has become
78e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * visible or not.
793d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
80e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param selector
81e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if found else false
82e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
834ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    protected boolean exists(UiSelector selector) {
84e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(getQueryController().findAccessibilityNodeInfo(selector) != null) {
85e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return true;
86e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
87e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
88e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
89e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
90e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
914ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * Searches for child UI element within the constraints of this UiScrollable {@link UiSelector}
923d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * container. It looks for any child matching the <code>childPattern</code> argument within its
933d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * hierarchy with a matching content-description text. The returned UiObject will represent the
943d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * UI element matching the <code>childPattern</code> and not the sub element that matched the
953d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * content description.</p>
963d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * By default this operation will perform scroll search while attempting to find the UI element
974ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * See {@link #getChildByDescription(UiSelector, String, boolean)}
983d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
994ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param childPattern {@link UiSelector} selector of the child pattern to match and return
100e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param text String of the identifying child contents of of the <code>childPattern</code>
101e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link UiObject} pointing at and instance of <code>childPattern</code>
102e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
103e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
104e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    @Override
1054ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChildByDescription(UiSelector childPattern, String text)
106e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throws UiObjectNotFoundException {
107e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getChildByDescription(childPattern, text, true);
108e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
109e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
110e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1113d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link #getChildByDescription(UiSelector, String)}
1123d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1134ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param childPattern {@link UiSelector} selector of the child pattern to match and return
114e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param text String may be a partial match for the content-description of a child element.
115e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param allowScrollSearch set to true if scrolling is allowed
116e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link UiObject} pointing at and instance of <code>childPattern</code>
117e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
118e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
1194ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChildByDescription(UiSelector childPattern, String text,
1204ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz            boolean allowScrollSearch) throws UiObjectNotFoundException {
121e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (text != null) {
122e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (allowScrollSearch) {
1234ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz                scrollIntoView(new UiSelector().descriptionContains(text));
124e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
125e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return super.getChildByDescription(childPattern, text);
126e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
127e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        throw new UiObjectNotFoundException("for description= \"" + text + "\"");
128e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
129e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
130e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1314ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * Searches for child UI element within the constraints of this UiScrollable {@link UiSelector}
132e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * selector. It looks for any child matching the <code>childPattern</code> argument and
133e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * return the <code>instance</code> specified. The operation is performed only on the visible
134e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * items and no scrolling is performed in this case.
1353d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1364ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param childPattern {@link UiSelector} selector of the child pattern to match and return
137e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param instance int the desired matched instance of this <code>childPattern</code>
138e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link UiObject} pointing at and instance of <code>childPattern</code>
139e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
140e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    @Override
1414ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChildByInstance(UiSelector childPattern, int instance)
142e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throws UiObjectNotFoundException {
1434ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        UiSelector patternSelector = UiSelector.patternBuilder(getSelector(),
1444ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz                UiSelector.patternBuilder(childPattern).instance(instance));
145e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return new UiObject(patternSelector);
146e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
147e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
148e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1494ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * Searches for child UI element within the constraints of this UiScrollable {@link UiSelector}
1503d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * container. It looks for any child matching the <code>childPattern</code> argument that has
1513d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * a sub UI element anywhere within its sub hierarchy that has text attribute
152e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <code>text</code>. The returned UiObject will point at the <code>childPattern</code>
1533d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * instance that matched the search and not at the text matched sub element</p>
154e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * By default this operation will perform scroll search while attempting to find the UI
155e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * element.
1564ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * See {@link #getChildByText(UiSelector, String, boolean)}
1573d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1584ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param childPattern {@link UiSelector} selector of the child pattern to match and return
159e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param text String of the identifying child contents of of the <code>childPattern</code>
160e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link UiObject} pointing at and instance of <code>childPattern</code>
161e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
162e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
163e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    @Override
1644ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChildByText(UiSelector childPattern, String text)
165e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throws UiObjectNotFoundException {
166e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getChildByText(childPattern, text, true);
167e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
168e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
169e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1703d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link #getChildByText(UiSelector, String)}
1713d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1724ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param childPattern {@link UiSelector} selector of the child pattern to match and return
173e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param text String of the identifying child contents of of the <code>childPattern</code>
174e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param allowScrollSearch set to true if scrolling is allowed
175e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link UiObject} pointing at and instance of <code>childPattern</code>
176e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
177e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
1784ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChildByText(UiSelector childPattern, String text, boolean allowScrollSearch)
179e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throws UiObjectNotFoundException {
180e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
181e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (text != null) {
182e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (allowScrollSearch) {
1834ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz                scrollIntoView(new UiSelector().text(text));
184e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
185e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return super.getChildByText(childPattern, text);
186e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
187e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        throw new UiObjectNotFoundException("for text= \"" + text + "\"");
188e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
189e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
190e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1913d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Performs a swipe Up on the UI element until the requested content-description
1923d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * is visible or until swipe attempts have been exhausted. See {@link #setMaxSearchSwipes(int)}
1933d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
194e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param text to look for anywhere within the contents of this scrollable.
195e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if item us found else false
196e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
1976b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollDescriptionIntoView(String text) throws UiObjectNotFoundException {
1984ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return scrollIntoView(new UiSelector().description(text));
199e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
200e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
201e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2024ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * Perform a scroll search for a UI element matching the {@link UiSelector} selector argument.
2033d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link #scrollDescriptionIntoView(String)} and {@link #scrollTextIntoView(String)}.
2043d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
205ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * @param obj {@link UiObject}
206ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * @return true if the item was found and now is in view else false
207ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     */
208ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz    public boolean scrollIntoView(UiObject obj) throws UiObjectNotFoundException {
209ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz        return scrollIntoView(obj.getSelector());
210ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz    }
211ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz
212ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz    /**
213ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * Perform a scroll search for a UI element matching the {@link UiSelector} selector argument.
214ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * See {@link #scrollDescriptionIntoView(String)} and {@link #scrollTextIntoView(String)}.
215ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     *
2164ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param selector {@link UiSelector} selector
217e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the item was found and now is in view else false
218e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
2196b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollIntoView(UiSelector selector) throws UiObjectNotFoundException {
220e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // if we happen to be on top of the text we want then return here
221e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (exists(getSelector().childSelector(selector))) {
222e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return (true);
223e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } else {
224e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // we will need to reset the search from the beginning to start search
225e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            scrollToBeginning(mMaxSearchSwipes);
226e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (exists(getSelector().childSelector(selector))) {
227e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return (true);
228e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
229e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            for (int x = 0; x < mMaxSearchSwipes; x++) {
230e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if(!scrollForward()) {
231e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    return false;
232e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
233e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
234e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if(exists(getSelector().childSelector(selector))) {
235e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    return true;
236e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
237e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
238e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
239e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
240e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
241e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
242e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2433d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Performs a swipe up on the UI element until the requested text is visible
2443d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * or until swipe attempts have been exhausted. See {@link #setMaxSearchSwipes(int)}
2453d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
246e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param text to look for
247e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if item us found else false
248e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
2496b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollTextIntoView(String text) throws UiObjectNotFoundException {
2504ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return scrollIntoView(new UiSelector().text(text));
251e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
252e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
253e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
254e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #getChildByDescription(String, boolean)} and {@link #getChildByText(String, boolean)}
255e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * use an arguments that specifies if scrolling is allowed while searching for the UI element.
256e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * The number of scrolls allowed to perform a search can be modified by this method.
257e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * The current value can be read by calling {@link #getMaxSearchSwipes()}
2583d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2593d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param swipes is the number of search swipes until abort
260ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * @return reference to itself
261e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
262ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz    public UiScrollable setMaxSearchSwipes(int swipes) {
263e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mMaxSearchSwipes = swipes;
264ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz        return this;
265e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
266e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
267e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
268e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * {@link #getChildByDescription(String, boolean)} and {@link #getChildByText(String, boolean)}
269e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * use an arguments that specifies if scrolling is allowed while searching for the UI element.
270e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * The number of scrolls currently allowed to perform a search can be read by this method.
271e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * See {@link #setMaxSearchSwipes(int)}
2723d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
273e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return max value of the number of swipes currently allowed during a scroll search
274e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
275e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public int getMaxSearchSwipes() {
276e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mMaxSearchSwipes;
277e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
278e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
279e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
280e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * A convenience version of {@link UiScrollable#scrollForward(int)}, performs a fling
281e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
282e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if scrolled and false if can't scroll anymore
283e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
2846b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean flingForward() throws UiObjectNotFoundException {
285e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollForward(FLING_STEPS);
286e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
287e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
288e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
289e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * A convenience version of {@link UiScrollable#scrollForward(int)}, performs a regular scroll
290e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
291e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if scrolled and false if can't scroll anymore
292e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
2936b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollForward() throws UiObjectNotFoundException {
294e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollForward(SCROLL_STEPS);
295e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
296e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
297e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
298e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform a scroll forward. If this list is set to vertical (see {@link #setAsVerticalList()}
299e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * default) then the swipes will be executed from the bottom to top. If this list is set
300e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * to horizontal (see {@link #setAsHorizontalList()}) then the swipes will be executed from
3013d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * the right to left. Caution is required on devices configured with right to left languages
3023d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * like Arabic and Hebrew.
303e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
304e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param steps use steps to control the speed, so that it may be a scroll, or fling
305e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if scrolled and false if can't scroll anymore
306e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
3076b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollForward(int steps) throws UiObjectNotFoundException {
308e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Log.d(LOG_TAG, "scrollForward() on selector = " + getSelector());
309e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
310e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
3116b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
312e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
313e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = new Rect();;
314e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        node.getBoundsInScreen(rect);
315e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
316e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int downX = 0;
317e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int downY = 0;
318e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int upX = 0;
319e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int upY = 0;
320e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
321e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // scrolling is by default assumed vertically unless the object is explicitly
322e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // set otherwise by setAsHorizontalContainer()
323e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(mIsVerticalList) {
324e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            int swipeAreaAdjust = (int)(rect.height() * getSwipeDeadZonePercentage());
325e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // scroll vertically: swipe down -> up
326e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downX = rect.centerX();
327e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downY = rect.bottom - swipeAreaAdjust;
328e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upX = rect.centerX();
329e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upY = rect.top + swipeAreaAdjust;
330e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } else {
331e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            int swipeAreaAdjust = (int)(rect.width() * getSwipeDeadZonePercentage());
332e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // scroll horizontally: swipe right -> left
333e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // TODO: Assuming device is not in right to left language
334e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downX = rect.right - swipeAreaAdjust;
335e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downY = rect.centerY();
336e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upX = rect.left + swipeAreaAdjust;
337e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upY = rect.centerY();
338e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
339e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().scrollSwipe(downX, downY, upX, upY, steps);
340e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
341e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
342e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3433d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link UiScrollable#scrollBackward(int)}
344e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
345e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if scrolled and false if can't scroll anymore
346e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
3476b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean flingBackward() throws UiObjectNotFoundException {
348e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollBackward(FLING_STEPS);
349e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
350e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
351e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3523d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link UiScrollable#scrollBackward(int)}
353e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
354e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if scrolled and false if can't scroll anymore
355e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
3566b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollBackward() throws UiObjectNotFoundException {
357e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollBackward(SCROLL_STEPS);
358e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
359e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
360e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
361e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform a scroll backward. If this list is set to vertical (see {@link #setAsVerticalList()}
362e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * default) then the swipes will be executed from the top to bottom. If this list is set
363e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * to horizontal (see {@link #setAsHorizontalList()}) then the swipes will be executed from
3643d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * the left to right. Caution is required on devices configured with right to left languages
3653d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * like Arabic and Hebrew.
366e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
367e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param steps use steps to control the speed, so that it may be a scroll, or fling
368e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if scrolled and false if can't scroll anymore
369e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
3706b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollBackward(int steps) throws UiObjectNotFoundException {
371e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Log.d(LOG_TAG, "scrollBackward() on selector = " + getSelector());
372e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
3736b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu        if (node == null) {
3746b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
375e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
376e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Rect rect = new Rect();;
377e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        node.getBoundsInScreen(rect);
378e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
379e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int downX = 0;
380e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int downY = 0;
381e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int upX = 0;
382e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int upY = 0;
383e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
384e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // scrolling is by default assumed vertically unless the object is explicitly
385e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // set otherwise by setAsHorizontalContainer()
386e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(mIsVerticalList) {
387e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            int swipeAreaAdjust = (int)(rect.height() * getSwipeDeadZonePercentage());
388e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            Log.d(LOG_TAG, "scrollToBegining() using vertical scroll");
389e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // scroll vertically: swipe up -> down
390e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downX = rect.centerX();
391e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downY = rect.top + swipeAreaAdjust;
392e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upX = rect.centerX();
393e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upY = rect.bottom - swipeAreaAdjust;
394e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } else {
395e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            int swipeAreaAdjust = (int)(rect.width() * getSwipeDeadZonePercentage());
396e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            Log.d(LOG_TAG, "scrollToBegining() using hotizontal scroll");
397e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // scroll horizontally: swipe left -> right
398e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // TODO: Assuming device is not in right to left language
399e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downX = rect.left + swipeAreaAdjust;
400e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downY = rect.centerY();
401e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upX = rect.right - swipeAreaAdjust;
402e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upY = rect.centerY();
403e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
404e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().scrollSwipe(downX, downY, upX, upY, steps);
405e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
406e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
407e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
408e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Scrolls to the beginning of a scrollable UI element. The beginning could be the top most
4093d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * in case of vertical lists or the left most in case of horizontal lists. Caution is required
4103d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * on devices configured with right to left languages like Arabic and Hebrew.
411e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
412e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param steps use steps to control the speed, so that it may be a scroll, or fling
413e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on scrolled else false
414e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
4156b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollToBeginning(int maxSwipes, int steps) throws UiObjectNotFoundException {
416e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Log.d(LOG_TAG, "scrollToBeginning() on selector = " + getSelector());
417e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // protect against potential hanging and return after preset attempts
418e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        for(int x = 0; x < maxSwipes; x++) {
419e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(!scrollBackward(steps)) {
420e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                break;
421e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
422e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
423e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return true;
424e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
425e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
426e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4273d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link UiScrollable#scrollToBeginning(int, int)}
428e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
429e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param maxSwipes
430e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on scrolled else false
431e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
4326b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollToBeginning(int maxSwipes) throws UiObjectNotFoundException {
433e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollToBeginning(maxSwipes, SCROLL_STEPS);
434e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
435e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
436e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4373d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link UiScrollable#scrollToBeginning(int, int)}
438e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
439e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param maxSwipes
440e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on scrolled else false
441e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
4426b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean flingToBeginning(int maxSwipes) throws UiObjectNotFoundException {
443e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollToBeginning(maxSwipes, FLING_STEPS);
444e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
445e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
446e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
447e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Scrolls to the end of a scrollable UI element. The end could be the bottom most
4483d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * in case of vertical controls or the right most for horizontal controls. Caution
4493d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * is required on devices configured with right to left languages like Arabic and Hebrew.
450e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
451e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param steps use steps to control the speed, so that it may be a scroll, or fling
452e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on scrolled else false
453e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
4546b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollToEnd(int maxSwipes, int steps) throws UiObjectNotFoundException {
455e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // protect against potential hanging and return after preset attempts
456e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        for(int x = 0; x < maxSwipes; x++) {
457e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(!scrollForward(steps)) {
458e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                break;
459e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
460e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
461e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return true;
462e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
463e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
464e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4653d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link UiScrollable#scrollToEnd(int, int)
466e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
467e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param maxSwipes
468e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on scrolled else false
469e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
4706b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollToEnd(int maxSwipes) throws UiObjectNotFoundException {
471e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollToEnd(maxSwipes, SCROLL_STEPS);
472e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
473e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
474e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4753d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link UiScrollable#scrollToEnd(int, int)}
476e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
477e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param maxSwipes
478e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on scrolled else false
479e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
4806b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean flingToEnd(int maxSwipes) throws UiObjectNotFoundException {
481e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollToEnd(maxSwipes, FLING_STEPS);
482e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
483e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
484a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz    /**
485a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * Returns the percentage of a widget's size that's considered as no touch zone when swiping
486a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     *
487a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * Dead zones are set as percentage of a widget's total width or height where
488a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * swipe start point cannot start or swipe end point cannot end. It is like a margin
489a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * around the actual dimensions of the widget. Swipes will always be start and
490a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * end inside this margin.
491a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     *
492a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * This is important when the widget being swiped may not respond to the swipe if
493a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * started at a point too near to the edge. The default is 10% from either edge.
494a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     *
495a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * @return a value between 0 and 1
496a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     */
497e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public double getSwipeDeadZonePercentage() {
498e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mSwipeDeadZonePercentage;
499e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
500e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
501a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz    /**
502a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * Sets the percentage of a widget's size that's considered as no touch zone when swiping
503a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     *
504a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * Dead zones are set as percentage of a widget's total width or height where
505a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * swipe start point cannot start or swipe end point cannot end. It is like a margin
506a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * around the actual dimensions of the widget. Swipes will always start and
507a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * end inside this margin.
508a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     *
509a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * This is important when the widget being swiped may not respond to the swipe if
510a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * started at a point too near to the edge. The default is 10% from either edge
511a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     *
512a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * @param swipeDeadZonePercentage is a value between 0 and 1
513ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * @return reference to itself
514a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     */
515ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz    public UiScrollable setSwipeDeadZonePercentage(double swipeDeadZonePercentage) {
516e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mSwipeDeadZonePercentage = swipeDeadZonePercentage;
517ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz        return this;
518e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
519e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu}
520