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.
25dbba713661688a285e701a006ce2d199296ac328Guang Zhu * @since API Level 16
26e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu */
27e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhupublic class UiScrollable extends UiCollection {
28e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final String LOG_TAG = UiScrollable.class.getSimpleName();
29e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
30e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // More steps slows the swipe and prevents contents from being flung too far
31e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final int SCROLL_STEPS = 55;
32e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
33e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final int FLING_STEPS = 5;
34e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
35e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // Restrict a swipe's starting and ending points inside a 10% margin of the target
36e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static final double DEFAULT_SWIPE_DEADZONE_PCT = 0.1;
37e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
38e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // Limits the number of swipes/scrolls performed during a search
39e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private static int mMaxSearchSwipes = 30;
40e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
41e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    // Used in ScrollForward() and ScrollBackward() to determine swipe direction
42f612e6a05f47b28ae0f5715545658c08dd759dd7Guang Zhu    private boolean mIsVerticalList = true;
43e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
44e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    private double mSwipeDeadZonePercentage = DEFAULT_SWIPE_DEADZONE_PCT;
45e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
46e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
473d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * UiScrollable is a {@link UiCollection} and as such requires a {@link UiSelector} to
483d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * identify the container UI element of the scrollable collection. Further operations on
493d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * the items in the container will require specifying UiSelector as an item selector.
503d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
514ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param container a {@link UiSelector} selector
52dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
53e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
544ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiScrollable(UiSelector container) {
55e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // wrap the container selector with container so that QueryController can handle
56e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // this type of enumeration search accordingly
57e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        super(container);
58e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
59e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
60e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
61e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Set the direction of swipes when performing scroll search
62ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * @return reference to itself
63dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
64e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
65ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz    public UiScrollable setAsVerticalList() {
66f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
67e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mIsVerticalList = true;
68ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz        return this;
69e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
70e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
71e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
72e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Set the direction of swipes when performing scroll search
73ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * @return reference to itself
74dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
75e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
76ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz    public UiScrollable setAsHorizontalList() {
77f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
78e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mIsVerticalList = false;
79ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz        return this;
80e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
81e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
82e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
83e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Used privately when performing swipe searches to decide if an element has become
84e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * visible or not.
853d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
86e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param selector
87e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if found else false
88dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
89e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
904ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    protected boolean exists(UiSelector selector) {
91e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(getQueryController().findAccessibilityNodeInfo(selector) != null) {
92e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return true;
93e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
94e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
95e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
96e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
97e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
984ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * Searches for child UI element within the constraints of this UiScrollable {@link UiSelector}
993d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * container. It looks for any child matching the <code>childPattern</code> argument within its
1003d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * hierarchy with a matching content-description text. The returned UiObject will represent the
1013d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * UI element matching the <code>childPattern</code> and not the sub element that matched the
1023d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * content description.</p>
1033d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * By default this operation will perform scroll search while attempting to find the UI element
1044ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * See {@link #getChildByDescription(UiSelector, String, boolean)}
1053d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1064ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param childPattern {@link UiSelector} selector of the child pattern to match and return
107e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param text String of the identifying child contents of of the <code>childPattern</code>
108e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link UiObject} pointing at and instance of <code>childPattern</code>
109e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
110dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
111e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
112e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    @Override
1134ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChildByDescription(UiSelector childPattern, String text)
114e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throws UiObjectNotFoundException {
115f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(childPattern, text);
116e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getChildByDescription(childPattern, text, true);
117e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
118e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
119e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1203d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link #getChildByDescription(UiSelector, String)}
1213d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1224ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param childPattern {@link UiSelector} selector of the child pattern to match and return
123e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param text String may be a partial match for the content-description of a child element.
124e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param allowScrollSearch set to true if scrolling is allowed
125e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link UiObject} pointing at and instance of <code>childPattern</code>
126e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
127dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
128e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
1294ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChildByDescription(UiSelector childPattern, String text,
1304ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz            boolean allowScrollSearch) throws UiObjectNotFoundException {
131f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(childPattern, text, allowScrollSearch);
132e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (text != null) {
133e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (allowScrollSearch) {
1344ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz                scrollIntoView(new UiSelector().descriptionContains(text));
135e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
136e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return super.getChildByDescription(childPattern, text);
137e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
138e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        throw new UiObjectNotFoundException("for description= \"" + text + "\"");
139e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
140e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
141e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1424ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * Searches for child UI element within the constraints of this UiScrollable {@link UiSelector}
143e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * selector. It looks for any child matching the <code>childPattern</code> argument and
144e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * return the <code>instance</code> specified. The operation is performed only on the visible
145e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * items and no scrolling is performed in this case.
1463d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1474ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param childPattern {@link UiSelector} selector of the child pattern to match and return
148e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param instance int the desired matched instance of this <code>childPattern</code>
149e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link UiObject} pointing at and instance of <code>childPattern</code>
150dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
151e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
152e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    @Override
1534ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChildByInstance(UiSelector childPattern, int instance)
154e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throws UiObjectNotFoundException {
155f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(childPattern, instance);
1564ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        UiSelector patternSelector = UiSelector.patternBuilder(getSelector(),
1574ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz                UiSelector.patternBuilder(childPattern).instance(instance));
158e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return new UiObject(patternSelector);
159e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
160e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
161e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1624ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * Searches for child UI element within the constraints of this UiScrollable {@link UiSelector}
1633d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * container. It looks for any child matching the <code>childPattern</code> argument that has
1643d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * a sub UI element anywhere within its sub hierarchy that has text attribute
165e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * <code>text</code>. The returned UiObject will point at the <code>childPattern</code>
1663d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * instance that matched the search and not at the text matched sub element</p>
167e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * By default this operation will perform scroll search while attempting to find the UI
168e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * element.
1694ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * See {@link #getChildByText(UiSelector, String, boolean)}
1703d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1714ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param childPattern {@link UiSelector} selector of the child pattern to match and return
172e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param text String of the identifying child contents of of the <code>childPattern</code>
173e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link UiObject} pointing at and instance of <code>childPattern</code>
174e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
175dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
176e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
177e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    @Override
1784ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChildByText(UiSelector childPattern, String text)
179e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throws UiObjectNotFoundException {
180f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(childPattern, text);
181e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getChildByText(childPattern, text, true);
182e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
183e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
184e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
1853d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link #getChildByText(UiSelector, String)}
1863d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
1874ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param childPattern {@link UiSelector} selector of the child pattern to match and return
188e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param text String of the identifying child contents of of the <code>childPattern</code>
189e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param allowScrollSearch set to true if scrolling is allowed
190e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return {@link UiObject} pointing at and instance of <code>childPattern</code>
191e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @throws UiObjectNotFoundException
192dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
193e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
1944ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz    public UiObject getChildByText(UiSelector childPattern, String text, boolean allowScrollSearch)
195e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            throws UiObjectNotFoundException {
196f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(childPattern, text, allowScrollSearch);
197e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (text != null) {
198e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (allowScrollSearch) {
1994ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz                scrollIntoView(new UiSelector().text(text));
200e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
201e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return super.getChildByText(childPattern, text);
202e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
203e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        throw new UiObjectNotFoundException("for text= \"" + text + "\"");
204e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
205e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
206e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2073d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Performs a swipe Up on the UI element until the requested content-description
2083d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * is visible or until swipe attempts have been exhausted. See {@link #setMaxSearchSwipes(int)}
2093d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
210e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param text to look for anywhere within the contents of this scrollable.
211e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if item us found else false
212dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
213e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
2146b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollDescriptionIntoView(String text) throws UiObjectNotFoundException {
215f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(text);
2164ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return scrollIntoView(new UiSelector().description(text));
217e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
218e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
219e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2204ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * Perform a scroll search for a UI element matching the {@link UiSelector} selector argument.
2213d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link #scrollDescriptionIntoView(String)} and {@link #scrollTextIntoView(String)}.
2223d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
223ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * @param obj {@link UiObject}
224ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * @return true if the item was found and now is in view else false
225dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
226ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     */
227ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz    public boolean scrollIntoView(UiObject obj) throws UiObjectNotFoundException {
228f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(obj.getSelector());
229ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz        return scrollIntoView(obj.getSelector());
230ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz    }
231ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz
232ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz    /**
233ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * Perform a scroll search for a UI element matching the {@link UiSelector} selector argument.
234ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * See {@link #scrollDescriptionIntoView(String)} and {@link #scrollTextIntoView(String)}.
235ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     *
2364ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz     * @param selector {@link UiSelector} selector
237e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if the item was found and now is in view else false
238dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
239e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
2406b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollIntoView(UiSelector selector) throws UiObjectNotFoundException {
241f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(selector);
242e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // if we happen to be on top of the text we want then return here
243e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if (exists(getSelector().childSelector(selector))) {
244e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            return (true);
245e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } else {
246e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // we will need to reset the search from the beginning to start search
247e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            scrollToBeginning(mMaxSearchSwipes);
248e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if (exists(getSelector().childSelector(selector))) {
249e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                return (true);
250e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
251e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            for (int x = 0; x < mMaxSearchSwipes; x++) {
252e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if(!scrollForward()) {
253e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    return false;
254e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
255e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
256e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                if(exists(getSelector().childSelector(selector))) {
257e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                    return true;
258e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                }
259e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
260e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
261e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return false;
262e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
263e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
264e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2653d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * Performs a swipe up on the UI element until the requested text is visible
2663d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * or until swipe attempts have been exhausted. See {@link #setMaxSearchSwipes(int)}
2673d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
268e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param text to look for
269e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if item us found else false
270dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
271e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
2726b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollTextIntoView(String text) throws UiObjectNotFoundException {
273f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(text);
2744ab790eccf6d5c27f542056b87d26d38f7caeeb3Adam Momtaz        return scrollIntoView(new UiSelector().text(text));
275e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
276e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
277e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2781893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link #getChildByDescription(UiSelector, String)} and
2791893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link #getChildByText(UiSelector, String)} use an arguments that specifies if scrolling is
2801893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * allowed while searching for the UI element.  The number of scrolls allowed to perform a
2811893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * search can be modified by this method.  The current value can be read by calling
2821893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link #getMaxSearchSwipes()}
2833d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
2843d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * @param swipes is the number of search swipes until abort
285ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * @return reference to itself
286dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
287e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
288ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz    public UiScrollable setMaxSearchSwipes(int swipes) {
289f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(swipes);
290e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mMaxSearchSwipes = swipes;
291ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz        return this;
292e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
293e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
294e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
2951893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link #getChildByDescription(UiSelector, String)} and
2961893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * {@link #getChildByText(UiSelector, String)} use an arguments that specifies if scrolling is
2971893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * allowed while searching for the UI element.  The number of scrolls currently allowed to
2981893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * perform a search can be read by this method.
299e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * See {@link #setMaxSearchSwipes(int)}
3003d50587be8ff021369c90554d814839335b445b0Adam Momtaz     *
301e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return max value of the number of swipes currently allowed during a scroll search
302dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
303e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
304e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public int getMaxSearchSwipes() {
305f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
306e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mMaxSearchSwipes;
307e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
308e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
309e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
310e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * A convenience version of {@link UiScrollable#scrollForward(int)}, performs a fling
311e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
312e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if scrolled and false if can't scroll anymore
313dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
314e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
3156b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean flingForward() throws UiObjectNotFoundException {
316f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
317e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollForward(FLING_STEPS);
318e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
319e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
320e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
321e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * A convenience version of {@link UiScrollable#scrollForward(int)}, performs a regular scroll
322e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
323e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if scrolled and false if can't scroll anymore
324dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
325e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
3266b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollForward() throws UiObjectNotFoundException {
327f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
328e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollForward(SCROLL_STEPS);
329e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
330e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
331e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
332e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform a scroll forward. If this list is set to vertical (see {@link #setAsVerticalList()}
333e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * default) then the swipes will be executed from the bottom to top. If this list is set
334e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * to horizontal (see {@link #setAsHorizontalList()}) then the swipes will be executed from
3353d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * the right to left. Caution is required on devices configured with right to left languages
3363d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * like Arabic and Hebrew.
337e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
338e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param steps use steps to control the speed, so that it may be a scroll, or fling
339e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if scrolled and false if can't scroll anymore
340dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
341e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
3426b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollForward(int steps) throws UiObjectNotFoundException {
343f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(steps);
344e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Log.d(LOG_TAG, "scrollForward() on selector = " + getSelector());
345e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
346e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(node == null) {
3476b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
348e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
3491893caed0ad4e73b0676f206282d490c2d345316Thanh Le        Rect rect = new Rect();
350e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        node.getBoundsInScreen(rect);
351e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
352e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int downX = 0;
353e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int downY = 0;
354e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int upX = 0;
355e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int upY = 0;
356e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
357e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // scrolling is by default assumed vertically unless the object is explicitly
358e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // set otherwise by setAsHorizontalContainer()
359e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(mIsVerticalList) {
360e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            int swipeAreaAdjust = (int)(rect.height() * getSwipeDeadZonePercentage());
361e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // scroll vertically: swipe down -> up
362e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downX = rect.centerX();
363e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downY = rect.bottom - swipeAreaAdjust;
364e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upX = rect.centerX();
365e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upY = rect.top + swipeAreaAdjust;
366e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } else {
367e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            int swipeAreaAdjust = (int)(rect.width() * getSwipeDeadZonePercentage());
368e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // scroll horizontally: swipe right -> left
369e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // TODO: Assuming device is not in right to left language
370e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downX = rect.right - swipeAreaAdjust;
371e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downY = rect.centerY();
372e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upX = rect.left + swipeAreaAdjust;
373e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upY = rect.centerY();
374e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
375e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().scrollSwipe(downX, downY, upX, upY, steps);
376e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
377e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
378e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3793d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link UiScrollable#scrollBackward(int)}
380e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
381e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if scrolled and false if can't scroll anymore
382dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
383e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
3846b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean flingBackward() throws UiObjectNotFoundException {
385f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
386e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollBackward(FLING_STEPS);
387e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
388e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
389e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
3903d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link UiScrollable#scrollBackward(int)}
391e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
392e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if scrolled and false if can't scroll anymore
393dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
394e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
3956b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollBackward() throws UiObjectNotFoundException {
396f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
397e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollBackward(SCROLL_STEPS);
398e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
399e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
400e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
401e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Perform a scroll backward. If this list is set to vertical (see {@link #setAsVerticalList()}
402e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * default) then the swipes will be executed from the top to bottom. If this list is set
403e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * to horizontal (see {@link #setAsHorizontalList()}) then the swipes will be executed from
4043d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * the left to right. Caution is required on devices configured with right to left languages
4053d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * like Arabic and Hebrew.
406e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
407e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param steps use steps to control the speed, so that it may be a scroll, or fling
408e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true if scrolled and false if can't scroll anymore
409dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
410e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
4116b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollBackward(int steps) throws UiObjectNotFoundException {
412f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(steps);
413e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Log.d(LOG_TAG, "scrollBackward() on selector = " + getSelector());
414e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
4156b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu        if (node == null) {
4166b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
417e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
4181893caed0ad4e73b0676f206282d490c2d345316Thanh Le        Rect rect = new Rect();
419e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        node.getBoundsInScreen(rect);
420e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
421e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int downX = 0;
422e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int downY = 0;
423e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int upX = 0;
424e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        int upY = 0;
425e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
426e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // scrolling is by default assumed vertically unless the object is explicitly
427e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // set otherwise by setAsHorizontalContainer()
428e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        if(mIsVerticalList) {
429e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            int swipeAreaAdjust = (int)(rect.height() * getSwipeDeadZonePercentage());
430e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            Log.d(LOG_TAG, "scrollToBegining() using vertical scroll");
431e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // scroll vertically: swipe up -> down
432e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downX = rect.centerX();
433e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downY = rect.top + swipeAreaAdjust;
434e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upX = rect.centerX();
435e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upY = rect.bottom - swipeAreaAdjust;
436e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        } else {
437e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            int swipeAreaAdjust = (int)(rect.width() * getSwipeDeadZonePercentage());
438e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            Log.d(LOG_TAG, "scrollToBegining() using hotizontal scroll");
439e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // scroll horizontally: swipe left -> right
440e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            // TODO: Assuming device is not in right to left language
441e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downX = rect.left + swipeAreaAdjust;
442e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            downY = rect.centerY();
443e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upX = rect.right - swipeAreaAdjust;
444e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            upY = rect.centerY();
445e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
446e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return getInteractionController().scrollSwipe(downX, downY, upX, upY, steps);
447e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
448e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
449e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
450e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Scrolls to the beginning of a scrollable UI element. The beginning could be the top most
4513d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * in case of vertical lists or the left most in case of horizontal lists. Caution is required
4523d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * on devices configured with right to left languages like Arabic and Hebrew.
453e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
454e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param steps use steps to control the speed, so that it may be a scroll, or fling
455e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on scrolled else false
456dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
457e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
4586b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollToBeginning(int maxSwipes, int steps) throws UiObjectNotFoundException {
459f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(maxSwipes, steps);
460e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        Log.d(LOG_TAG, "scrollToBeginning() on selector = " + getSelector());
461e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // protect against potential hanging and return after preset attempts
462e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        for(int x = 0; x < maxSwipes; x++) {
463e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(!scrollBackward(steps)) {
464e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                break;
465e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
466e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
467e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return true;
468e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
469e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
470e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4713d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link UiScrollable#scrollToBeginning(int, int)}
472e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
473e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param maxSwipes
474e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on scrolled else false
475dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
476e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
4776b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollToBeginning(int maxSwipes) throws UiObjectNotFoundException {
478f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(maxSwipes);
479e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollToBeginning(maxSwipes, SCROLL_STEPS);
480e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
481e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
482e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
4833d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link UiScrollable#scrollToBeginning(int, int)}
484e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
485e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param maxSwipes
486e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on scrolled else false
487dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
488e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
4896b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean flingToBeginning(int maxSwipes) throws UiObjectNotFoundException {
490f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(maxSwipes);
491e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollToBeginning(maxSwipes, FLING_STEPS);
492e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
493e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
494e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
495e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * Scrolls to the end of a scrollable UI element. The end could be the bottom most
4963d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * in case of vertical controls or the right most for horizontal controls. Caution
4973d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * is required on devices configured with right to left languages like Arabic and Hebrew.
498e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
499e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param steps use steps to control the speed, so that it may be a scroll, or fling
500e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on scrolled else false
501dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
502e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
5036b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollToEnd(int maxSwipes, int steps) throws UiObjectNotFoundException {
504f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(maxSwipes, steps);
505e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        // protect against potential hanging and return after preset attempts
506e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        for(int x = 0; x < maxSwipes; x++) {
507e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            if(!scrollForward(steps)) {
508e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu                break;
509e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu            }
510e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        }
511e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return true;
512e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
513e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
514e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5151893caed0ad4e73b0676f206282d490c2d345316Thanh Le     * See {@link UiScrollable#scrollToEnd(int, int)}
516e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
517e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param maxSwipes
518e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on scrolled else false
519dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
520e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
5216b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean scrollToEnd(int maxSwipes) throws UiObjectNotFoundException {
522f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(maxSwipes);
523e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollToEnd(maxSwipes, SCROLL_STEPS);
524e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
525e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
526e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    /**
5273d50587be8ff021369c90554d814839335b445b0Adam Momtaz     * See {@link UiScrollable#scrollToEnd(int, int)}
528e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     *
529e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @param maxSwipes
530e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     * @return true on scrolled else false
531dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
532e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu     */
5336b6adccb07300778cd665ca83c0e3672d97de41dGuang Zhu    public boolean flingToEnd(int maxSwipes) throws UiObjectNotFoundException {
534f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(maxSwipes);
535e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return scrollToEnd(maxSwipes, FLING_STEPS);
536e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
537e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
538a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz    /**
539a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * Returns the percentage of a widget's size that's considered as no touch zone when swiping
540a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     *
541a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * Dead zones are set as percentage of a widget's total width or height where
542a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * swipe start point cannot start or swipe end point cannot end. It is like a margin
543a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * around the actual dimensions of the widget. Swipes will always be start and
544a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * end inside this margin.
545a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     *
546a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * This is important when the widget being swiped may not respond to the swipe if
547a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * started at a point too near to the edge. The default is 10% from either edge.
548a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     *
549a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * @return a value between 0 and 1
550dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
551a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     */
552e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    public double getSwipeDeadZonePercentage() {
553f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace();
554e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        return mSwipeDeadZonePercentage;
555e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
556e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu
557a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz    /**
558a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * Sets the percentage of a widget's size that's considered as no touch zone when swiping
559a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     *
560a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * Dead zones are set as percentage of a widget's total width or height where
561a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * swipe start point cannot start or swipe end point cannot end. It is like a margin
562a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * around the actual dimensions of the widget. Swipes will always start and
563a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * end inside this margin.
564a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     *
565a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * This is important when the widget being swiped may not respond to the swipe if
566a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * started at a point too near to the edge. The default is 10% from either edge
567a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     *
568a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     * @param swipeDeadZonePercentage is a value between 0 and 1
569ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz     * @return reference to itself
570dbba713661688a285e701a006ce2d199296ac328Guang Zhu     * @since API Level 16
571a46ac3504426564954292b13380120e8cc6b1527Adam Momtaz     */
572ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz    public UiScrollable setSwipeDeadZonePercentage(double swipeDeadZonePercentage) {
573f406deb12db531785300f04e219fef28ef60b126Maxim Siniavine        Tracer.trace(swipeDeadZonePercentage);
574e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu        mSwipeDeadZonePercentage = swipeDeadZonePercentage;
575ee4b48cd50f6e4e24ba7f538b8a77c839c1087f5Adam Momtaz        return this;
576e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu    }
577e54d649fb83a0a44516e5c25a9ac1992c8950e59Guang Zhu}
578