blob: b07b4f67e51fdbf30b54d2b3e92946bc7972ae93 [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.tv;
import android.app.Instrumentation;
import android.os.SystemClock;
import android.platform.test.helpers.AbstractLeanbackAppHelper;
import android.platform.test.helpers.CommandHelper;
import android.platform.test.helpers.exceptions.UiTimeoutException;
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.UiObject2;
import android.support.test.uiautomator.Until;
import android.util.Log;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PlayMoviesHelperImpl extends AbstractLeanbackAppHelper {
private static final String LOG_TAG = PlayMoviesHelperImpl.class.getSimpleName();
private static final String UI_PACKAGE = "com.google.android.videos";
private static final String RES_MAIN_ACTIVITY_ID = "browse_container_dock";
private static final String RES_SEARCH_ORB_ID = "title_orb";
private static final String RES_SEARCH_BOX_ID = "lb_search_text_editor";
private static final String TEXT_MOVIES = "Movies";
private static final String TEXT_MY_LIBRARY = "My library";
private static final String TEXT_PLAY_TRAILER = "PLAY TRAILER";
private static final long SHORT_SLEEP_MS = 5000; // 5 seconds
private static final long LONG_SLEEP_MS = 30000; // 30 seconds
private CommandHelper mCmdHelper;
public PlayMoviesHelperImpl(Instrumentation instrumentation) {
super(instrumentation);
mCmdHelper = new CommandHelper(instrumentation);
}
/**
* {@inheritDoc}
*/
@Override
public String getPackage() {
return UI_PACKAGE;
}
/**
* {@inheritDoc}
*/
@Override
public String getLauncherName() {
return "Play Movies & TV";
}
/**
* {@inheritDoc}
*/
@Override
protected BySelector getMainActivitySelector() {
return By.res(UI_PACKAGE, RES_MAIN_ACTIVITY_ID);
}
/**
* Selects search orb. The app should be opened beforehand by calling open().
*/
public void selectSearchOrb() {
returnToMainActivity();
// Wait until the search orb appears at runtime.
UiObject2 searchOrb = mDevice.wait(
Until.findObject(By.res(UI_PACKAGE, RES_SEARCH_ORB_ID).clickable(true)),
SHORT_SLEEP_MS);
if (searchOrb == null) {
throw new UiTimeoutException("Failed to select search orb");
}
searchOrb.click();
}
/**
* Searches for the given query and keep the search result open.
* Play Movies app should be opened beforehand by calling open().
*
* @param query a search query string typed in Play Movies' search box.
*/
public void search(String query) {
selectSearchOrb();
mDevice.waitForIdle();
Log.v(LOG_TAG, "Searching for the movie: " + query);
UiObject2 editText = mDevice.wait(Until.findObject(
By.res(UI_PACKAGE, RES_SEARCH_BOX_ID)), SHORT_SLEEP_MS);
if (editText == null) {
throw new UnknownUiException("Search text editor not found");
}
int retries = 4;
while(!editText.isFocused() && retries > 0) {
mDevice.pressDPadRight();
mDevice.waitForIdle();
retries--;
}
// Set query and search
editText.setText(query);
SystemClock.sleep(SHORT_SLEEP_MS);
mDevice.pressEnter();
SystemClock.sleep(SHORT_SLEEP_MS);
}
/**
* Finds a movie with the trailer from the search result and start playing.
* search() should be called right before calling this method.
*/
public UiObject2 searchForMovieWithTrailer() {
mDevice.wait(Until.findObject(By.text(TEXT_MOVIES)), SHORT_SLEEP_MS);
mDevice.pressDPadCenter();
mDevice.waitForIdle();
// Skip until a trailer is found from the result
UiObject2 trailerButton = null;
final int MAX_ATTEMPTS_SEARCH_TRAILERS = 5;
for (long i = 0; i < MAX_ATTEMPTS_SEARCH_TRAILERS; i++) {
trailerButton = getTrailerButton();
if (trailerButton == null) {
// The trailer was not found for the movie,
// back and open the detail of the next movie
mDevice.pressBack();
mDevice.wait(Until.findObject(By.text(TEXT_MOVIES)), SHORT_SLEEP_MS);
mDevice.pressDPadRight();
SystemClock.sleep(SHORT_SLEEP_MS);
mDevice.pressDPadCenter();
mDevice.waitForIdle();
} else {
// The trailer was found for the movie
break;
}
}
SystemClock.sleep(SHORT_SLEEP_MS);
return trailerButton;
}
public UiObject2 getTrailerButton() {
return mDevice.wait(Until.findObject(By.text(TEXT_PLAY_TRAILER)), SHORT_SLEEP_MS);
}
/**
* Setup expectations: Trailer is selected, and shown in details fragment.
*
* Play a trailer
*/
public void playTrailerInDetails(long durationMs) {
UiObject2 trailerButton = getTrailerButton();
if (trailerButton == null) {
throw new UnknownUiException("Trailer action not found");
}
trailerButton.click();
// Using "Play trailer" to wait for the playback to start
mDevice.wait(Until.gone(By.text(TEXT_PLAY_TRAILER)),
SHORT_SLEEP_MS);
// Using "Play trailer" button to wait until the trailer finishes
trailerButton = mDevice.wait(
Until.findObject(By.text(TEXT_PLAY_TRAILER)), durationMs);
if (trailerButton == null) {
throw new RuntimeException("Trailer too long or something went wrong");
}
}
/**
* Open My Library section
*/
public void openMyLibrary() {
returnToMainActivity();
openHeader(TEXT_MY_LIBRARY);
}
/**
* Setup expectations: None.
* Open My Movies in My library section, wait for the list of movies to come.
*/
public void openMyMoviesList() {
openMyLibrary();
if (getCardByNameInRowContent(TEXT_MOVIES) == null) {
throw new UnknownUiException("Movies in My library not found");
}
mDevice.performActionAndWait(new Runnable() {
@Override
public void run() {
mDevice.pressDPadCenter();
}
}, Until.newWindow(), SHORT_SLEEP_MS);
}
/**
* Get a card with the given name in row_content
*
* @param title of the card
* @return UIObject2 for the focusable button
*/
private UiObject2 getCardByNameInRowContent(String title) {
UiObject2 container = mDevice.findObject(
By.res(getPackage(), "row_content").hasDescendant(By.focused(true)));
return select(container, By.res(getPackage(), "title_text").text(title),
Direction.RIGHT);
}
/**
* Setup expectations: The movie(s) is listed in the Vertical grid fragment
*/
public void selectTheFocusedMovieInVerticalGrid() {
assertWidgetEquals(Widget.VERTICAL_GRID_FRAGMENT);
mDevice.performActionAndWait(new Runnable() {
@Override
public void run() {
mDevice.pressDPadCenter();
}
}, Until.newWindow(), SHORT_SLEEP_MS);
}
/**
* Setup expectations: The movie to play is listed in the Details fragment
*
* Play the selected movies from beginning
*/
public void playFromBeginning() {
assertWidgetEquals(Widget.DETAILS_FRAGMENT);
// Play from beginning
UiObject2 actionButton = mDevice.wait(Until.findObject(By.clazz(".Button")),
LONG_SLEEP_MS);
if (actionButton == null) {
throw new UnknownUiException("action button not found");
}
String selectedText = actionButton.getText();
Log.v(LOG_TAG, String.format("Selected text is: %s", selectedText));
while (!(selectedText.toLowerCase().equals("play from beginning") ||
selectedText.toLowerCase().equals("play movie"))) {
String prevText = selectedText;
// Select the next item
mDevice.pressDPadRight();
// Make sure the text has changed
selectedText = mDevice.findObject(By.clazz(".Button").focused(true)).getText();
if (selectedText.equals(prevText)) {
throw new UnknownUiException("'Play from beginning' or 'Play movie' not found");
}
}
mDevice.pressDPadCenter();
// Dismiss confirmation dialog if it's a rental movie
UiObject2 yesButton = mDevice.wait(
Until.findObject(By.res(UI_PACKAGE, "guidedactions_list")), SHORT_SLEEP_MS);
if (yesButton != null) {
mDevice.pressDPadCenter();
}
}
/**
* Get the current playback state for a given package that owns the media session.
* @param packageName the package name of media session owner
* @return
* 0 = PlaybackState.STATE_NONE
* 1 = PlaybackState.STATE_STOPPED
* 2 = PlaybackState.STATE_PAUSED
* 3 = PlaybackState.STATE_PLAYING
*/
public int getPlaybackState(String packageName) {
String output = mCmdHelper.executeDumpsysMediaSession();
// Parse the output of dumpsys media_session.
// Example :
// LeanbackSampleApp com.example.android.tvleanback/LeanbackSampleApp
// package=com.example.android.tvleanback
// ...
// state=PlaybackState {state=3, position=0, buffered position=0, speed=1.0, updated=...}
int playbackState = 0;
int index = output.indexOf(String.format("package=%s", packageName));
if (index == -1) {
Log.w(LOG_TAG, String.format("No media session found for the package: %s", packageName));
return playbackState;
}
final Pattern PLAYBACKSTATE_REGEX = Pattern.compile(
"\\s*state=PlaybackState \\{state=(\\d+),.*");
Matcher matcher = PLAYBACKSTATE_REGEX.matcher(output.substring(index));
if (matcher.find()) {
playbackState = Integer.parseInt(matcher.group(1));
Log.i(LOG_TAG, String.format("PlaybackState=%s package=%s", playbackState, packageName));
}
return playbackState;
}
}