blob: 48acc0c2b20a25dbb3d100f0c306c6d14d2a0775 [file] [log] [blame]
/*
* 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;
}
}