| /* |
| * 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 androidx.test.InstrumentationRegistry; |
| import androidx.test.uiautomator.By; |
| import androidx.test.uiautomator.Direction; |
| import androidx.test.uiautomator.UiDevice; |
| import androidx.test.uiautomator.UiObject2; |
| |
| /** |
| * This interface is intended to be inherited by AppHelper classes to add scrolling functionlity. |
| */ |
| public interface Scrollable { |
| int DEFAULT_MARGIN = 5; |
| int DEFAULT_DURATION_MS = 1000; |
| |
| /** |
| * 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); |
| } |
| |
| /** |
| * Scroll up from the bottom of the scrollable region to top of the scrollable region (i.e. by |
| * one page). |
| */ |
| public default boolean scrollUpOnePage() { |
| scrollUpOnePage(DEFAULT_DURATION_MS); |
| return true; |
| } |
| |
| /** |
| * 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); |
| } |
| |
| /** |
| * Scroll down from the top of the scrollable region to bottom of the scrollable region (i.e. by |
| * one page). |
| */ |
| public default boolean scrollDownOnePage() { |
| scrollDownOnePage(DEFAULT_DURATION_MS); |
| return true; |
| } |
| |
| /** |
| * 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; |
| } |
| } |