blob: adbabb121a754dea4e53f29a729a0a40260c6138 [file] [log] [blame]
/*
* Copyright (C) 2016 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.test.helpers;
import android.app.Instrumentation;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.SystemClock;
import android.platform.test.helpers.exceptions.UnknownUiException;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.Direction;
import android.support.test.uiautomator.Until;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.util.Log;
import java.util.List;
import java.util.regex.Pattern;
import junit.framework.Assert;
import android.os.Environment;
import java.io.File;
import java.io.IOException;
public class PhotosHelperImpl extends AbstractPhotosHelper {
private static final String LOG_TAG = PhotosHelperImpl.class.getSimpleName();
private static final long APP_LOAD_WAIT = 7500;
private static final long HACKY_WAIT = 2500;
private static final long PICTURE_LOAD_WAIT = 20000;
private static final long UI_NAVIGATION_WAIT = 5000;
private static final Pattern UI_PHOTO_DESC = Pattern.compile("^Photo.*");
private static final String UI_DONE_BUTTON_ID = "done_button";
private static final String UI_GET_STARTED_CONTAINER = "get_started_container";
private static final String UI_GET_STARTED_ID = "get_started";
private static final String UI_LOADING_ICON_ID = "list_empty_progress_bar";
private static final String UI_NEXT_BUTTON_ID = "next_button";
private static final String UI_PACKAGE_NAME = "com.google.android.apps.photos";
private static final String UI_PHOTO_TAB_ID = "tab_photos";
private static final String UI_DEVICE_FOLDER_TEXT = "Device folders";
private static final String UI_PHOTO_VIEW_PAGER_ID = "photo_view_pager";
private static final String UI_PHOTO_SCROLL_VIEW_ID = "recycler_view";
private static final String UI_NAVIGATION_LIST_ID = "navigation_list";
private static final int MAX_UI_SCROLL_COUNT = 20;
private static final int MAX_DISMISS_INIT_DIALOG_RETRY = 20;
public PhotosHelperImpl(Instrumentation instr) {
super(instr);
}
/**
* {@inheritDoc}
*/
@Override
public String getPackage() {
return "com.google.android.apps.photos";
}
/**
* {@inheritDoc}
*/
@Override
public String getLauncherName() {
return "Photos";
}
/**
* {@inheritDoc}
*/
@Override
public void dismissInitialDialogs() {
// Target Photos version 1.18.0.119671374
SystemClock.sleep(APP_LOAD_WAIT);
if (isOnInitialDialogScreen()) {
UiObject2 getStartedButton = mDevice.wait(
Until.findObject(By.res(UI_PACKAGE_NAME, UI_GET_STARTED_ID)), APP_LOAD_WAIT);
int retryCount = 0;
while ((retryCount < MAX_DISMISS_INIT_DIALOG_RETRY) &&
(getStartedButton == null)) {
/*
The UiAutomator sometimes cannot find GET STARTED button even though
it is seen on the screen.
The reason is because the initial "spinner" animation screen updates
views too quickly for UiAutomator to catch the change.
The following hack is used to reload the init dialog for UiAutomator to
retry catching the GET STARTED button.
*/
mDevice.pressBack();
mDevice.waitForIdle();
mDevice.pressHome();
mDevice.waitForIdle();
open();
getStartedButton = mDevice.wait(
Until.findObject(By.res(UI_PACKAGE_NAME, UI_GET_STARTED_ID)),
APP_LOAD_WAIT);
retryCount += 1;
if (!isOnInitialDialogScreen()) {
break;
}
}
if (isOnInitialDialogScreen() && (getStartedButton == null)) {
throw new IllegalStateException("UiAutomator cannot catch GET STARTED button");
}
else {
if (getStartedButton != null) {
getStartedButton.click();
}
}
}
else {
Log.e(LOG_TAG, "Didn't find GET STARTED button.");
}
// Address dialogs with an account vs. without an account
Pattern signInWords = Pattern.compile("Sign in", Pattern.CASE_INSENSITIVE);
boolean hasAccount = !mDevice.hasObject(By.text(signInWords));
if (!hasAccount) {
// Select 'NO THANKS' if no account exists
Pattern noThanksWords = Pattern.compile("No thanks", Pattern.CASE_INSENSITIVE);
UiObject2 noThanksButton = mDevice.findObject(By.text(noThanksWords));
if (noThanksButton != null) {
noThanksButton.click();
mDevice.waitForIdle();
} else {
Log.e(LOG_TAG, "Unable to find NO THANKS button.");
}
} else {
UiObject2 doneButton = mDevice.wait(Until.findObject(
By.res(UI_PACKAGE_NAME, UI_DONE_BUTTON_ID)), 5000);
if (doneButton != null) {
doneButton.click();
mDevice.waitForIdle();
}
else {
Log.e(LOG_TAG, "Didn't find DONE button.");
}
// Press the next button (arrow and check mark) four consecutive times
for (int repeat = 0; repeat < 4; repeat++) {
UiObject2 nextButton = mDevice.findObject(
By.res(UI_PACKAGE_NAME, UI_NEXT_BUTTON_ID));
if (nextButton != null) {
nextButton.click();
mDevice.waitForIdle();
} else {
Log.e(LOG_TAG, "Unable to find arrow or check mark buttons.");
}
}
mDevice.wait(Until.gone(
By.res(UI_PACKAGE_NAME, UI_LOADING_ICON_ID)), PICTURE_LOAD_WAIT);
}
mDevice.waitForIdle();
}
/**
* {@inheritDoc}
*/
@Override
public void openFirstClip() {
if (searchForVideoClip()) {
UiObject2 clip = getFirstClip();
if (clip != null) {
clip.click();
mDevice.wait(Until.findObject(
By.res(UI_PACKAGE_NAME, "photos_videoplayer_play_button_holder")), 2000);
}
else {
throw new IllegalStateException("Cannot play a video after finding video clips");
}
}
else {
throw new UnsupportedOperationException("Cannot find a video clip");
}
}
/**
* {@inheritDoc}
*/
@Override
public void pauseClip() {
UiObject2 holder = mDevice.findObject(
By.res(UI_PACKAGE_NAME, "photos_videoplayer_play_button_holder"));
if (holder != null) {
holder.click();
} else {
throw new UnknownUiException("Unable to find pause button holder.");
}
UiObject2 pause = mDevice.wait(Until.findObject(
By.res(UI_PACKAGE_NAME, "photos_videoplayer_pause_button")), 2500);
if (pause != null) {
pause.click();
mDevice.wait(Until.findObject(By.desc("Play video")), 2500);
} else {
throw new UnknownUiException("Unable to find pause button.");
}
}
/**
* {@inheritDoc}
*/
@Override
public void playClip() {
UiObject2 play = mDevice.findObject(By.desc("Play video"));
if (play != null) {
play.click();
mDevice.wait(Until.findObject(
By.res(UI_PACKAGE_NAME, "photos_videoplayer_pause_button")), 2500);
} else {
throw new UnknownUiException("Unable to find play button");
}
}
/**
* {@inheritDoc}
*/
@Override
public void goToMainScreen() {
for (int retriesRemaining = 5; retriesRemaining > 0 && !isOnMainScreen();
--retriesRemaining) {
// check if we see the Photos tab at the bottom of the screen
// If we do, clicking on the tab should go to home screen.
UiObject2 photosButton = mDevice.findObject(
By.res(UI_PACKAGE_NAME, UI_PHOTO_TAB_ID));
if (photosButton != null) {
photosButton.click();
}
else {
mDevice.pressBack();
}
mDevice.waitForIdle();
}
if (!isOnMainScreen()) {
throw new IllegalStateException("Cannot go to main screen");
}
}
/**
* {@inheritDoc}
*/
@Override
public void openPicture(int index) {
mDevice.waitForIdle();
List<UiObject2> photos = mDevice.findObjects(By.pkg(UI_PACKAGE_NAME).desc(UI_PHOTO_DESC));
if (photos == null) {
throw new IllegalStateException("Cannot find photos on current view screen");
}
if ((index < 0) || (index >= photos.size())) {
String errMsg = String.format("Photo index (%d) out of bound (0..%d)",
index, photos.size());
throw new IllegalArgumentException(errMsg);
}
UiObject2 photo = photos.get(index);
photo.click();
if (!mDevice.wait(Until.hasObject(By.res(UI_PACKAGE_NAME, UI_PHOTO_VIEW_PAGER_ID)),
UI_NAVIGATION_WAIT)) {
throw new IllegalStateException("Cannot display photo on screen");
}
}
/**
* {@inheritDoc}
*/
@Override
public void scrollAlbum(Direction direction) {
if (!(Direction.LEFT.equals(direction) || Direction.RIGHT.equals(direction))) {
throw new IllegalArgumentException("Scroll direction must be LEFT or RIGHT");
}
UiObject2 scrollContainer = mDevice.findObject(
By.res(UI_PACKAGE_NAME, UI_PHOTO_VIEW_PAGER_ID));
if (scrollContainer == null) {
throw new UnknownUiException("Cannot find scroll container");
}
scrollContainer.scroll(direction, 1.0f);
}
/**
* {@inheritDoc}
*/
@Override
public void goToDeviceFolderScreen() {
if (!isOnDeviceFolderScreen()) {
if (!isOnMainScreen()) {
goToMainScreen();
}
openNavigationDrawer();
UiObject2 deviceFolderButton = mDevice.wait(Until.findObject(
By.text(UI_DEVICE_FOLDER_TEXT)), UI_NAVIGATION_WAIT);
if (deviceFolderButton != null) {
deviceFolderButton.click();
}
else {
UiObject2 photosButton = mDevice.wait(Until.findObject(By.text("Photos")),
UI_NAVIGATION_WAIT);
if (photosButton != null) {
photosButton.click();
}
else {
throw new IllegalStateException("No device folder in navigation drawer");
}
}
}
if (!isOnDeviceFolderScreen()) {
throw new UnknownUiException("Can not go to device folder screen");
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean searchForDeviceFolder(String folderName) {
boolean foundFolder = false;
int scrollCount = 0;
while (!foundFolder && (scrollCount < MAX_UI_SCROLL_COUNT)) {
foundFolder = mDevice.wait(Until.hasObject(By.text(folderName)), 2000);
if (!foundFolder) {
if (!scrollView(Direction.DOWN)) {
break;
}
}
scrollCount += 1;
}
if (!foundFolder) {
foundFolder = mDevice.wait(Until.hasObject(By.text(folderName)), 2000);
}
return foundFolder;
}
/**
* {@inheritDoc}
*/
@Override
public boolean searchForVideoClip() {
boolean foundVideoClip = false;
int scrollCount = 0;
while (!foundVideoClip && (scrollCount < MAX_UI_SCROLL_COUNT)) {
foundVideoClip = (getFirstClip() != null);
if (!foundVideoClip) {
if (!scrollView(Direction.DOWN)) {
break;
}
}
scrollCount += 1;
}
return foundVideoClip;
}
/**
* {@inheritDoc}
*/
@Override
public boolean searchForPicture() {
boolean foundPicture = false;
int scrollCount = 0;
while (!foundPicture && (scrollCount < MAX_UI_SCROLL_COUNT)) {
foundPicture = mDevice.wait(Until.hasObject(By.descStartsWith("Photo")), 2000);
if (!foundPicture) {
if (!scrollView(Direction.DOWN)) {
break;
}
}
scrollCount += 1;
}
return foundPicture;
}
/**
* {@inheritDoc}
*/
@Override
public void openDeviceFolder(String folderName) {
UiObject2 deviceFolder = mDevice.wait(Until.findObject(By.text(folderName)),
UI_NAVIGATION_WAIT);
if (deviceFolder != null) {
deviceFolder.click();
}
else {
throw new IllegalArgumentException(String.format("Cannot open device folder %s",
folderName));
}
}
private UiObject2 getFirstClip() {
return mDevice.wait(Until.findObject(By.descStartsWith("Video")), 2000);
}
/**
* This function returns true if Photos is currently on the first-use
* initial dialog screen, with "Get Started" button displayed on screen
*
* @return Returns true if app is on the initial dialog screen, false otherwise
*/
private boolean isOnInitialDialogScreen() {
return mDevice.hasObject(By.res(UI_PACKAGE_NAME, UI_GET_STARTED_CONTAINER));
}
private boolean isOnMainScreen() {
return mDevice.hasObject(By.descContains("Photos, selected"));
}
/**
* This function returns true if Photos is currently in the
* photo-viewing screen, displaying either one photo
* or video on the screen.
*
* @return Returns true if one photo or video is displayed on the screen,
* false otherwise.
*/
private boolean isOnPhotoViewingScreen() {
return mDevice.hasObject(By.res(UI_PACKAGE_NAME, UI_PHOTO_VIEW_PAGER_ID));
}
private boolean isOnDeviceFolderScreen() {
if (mDevice.hasObject(By.pkg(UI_PACKAGE_NAME).text(UI_DEVICE_FOLDER_TEXT))) {
return true;
}
// sometimes the "Device Folder" tab is hidden.
// scroll down once to make sure the tab is visible
UiObject2 scrollContainer = mDevice.findObject(
By.res(UI_PACKAGE_NAME, UI_PHOTO_SCROLL_VIEW_ID));
if (scrollContainer != null) {
scrollContainer.scroll(Direction.DOWN, 1.0f);
return mDevice.hasObject(By.pkg(UI_PACKAGE_NAME).text(UI_DEVICE_FOLDER_TEXT));
}
else {
return false;
}
}
/**
* This function performs one scroll on the current screen, in the direction
* specified by input argument.
*
* @param dir The direction of the scroll
* @return Returns whether the object can still scroll in the given direction
*/
private boolean scrollView(Direction dir) {
UiObject2 scrollContainer = mDevice.findObject(By.res(UI_PACKAGE_NAME,
UI_PHOTO_SCROLL_VIEW_ID));
if (scrollContainer == null) {
return false;
}
return scrollContainer.scroll(dir, 1.0f);
}
private void openNavigationDrawer() {
UiObject2 navigationDrawer = mDevice.findObject(By.desc("Show Navigation Drawer"));
if (navigationDrawer == null) {
mDevice.pressBack();
navigationDrawer = mDevice.wait(Until.findObject(By.desc("Show Navigation Drawer")),
UI_NAVIGATION_WAIT);
}
if (navigationDrawer == null) {
throw new UnknownUiException("Cannot find navigation drawer");
}
navigationDrawer.click();
if (!mDevice.hasObject(By.res(UI_PACKAGE_NAME, UI_NAVIGATION_LIST_ID))) {
throw new UnknownUiException("Cannot open navigation drawer");
}
}
}