118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu/*
218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Copyright (C) 2012 The Android Open Source Project
318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu *
418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Licensed under the Apache License, Version 2.0 (the "License");
518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * you may not use this file except in compliance with the License.
618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * You may obtain a copy of the License at
718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu *
818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu *      http://www.apache.org/licenses/LICENSE-2.0
918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu *
1018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * Unless required by applicable law or agreed to in writing, software
1118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * distributed under the License is distributed on an "AS IS" BASIS,
1218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * See the License for the specific language governing permissions and
1418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * limitations under the License.
1518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */
1618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhupackage com.android.uiautomator.core;
1718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
1818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.graphics.Rect;
1918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.util.Log;
2018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhuimport android.view.accessibility.AccessibilityNodeInfo;
2118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
2218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu/**
2318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * UiScrollable is a {@link UiCollection} and provides support for searching
2418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * for items in scrollable layout elements. This class can be used with
2518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * horizontally or vertically scrollable controls.
2618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu * @since API Level 16
2718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu */
2818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhupublic class UiScrollable extends UiCollection {
2918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static final String LOG_TAG = UiScrollable.class.getSimpleName();
3018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
3118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    // More steps slows the swipe and prevents contents from being flung too far
3218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static final int SCROLL_STEPS = 55;
3318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
3418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static final int FLING_STEPS = 5;
3518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
3618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    // Restrict a swipe's starting and ending points inside a 10% margin of the target
3718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static final double DEFAULT_SWIPE_DEADZONE_PCT = 0.1;
3818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
3918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    // Limits the number of swipes/scrolls performed during a search
4018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private static int mMaxSearchSwipes = 30;
4118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
4218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    // Used in ScrollForward() and ScrollBackward() to determine swipe direction
4318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private boolean mIsVerticalList = true;
4418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
4518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    private double mSwipeDeadZonePercentage = DEFAULT_SWIPE_DEADZONE_PCT;
4618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
4718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
4818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Constructor.
4918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
5018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param container a {@link UiSelector} selector to identify the scrollable
5118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *     layout element.
5218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
5318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
5418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public UiScrollable(UiSelector container) {
5518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // wrap the container selector with container so that QueryController can handle
5618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // this type of enumeration search accordingly
5718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        super(container);
5818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
5918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
6018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
6118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Set the direction of swipes to be vertical when performing scroll actions.
6218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return reference to itself
6318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
6418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
6518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public UiScrollable setAsVerticalList() {
6618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace();
6718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mIsVerticalList = true;
6818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return this;
6918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
7018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
7118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
7218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Set the direction of swipes to be horizontal when performing scroll actions.
7318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return reference to itself
7418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
7518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
7618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public UiScrollable setAsHorizontalList() {
7718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace();
7818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mIsVerticalList = false;
7918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return this;
8018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
8118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
8218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
8318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Used privately when performing swipe searches to decide if an element has become
8418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * visible or not.
8518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
8618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param selector
8718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if found else false
8818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
8918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
9018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    protected boolean exists(UiSelector selector) {
9118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if(getQueryController().findAccessibilityNodeInfo(selector) != null) {
9218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return true;
9318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
9418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return false;
9518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
9618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
9718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
9818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Searches for a child element in the present scrollable container.
9918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * The search first looks for a child element that matches the selector
10018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * you provided, then looks for the content-description in its children elements.
10118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * If both search conditions are fulfilled, the method returns a {@ link UiObject}
10218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * representing the element matching the selector (not the child element in its
10318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * subhierarchy containing the content-description). By default, this method performs a
10418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * scroll search.
10518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * See {@link #getChildByDescription(UiSelector, String, boolean)}
10618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
10718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param childPattern {@link UiSelector} for a child in a scollable layout element
10818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param text Content-description to find in the children of
10918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * the <code>childPattern</code> match
11018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return {@link UiObject} representing the child element that matches the search conditions
11118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @throws UiObjectNotFoundException
11218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
11318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
11418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    @Override
11518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public UiObject getChildByDescription(UiSelector childPattern, String text)
11618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            throws UiObjectNotFoundException {
11718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(childPattern, text);
11818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return getChildByDescription(childPattern, text, true);
11918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
12018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
12118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
12218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Searches for a child element in the present scrollable container.
12318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * The search first looks for a child element that matches the selector
12418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * you provided, then looks for the content-description in its children elements.
12518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * If both search conditions are fulfilled, the method returns a {@ link UiObject}
12618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * representing the element matching the selector (not the child element in its
12718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * subhierarchy containing the content-description).
12818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
12918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param childPattern {@link UiSelector} for a child in a scollable layout element
13018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param text Content-description to find in the children of
13118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * the <code>childPattern</code> match (may be a partial match)
13218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param allowScrollSearch set to true if scrolling is allowed
13318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return {@link UiObject} representing the child element that matches the search conditions
13418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @throws UiObjectNotFoundException
13518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
13618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
13718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public UiObject getChildByDescription(UiSelector childPattern, String text,
13818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            boolean allowScrollSearch) throws UiObjectNotFoundException {
13918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(childPattern, text, allowScrollSearch);
14018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (text != null) {
14118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if (allowScrollSearch) {
14218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                scrollIntoView(new UiSelector().descriptionContains(text));
14318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
14418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return super.getChildByDescription(childPattern, text);
14518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
14618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        throw new UiObjectNotFoundException("for description= \"" + text + "\"");
14718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
14818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
14918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
15018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Searches for a child element in the present scrollable container that
15118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * matches the selector you provided. The search is performed without
15218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * scrolling and only on visible elements.
15318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
15418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param childPattern {@link UiSelector} for a child in a scollable layout element
15518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param instance int number representing the occurance of
15618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * a <code>childPattern</code> match
15718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return {@link UiObject} representing the child element that matches the search conditions
15818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
15918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
16018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    @Override
16118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public UiObject getChildByInstance(UiSelector childPattern, int instance)
16218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            throws UiObjectNotFoundException {
16318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(childPattern, instance);
16418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        UiSelector patternSelector = UiSelector.patternBuilder(getSelector(),
16518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                UiSelector.patternBuilder(childPattern).instance(instance));
16618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return new UiObject(patternSelector);
16718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
16818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
16918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
17018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Searches for a child element in the present scrollable
17118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * container. The search first looks for a child element that matches the
17218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * selector you provided, then looks for the text in its children elements.
17318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * If both search conditions are fulfilled, the method returns a {@ link UiObject}
17418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * representing the element matching the selector (not the child element in its
17518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * subhierarchy containing the text). By default, this method performs a
17618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * scroll search.
17718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * See {@link #getChildByText(UiSelector, String, boolean)}
17818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
17918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param childPattern {@link UiSelector} selector for a child in a scrollable layout element
18018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param text String to find in the children of the <code>childPattern</code> match
18118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return {@link UiObject} representing the child element that matches the search conditions
18218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @throws UiObjectNotFoundException
18318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
18418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
18518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    @Override
18618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public UiObject getChildByText(UiSelector childPattern, String text)
18718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            throws UiObjectNotFoundException {
18818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(childPattern, text);
18918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return getChildByText(childPattern, text, true);
19018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
19118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
19218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
19318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Searches for a child element in the present scrollable container. The
19418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * search first looks for a child element that matches the
19518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * selector you provided, then looks for the text in its children elements.
19618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * If both search conditions are fulfilled, the method returns a {@ link UiObject}
19718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * representing the element matching the selector (not the child element in its
19818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * subhierarchy containing the text).
19918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
20018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param childPattern {@link UiSelector} selector for a child in a scrollable layout element
20118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param text String to find in the children of the <code>childPattern</code> match
20218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param allowScrollSearch set to true if scrolling is allowed
20318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return {@link UiObject} representing the child element that matches the search conditions
20418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @throws UiObjectNotFoundException
20518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
20618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
20718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public UiObject getChildByText(UiSelector childPattern, String text, boolean allowScrollSearch)
20818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            throws UiObjectNotFoundException {
20918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(childPattern, text, allowScrollSearch);
21018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (text != null) {
21118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if (allowScrollSearch) {
21218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                scrollIntoView(new UiSelector().text(text));
21318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
21418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return super.getChildByText(childPattern, text);
21518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
21618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        throw new UiObjectNotFoundException("for text= \"" + text + "\"");
21718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
21818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
21918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
22018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Performs a forward scroll action on the scrollable layout element until
22118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * the content-description is found, or until swipe attempts have been exhausted.
22218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * See {@link #setMaxSearchSwipes(int)}
22318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
22418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param text content-description to find within the contents of this scrollable layout element.
22518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if item is found; else, false
22618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
22718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
22818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean scrollDescriptionIntoView(String text) throws UiObjectNotFoundException {
22918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(text);
23018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return scrollIntoView(new UiSelector().description(text));
23118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
23218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
23318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
23418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Perform a forward scroll action to move through the scrollable layout element until
23518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * a visible item that matches the {@link UiObject} is found.
23618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
23718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param obj {@link UiObject}
23818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if the item was found and now is in view else false
23918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
24018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
24118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean scrollIntoView(UiObject obj) throws UiObjectNotFoundException {
24218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(obj.getSelector());
24318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return scrollIntoView(obj.getSelector());
24418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
24518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
24618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
24718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Perform a scroll forward action to move through the scrollable layout
24818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * element until a visible item that matches the selector is found.
24918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
25018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * See {@link #scrollDescriptionIntoView(String)} and {@link #scrollTextIntoView(String)}.
25118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
25218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param selector {@link UiSelector} selector
25318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if the item was found and now is in view; else, false
25418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
25518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
25618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean scrollIntoView(UiSelector selector) throws UiObjectNotFoundException {
25718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(selector);
25818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // if we happen to be on top of the text we want then return here
25918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        UiSelector childSelector = getSelector().childSelector(selector);
26018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (exists(childSelector)) {
26118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return (true);
26218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        } else {
26318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // we will need to reset the search from the beginning to start search
26418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            scrollToBeginning(mMaxSearchSwipes);
26518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if (exists(childSelector)) {
26618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                return (true);
26718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
26818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            for (int x = 0; x < mMaxSearchSwipes; x++) {
26918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                boolean scrolled = scrollForward();
27018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                if(exists(childSelector)) {
27118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    return true;
27218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                }
27318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                if (!scrolled) {
27418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                    return false;
27518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                }
27618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
27718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
27818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return false;
27918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
28018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
28118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
28218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Scrolls forward until the UiObject is fully visible in the scrollable container.
28318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Use this method to make sure that the child item's edges are not offscreen.
28418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
28518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param childObject {@link UiObject} representing the child element
28618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if the child element is already fully visible, or
28718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * if the method scrolled successfully until the child became fully visible;
28818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * otherwise, false if the attempt to scroll failed.
28918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @throws UiObjectNotFoundException
29018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @hide
29118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
29218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean ensureFullyVisible(UiObject childObject) throws UiObjectNotFoundException {
29318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Rect actual = childObject.getBounds();
29418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Rect visible = childObject.getVisibleBounds();
29518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (visible.width() * visible.height() == actual.width() * actual.height()) {
29618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // area match, item fully visible
29718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            return true;
29818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
29918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        boolean shouldSwipeForward = false;
30018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (mIsVerticalList) {
30118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // if list is vertical, matching top edge implies obscured bottom edge
30218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // so we need to scroll list forward
30318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            shouldSwipeForward = actual.top == visible.top;
30418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        } else {
30518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // if list is horizontal, matching left edge implies obscured right edge,
30618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // so we need to scroll list forward
30718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            shouldSwipeForward = actual.left == visible.left;
30818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
30918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (mIsVerticalList) {
31018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if (shouldSwipeForward) {
31118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                return swipeUp(10);
31218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            } else {
31318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                return swipeDown(10);
31418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
31518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        } else {
31618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if (shouldSwipeForward) {
31718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                return swipeLeft(10);
31818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            } else {
31918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                return swipeRight(10);
32018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
32118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
32218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
32318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
32418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
32518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Performs a forward scroll action on the scrollable layout element until
32618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * the text you provided is visible, or until swipe attempts have been exhausted.
32718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * See {@link #setMaxSearchSwipes(int)}
32818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
32918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param text test to look for
33018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if item is found; else, false
33118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
33218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
33318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean scrollTextIntoView(String text) throws UiObjectNotFoundException {
33418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(text);
33518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return scrollIntoView(new UiSelector().text(text));
33618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
33718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
33818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
33918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Sets the maximum number of scrolls allowed when performing a
34018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * scroll action in search of a child element.
34118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * See {@link #getChildByDescription(UiSelector, String)} and
34218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * {@link #getChildByText(UiSelector, String)}.
34318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
34418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param swipes the number of search swipes to perform until giving up
34518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return reference to itself
34618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
34718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
34818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public UiScrollable setMaxSearchSwipes(int swipes) {
34918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(swipes);
35018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mMaxSearchSwipes = swipes;
35118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return this;
35218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
35318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
35418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
35518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Gets the maximum number of scrolls allowed when performing a
35618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * scroll action in search of a child element.
35718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * See {@link #getChildByDescription(UiSelector, String)} and
35818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * {@link #getChildByText(UiSelector, String)}.
35918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
36018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return max the number of search swipes to perform until giving up
36118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
36218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
36318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public int getMaxSearchSwipes() {
36418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace();
36518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return mMaxSearchSwipes;
36618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
36718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
36818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
36918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Performs a forward fling with the default number of fling steps (5).
37018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * If the swipe direction is set to vertical, then the swipes will be
37118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * performed from bottom to top. If the swipe
37218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * direction is set to horizontal, then the swipes will be performed from
37318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * right to left. Make sure to take into account devices configured with
37418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * right-to-left languages like Arabic and Hebrew.
37518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
37618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if scrolled, false if can't scroll anymore
37718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
37818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
37918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean flingForward() throws UiObjectNotFoundException {
38018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace();
38118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return scrollForward(FLING_STEPS);
38218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
38318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
38418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
38518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Performs a forward scroll with the default number of scroll steps (55).
38618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * If the swipe direction is set to vertical,
38718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * then the swipes will be performed from bottom to top. If the swipe
38818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * direction is set to horizontal, then the swipes will be performed from
38918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * right to left. Make sure to take into account devices configured with
39018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * right-to-left languages like Arabic and Hebrew.
39118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
39218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if scrolled, false if can't scroll anymore
39318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
39418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
39518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean scrollForward() throws UiObjectNotFoundException {
39618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace();
39718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return scrollForward(SCROLL_STEPS);
39818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
39918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
40018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
40118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Performs a forward scroll. If the swipe direction is set to vertical,
40218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * then the swipes will be performed from bottom to top. If the swipe
40318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * direction is set to horizontal, then the swipes will be performed from
40418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * right to left. Make sure to take into account devices configured with
40518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * right-to-left languages like Arabic and Hebrew.
40618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
40718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param steps number of steps. Use this to control the speed of the scroll action
40818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if scrolled, false if can't scroll anymore
40918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
41018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
41118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean scrollForward(int steps) throws UiObjectNotFoundException {
41218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(steps);
41318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Log.d(LOG_TAG, "scrollForward() on selector = " + getSelector());
41418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
41518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if(node == null) {
41618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
41718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
41818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Rect rect = new Rect();
41918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        node.getBoundsInScreen(rect);
42018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
42118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int downX = 0;
42218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int downY = 0;
42318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int upX = 0;
42418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int upY = 0;
42518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
42618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // scrolling is by default assumed vertically unless the object is explicitly
42718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // set otherwise by setAsHorizontalContainer()
42818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if(mIsVerticalList) {
42918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            int swipeAreaAdjust = (int)(rect.height() * getSwipeDeadZonePercentage());
43018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // scroll vertically: swipe down -> up
43118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            downX = rect.centerX();
43218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            downY = rect.bottom - swipeAreaAdjust;
43318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            upX = rect.centerX();
43418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            upY = rect.top + swipeAreaAdjust;
43518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        } else {
43618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            int swipeAreaAdjust = (int)(rect.width() * getSwipeDeadZonePercentage());
43718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // scroll horizontally: swipe right -> left
43818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // TODO: Assuming device is not in right to left language
43918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            downX = rect.right - swipeAreaAdjust;
44018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            downY = rect.centerY();
44118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            upX = rect.left + swipeAreaAdjust;
44218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            upY = rect.centerY();
44318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
44418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return getInteractionController().scrollSwipe(downX, downY, upX, upY, steps);
44518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
44618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
44718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
44818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Performs a backwards fling action with the default number of fling
44918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * steps (5). If the swipe direction is set to vertical,
45018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * then the swipe will be performed from top to bottom. If the swipe
45118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * direction is set to horizontal, then the swipes will be performed from
45218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * left to right. Make sure to take into account devices configured with
45318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * right-to-left languages like Arabic and Hebrew.
45418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
45518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if scrolled, and false if can't scroll anymore
45618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
45718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
45818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean flingBackward() throws UiObjectNotFoundException {
45918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace();
46018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return scrollBackward(FLING_STEPS);
46118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
46218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
46318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
46418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Performs a backward scroll with the default number of scroll steps (55).
46518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * If the swipe direction is set to vertical,
46618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * then the swipes will be performed from top to bottom. If the swipe
46718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * direction is set to horizontal, then the swipes will be performed from
46818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * left to right. Make sure to take into account devices configured with
46918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * right-to-left languages like Arabic and Hebrew.
47018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
47118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if scrolled, and false if can't scroll anymore
47218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
47318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
47418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean scrollBackward() throws UiObjectNotFoundException {
47518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace();
47618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return scrollBackward(SCROLL_STEPS);
47718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
47818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
47918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
48018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Performs a backward scroll. If the swipe direction is set to vertical,
48118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * then the swipes will be performed from top to bottom. If the swipe
48218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * direction is set to horizontal, then the swipes will be performed from
48318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * left to right. Make sure to take into account devices configured with
48418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * right-to-left languages like Arabic and Hebrew.
48518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
48618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param steps number of steps. Use this to control the speed of the scroll action.
48718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true if scrolled, false if can't scroll anymore
48818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
48918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
49018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean scrollBackward(int steps) throws UiObjectNotFoundException {
49118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(steps);
49218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Log.d(LOG_TAG, "scrollBackward() on selector = " + getSelector());
49318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        AccessibilityNodeInfo node = findAccessibilityNodeInfo(WAIT_FOR_SELECTOR_TIMEOUT);
49418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if (node == null) {
49518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            throw new UiObjectNotFoundException(getSelector().toString());
49618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
49718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Rect rect = new Rect();
49818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        node.getBoundsInScreen(rect);
49918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
50018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int downX = 0;
50118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int downY = 0;
50218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int upX = 0;
50318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        int upY = 0;
50418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
50518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // scrolling is by default assumed vertically unless the object is explicitly
50618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // set otherwise by setAsHorizontalContainer()
50718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        if(mIsVerticalList) {
50818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            int swipeAreaAdjust = (int)(rect.height() * getSwipeDeadZonePercentage());
50918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            Log.d(LOG_TAG, "scrollToBegining() using vertical scroll");
51018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // scroll vertically: swipe up -> down
51118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            downX = rect.centerX();
51218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            downY = rect.top + swipeAreaAdjust;
51318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            upX = rect.centerX();
51418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            upY = rect.bottom - swipeAreaAdjust;
51518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        } else {
51618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            int swipeAreaAdjust = (int)(rect.width() * getSwipeDeadZonePercentage());
51718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            Log.d(LOG_TAG, "scrollToBegining() using hotizontal scroll");
51818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // scroll horizontally: swipe left -> right
51918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            // TODO: Assuming device is not in right to left language
52018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            downX = rect.left + swipeAreaAdjust;
52118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            downY = rect.centerY();
52218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            upX = rect.right - swipeAreaAdjust;
52318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            upY = rect.centerY();
52418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
52518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return getInteractionController().scrollSwipe(downX, downY, upX, upY, steps);
52618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
52718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
52818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
52918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Scrolls to the beginning of a scrollable layout element. The beginning
53018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * can be at the  top-most edge in the case of vertical controls, or the
53118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * left-most edge for horizontal controls. Make sure to take into account
53218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * devices configured with right-to-left languages like Arabic and Hebrew.
53318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
53418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param steps use steps to control the speed, so that it may be a scroll, or fling
53518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true on scrolled else false
53618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
53718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
53818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean scrollToBeginning(int maxSwipes, int steps) throws UiObjectNotFoundException {
53918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(maxSwipes, steps);
54018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Log.d(LOG_TAG, "scrollToBeginning() on selector = " + getSelector());
54118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // protect against potential hanging and return after preset attempts
54218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for(int x = 0; x < maxSwipes; x++) {
54318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if(!scrollBackward(steps)) {
54418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                break;
54518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
54618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
54718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return true;
54818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
54918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
55018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
55118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Scrolls to the beginning of a scrollable layout element. The beginning
55218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * can be at the  top-most edge in the case of vertical controls, or the
55318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * left-most edge for horizontal controls. Make sure to take into account
55418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * devices configured with right-to-left languages like Arabic and Hebrew.
55518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
55618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param maxSwipes
55718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true on scrolled else false
55818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
55918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
56018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean scrollToBeginning(int maxSwipes) throws UiObjectNotFoundException {
56118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(maxSwipes);
56218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return scrollToBeginning(maxSwipes, SCROLL_STEPS);
56318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
56418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
56518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
56618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Performs a fling gesture to reach the beginning of a scrollable layout element.
56718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * The beginning can be at the  top-most edge in the case of vertical controls, or
56818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * the left-most edge for horizontal controls. Make sure to take into
56918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * account devices configured with right-to-left languages like Arabic and Hebrew.
57018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
57118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param maxSwipes
57218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true on scrolled else false
57318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
57418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
57518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean flingToBeginning(int maxSwipes) throws UiObjectNotFoundException {
57618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(maxSwipes);
57718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return scrollToBeginning(maxSwipes, FLING_STEPS);
57818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
57918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
58018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
58118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Scrolls to the end of a scrollable layout element. The end can be at the
58218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * bottom-most edge in the case of vertical controls, or the right-most edge for
58318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * horizontal controls. Make sure to take into account devices configured with
58418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * right-to-left languages like Arabic and Hebrew.
58518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
58618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param steps use steps to control the speed, so that it may be a scroll, or fling
58718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true on scrolled else false
58818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
58918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
59018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean scrollToEnd(int maxSwipes, int steps) throws UiObjectNotFoundException {
59118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(maxSwipes, steps);
59218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        // protect against potential hanging and return after preset attempts
59318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        for(int x = 0; x < maxSwipes; x++) {
59418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            if(!scrollForward(steps)) {
59518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu                break;
59618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu            }
59718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        }
59818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return true;
59918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
60018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
60118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
60218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Scrolls to the end of a scrollable layout element. The end can be at the
60318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * bottom-most edge in the case of vertical controls, or the right-most edge for
60418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * horizontal controls. Make sure to take into account devices configured with
60518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * right-to-left languages like Arabic and Hebrew.
60618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
60718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param maxSwipes
60818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true on scrolled, else false
60918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
61018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
61118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean scrollToEnd(int maxSwipes) throws UiObjectNotFoundException {
61218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(maxSwipes);
61318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return scrollToEnd(maxSwipes, SCROLL_STEPS);
61418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
61518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
61618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
61718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Performs a fling gesture to reach the end of a scrollable layout element.
61818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * The end can be at the  bottom-most edge in the case of vertical controls, or
61918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * the right-most edge for horizontal controls. Make sure to take into
62018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * account devices configured with right-to-left languages like Arabic and Hebrew.
62118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
62218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param maxSwipes
62318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return true on scrolled, else false
62418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
62518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
62618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public boolean flingToEnd(int maxSwipes) throws UiObjectNotFoundException {
62718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(maxSwipes);
62818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return scrollToEnd(maxSwipes, FLING_STEPS);
62918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
63018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
63118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
63218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Returns the percentage of a widget's size that's considered as a no-touch
63318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * zone when swiping. The no-touch zone is set as a percentage of a widget's total
63418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * width or height, denoting a margin around the swipable area of the widget.
63518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Swipes must start and end inside this margin. This is important when the
63618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * widget being swiped may not respond to the swipe if started at a point
63718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * too near to the edge. The default is 10% from either edge.
63818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
63918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return a value between 0 and 1
64018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
64118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
64218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public double getSwipeDeadZonePercentage() {
64318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace();
64418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return mSwipeDeadZonePercentage;
64518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
64618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu
64718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    /**
64818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * Sets the percentage of a widget's size that's considered as no-touch
64918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * zone when swiping.
65018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * The no-touch zone is set as percentage of a widget's total width or height,
65118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * denoting a margin around the swipable area of the widget. Swipes must
65218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * always start and end inside this margin. This is important when the
65318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * widget being swiped may not respond to the swipe if started at a point
65418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * too near to the edge. The default is 10% from either edge.
65518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     *
65618b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @param swipeDeadZonePercentage is a value between 0 and 1
65718b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @return reference to itself
65818b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     * @since API Level 16
65918b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu     */
66018b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    public UiScrollable setSwipeDeadZonePercentage(double swipeDeadZonePercentage) {
66118b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        Tracer.trace(swipeDeadZonePercentage);
66218b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        mSwipeDeadZonePercentage = swipeDeadZonePercentage;
66318b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu        return this;
66418b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu    }
66518b892c723e812a7e111f102d2b0c0782b116bb6Guang Zhu}
666