summaryrefslogtreecommitdiff
path: root/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/utility/Scrollable.java
blob: cc8f9a831f245713a96eb6cd42a1f3ed1d7786d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.platform.helpers;

import android.graphics.Rect;
import android.platform.helpers.exceptions.TestHelperException;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.Direction;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import androidx.test.InstrumentationRegistry;

/**
 * This interface is intended to be inherited by AppHelper classes to add scrolling functionlity.
 */
public interface Scrollable {
    int DEFAULT_MARGIN = 5;

    /**
     * Setup expectations: None
     *
     * <p>return true is the corresponding app is in foreground, and false otherwise.
     */
    boolean isAppInForeground();

    /**
     * Setup expectations: None.
     *
     * <p>Scroll up from the bottom of the scrollable region to top of the scrollable region (i.e.
     * by one page) in <code>durationMs</code> milliseconds only if corresponding app is open.
     *
     * @param durationMs The duration in milliseconds to perform the scrolling gesture.
     */
    public default void scrollUpOnePage(long durationMs) {
        scrollUp(100f, durationMs);
    }

    /**
     * Setup expectations: None.
     *
     * <p>Scroll up from the bottom of the scrollable region towards the top of the scrollable
     * region by <code>percent</code> percent of the whole scrollable region in <code>durationMs
     * </code> milliseconds only if corresponding app is open.
     *
     * @param percent The percentage of the whole scrollable region by which to scroll up, ranging
     *     from 0 - 100. For instance, percent = 50 would scroll up by half of the screen.
     * @param durationMs The duration in milliseconds to perform the scrolling gesture.
     */
    public default void scrollUp(float percent, long durationMs) {
        scroll(Direction.UP, percent, durationMs);
    }

    /**
     * Setup expectations: None.
     *
     * <p>Scroll down from the top of the scrollable region to bottom of the scrollable region (i.e.
     * by one page) in <code>durationMs</code> milliseconds only if corresponding app is open.
     *
     * @param durationMs The duration in milliseconds to perform the scrolling gesture.
     */
    public default void scrollDownOnePage(long durationMs) {
        scrollDown(100f, durationMs);
    }

    /**
     * Setup expectations: None.
     *
     * <p>Scroll down from the top of the scrollable region towards the bottom of the scrollable
     * region by <code>percent</code> percent of the whole scrollable region in <code>durationMs
     * </code> milliseconds only if corresponding app is open.
     *
     * @param percent The percentage of the whole scrollable region by which to scroll down, ranging
     *     from 0 - 100. For instance, percent = 50 would scroll down by half of the screen.
     * @param durationMs The duration in milliseconds to perform the scrolling gesture.
     */
    public default void scrollDown(float percent, long durationMs) {
        scroll(Direction.DOWN, percent, durationMs);
    }

    /**
     * Setup expectations: None.
     *
     * <p>This method can be implemented optionally if customized margin is required.
     *
     * @return the gesture margin for scrolling.
     */
    public default Margin getScrollableMargin() {
        return new Margin(DEFAULT_MARGIN);
    }

    /**
     * Setup expectations: None.
     *
     * <p>This method can be implemented optionally if customized margin is required. It sets the
     * gesture margin returned by <code>getScrollableMargin()</code>.
     *
     * @param margin Left, top, right and bottom margins will all be set this this value.
     */
    public default void setScrollableMargin(int margin) {
        throw new UnsupportedOperationException("setScrollableMargin method not implemeneted.");
    }

    /**
     * Setup expectations: None.
     *
     * <p>This method can be implemented optionally if customized margin is required. It sets the
     * gesture margin returned by <code>getScrollableMargin()</code>.
     *
     * @param left The value to which to set the left margin for scrollling.
     * @param top The value to which to set the top margin for scrollling.
     * @param right The value to which to set the right margin for scrollling.
     * @param bottom The value to which to set the bottom margin for scrollling.
     */
    public default void setScrollableMargin(int left, int top, int right, int bottom) {
        throw new UnsupportedOperationException("setScrollableMargin method not implemeneted.");
    }

    public class Margin {
        private int mLeft;
        private int mTop;
        private int mRight;
        private int mBottom;

        public Margin(int margin) {
            mLeft = margin;
            mTop = margin;
            mRight = margin;
            mBottom = margin;
        }

        public Margin(int left, int top, int right, int bottom) {
            mLeft = left;
            mTop = top;
            mRight = right;
            mBottom = bottom;
        }

        public int getLeft() {
            return mLeft;
        }

        public int getTop() {
            return mTop;
        }

        public int getRight() {
            return mRight;
        }

        public int getBottom() {
            return mBottom;
        }
    }

    /**
     * This is not part of the public interface. For internal use only.
     *
     * <p>Scroll in <code>direction</code> direction by <code>percent</code> percent of the whole
     * scrollable region in <code>durationMs </code> milliseconds only if corresponding app is open.
     *
     * @param direction The direction in which to perform scrolling, it's either up or down.
     * @param percent The percentage of the whole scrollable region by which to scroll, ranging from
     *     0 - 100. For instance, percent = 50 would scroll up/down by half of the screen.
     * @param durationMs The duration in milliseconds to perform the scrolling gesture.
     */
    default void scroll(Direction direction, float percent, long durationMs) {
        if (isAppInForeground()) {
            UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
            UiObject2 scrollable = device.findObject(By.scrollable(true));

            if (scrollable != null) {
                Margin margin = getScrollableMargin();
                scrollable.setGestureMargins(
                        margin.getLeft(),
                        margin.getTop(),
                        margin.getRight(),
                        margin.getBottom());
                int scrollSpeed = calcScrollSpeed(scrollable, durationMs);
                scrollable.scroll(direction, percent / 100, scrollSpeed);
            } else {
                throw new TestHelperException("There is nothing that can scroll.");
            }
        } else {
            throw new TestHelperException("App is not open.");
        }
    }

    /**
     * This is not part of the public interface. For internal use only.
     *
     * <p>Return the scroll speed such that it takes <code>durationMs</code> milliseconds for the
     * device to scroll through the whole scrollable region(i.e. from the top of the scrollable
     * region to bottom).
     *
     * @param scrollable The given scrollable object to scroll through.
     * @param durationMs The duration in milliseconds to perform the scrolling gesture.
     */
    default int calcScrollSpeed(UiObject2 scrollable, long durationMs) {
        Rect bounds = scrollable.getVisibleBounds();
        double durationSeconds = (double) durationMs / 1000;
        int scrollSpeed = (int) (bounds.height() / durationSeconds);
        return scrollSpeed;
    }
}