blob: cd7029305870903d877795b97db35e9d42d00ff6 [file] [log] [blame]
/*
* Copyright (C) 2014 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.media.tv.cts;
import static androidx.test.ext.truth.view.MotionEventSubject.assertThat;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.PlaybackParams;
import android.media.tv.AitInfo;
import android.media.tv.TvContentRating;
import android.media.tv.TvContract;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
import android.media.tv.TvRecordingClient;
import android.media.tv.TvTrackInfo;
import android.media.tv.TvView;
import android.media.tv.cts.TvInputServiceTest.CountingTvInputService.CountingRecordingSession;
import android.media.tv.cts.TvInputServiceTest.CountingTvInputService.CountingSession;
import android.media.tv.interactive.TvInteractiveAppServiceInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.View;
import android.widget.LinearLayout;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.RequiredFeatureRule;
import com.google.common.truth.Truth;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Test {@link android.media.tv.TvInputService}.
*/
@RunWith(AndroidJUnit4.class)
public class TvInputServiceTest {
private static final String TAG = "TvInputServiceTest";
@Rule
public RequiredFeatureRule featureRule = new RequiredFeatureRule(
PackageManager.FEATURE_LIVE_TV);
@Rule
public ActivityScenarioRule<TvViewStubActivity> activityRule =
new ActivityScenarioRule(TvViewStubActivity.class);
private static final Uri CHANNEL_0 = TvContract.buildChannelUri(0);
/** The maximum time to wait for an operation. */
private static final long TIME_OUT = 5000L;
private static final TvTrackInfo TEST_TV_TRACK =
new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, "testTrackId")
.setVideoWidth(1920)
.setVideoHeight(1080)
.setLanguage("und")
.build();
private TvRecordingClient mTvRecordingClient;
private Instrumentation mInstrumentation;
private TvInputManager mManager;
private TvInputInfo mStubInfo;
private TvInputInfo mFaultyStubInfo;
private final StubCallback mCallback = new StubCallback();
private final StubTimeShiftPositionCallback mTimeShiftPositionCallback =
new StubTimeShiftPositionCallback();
private final StubRecordingCallback mRecordingCallback = new StubRecordingCallback();
private static class StubCallback extends TvView.TvInputCallback {
private int mChannelRetunedCount;
private int mVideoAvailableCount;
private int mVideoUnavailableCount;
private int mTrackSelectedCount;
private int mTrackChangedCount;
private int mVideoSizeChanged;
private int mContentAllowedCount;
private int mContentBlockedCount;
private int mTimeShiftStatusChangedCount;
private int mAitInfoUpdatedCount;
private Uri mChannelRetunedUri;
private Integer mVideoUnavailableReason;
private Integer mTrackSelectedType;
private String mTrackSelectedTrackId;
private List<TvTrackInfo> mTracksChangedTrackList;
private TvContentRating mContentBlockedRating;
private Integer mTimeShiftStatusChangedStatus;
private AitInfo mAitInfo;
@Override
public void onChannelRetuned(String inputId, Uri channelUri) {
mChannelRetunedCount++;
mChannelRetunedUri = channelUri;
}
@Override
public void onVideoAvailable(String inputId) {
mVideoAvailableCount++;
}
@Override
public void onVideoUnavailable(String inputId, int reason) {
mVideoUnavailableCount++;
mVideoUnavailableReason = reason;
}
@Override
public void onTrackSelected(String inputId, int type, String trackId) {
mTrackSelectedCount++;
mTrackSelectedType = type;
mTrackSelectedTrackId = trackId;
}
@Override
public void onTracksChanged(String inputId, List<TvTrackInfo> trackList) {
mTrackChangedCount++;
mTracksChangedTrackList = trackList;
}
@Override
public void onVideoSizeChanged(String inputId, int width, int height) {
mVideoSizeChanged++;
}
@Override
public void onContentAllowed(String inputId) {
mContentAllowedCount++;
}
@Override
public void onContentBlocked(String inputId, TvContentRating rating) {
mContentBlockedCount++;
mContentBlockedRating = rating;
}
@Override
public void onTimeShiftStatusChanged(String inputId, int status) {
mTimeShiftStatusChangedCount++;
mTimeShiftStatusChangedStatus = status;
}
public void onAitInfoUpdated(String inputId, AitInfo aitInfo) {
mAitInfoUpdatedCount++;
mAitInfo = aitInfo;
}
public void resetCounts() {
mChannelRetunedCount = 0;
mVideoAvailableCount = 0;
mVideoUnavailableCount = 0;
mTrackSelectedCount = 0;
mTrackChangedCount = 0;
mContentAllowedCount = 0;
mContentBlockedCount = 0;
mTimeShiftStatusChangedCount = 0;
mAitInfoUpdatedCount = 0;
}
public void resetPassedValues() {
mChannelRetunedUri = null;
mVideoUnavailableReason = null;
mTrackSelectedType = null;
mTrackSelectedTrackId = null;
mTracksChangedTrackList = null;
mContentBlockedRating = null;
mTimeShiftStatusChangedStatus = null;
mAitInfo = null;
}
}
private static class StubTimeShiftPositionCallback extends TvView.TimeShiftPositionCallback {
private int mTimeShiftStartPositionChanged;
private int mTimeShiftCurrentPositionChanged;
@Override
public void onTimeShiftStartPositionChanged(String inputId, long timeMs) {
mTimeShiftStartPositionChanged++;
}
@Override
public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) {
mTimeShiftCurrentPositionChanged++;
}
public void resetCounts() {
mTimeShiftStartPositionChanged = 0;
mTimeShiftCurrentPositionChanged = 0;
}
}
private static Bundle createTestBundle() {
Bundle b = new Bundle();
b.putString("stringKey", new String("Test String"));
return b;
}
@Before
public void setUp() {
mInstrumentation = InstrumentationRegistry
.getInstrumentation();
mTvRecordingClient = new TvRecordingClient(mInstrumentation.getTargetContext(),
"TvInputServiceTest",
mRecordingCallback, null);
mManager = (TvInputManager) mInstrumentation.getTargetContext().getSystemService(
Context.TV_INPUT_SERVICE);
for (TvInputInfo info : mManager.getTvInputList()) {
if (info.getServiceInfo().name.equals(CountingTvInputService.class.getName())) {
mStubInfo = info;
}
if (info.getServiceInfo().name.equals(FaultyTvInputService.class.getName())) {
mFaultyStubInfo = info;
}
if (mStubInfo != null && mFaultyStubInfo != null) {
break;
}
}
assertThat(mStubInfo).isNotNull();
CountingTvInputService.sSession = null;
resetCounts();
resetPassedValues();
}
@After
public void tearDown() {
activityRule.getScenario().onActivity(activity -> {
activity.getTvView().reset();
});
}
@Test
public void verifyCommandTuneForRecording() {
final CountingRecordingSession session = tuneForRecording(CHANNEL_0);
assertThat(session.mSessionId).isNotEmpty();
assertThat(session.mTuneCount).isEqualTo(1);
assertThat(session.mTunedChannelUri).isEqualTo(CHANNEL_0);
}
@Test
public void verifyCommandTuneForRecordingWithBundle() {
final Bundle bundle = createTestBundle();
final CountingRecordingSession session = tuneForRecording(CHANNEL_0, bundle);
assertThat(session.mSessionId).isNotEmpty();
assertThat(session.mTuneCount).isEqualTo(1);
assertThat(session.mTuneWithBundleCount).isEqualTo(1);
assertThat(session.mTunedChannelUri).isEqualTo(CHANNEL_0);
assertBundlesAreEqual(session.mTuneWithBundleData, bundle);
}
@Test
public void verifyCommandRelease() {
final CountingRecordingSession session = tuneForRecording(CHANNEL_0);
mTvRecordingClient.release();
PollingCheck.waitFor(TIME_OUT, () -> session.mReleaseCount > 0);
assertThat(session.mReleaseCount).isEqualTo(1);
}
@Test
public void verifyCommandStartRecording() {
final CountingRecordingSession session = tuneForRecording(CHANNEL_0);
notifyTuned(CHANNEL_0);
mTvRecordingClient.startRecording(CHANNEL_0);
PollingCheck.waitFor(TIME_OUT, () -> session.mStartRecordingCount > 0);
assertThat(session.mStartRecordingCount).isEqualTo(1);
assertThat(session.mProgramHint).isEqualTo(CHANNEL_0);
}
@Test
public void verifyCommandStartRecordingWithBundle() {
Bundle bundle = createTestBundle();
final CountingRecordingSession session = tuneForRecording(CHANNEL_0, bundle);
notifyTuned(CHANNEL_0);
mTvRecordingClient.startRecording(CHANNEL_0, bundle);
PollingCheck.waitFor(TIME_OUT, () -> session.mStartRecordingWithBundleCount > 0);
assertThat(session.mStartRecordingCount).isEqualTo(1);
assertThat(session.mStartRecordingWithBundleCount).isEqualTo(1);
assertThat(session.mProgramHint).isEqualTo(CHANNEL_0);
assertBundlesAreEqual(session.mStartRecordingWithBundleData, bundle);
}
@Test
public void verifyCommandPauseResumeRecordingWithBundle() {
final CountingRecordingSession session = tuneForRecording(CHANNEL_0);
notifyTuned(CHANNEL_0);
mTvRecordingClient.startRecording(CHANNEL_0);
final Bundle bundle = createTestBundle();
mTvRecordingClient.pauseRecording(bundle);
PollingCheck.waitFor(TIME_OUT, () -> session.mPauseRecordingWithBundleCount > 0);
assertThat(session.mPauseRecordingWithBundleCount).isEqualTo(1);
mTvRecordingClient.resumeRecording(bundle);
PollingCheck.waitFor(TIME_OUT, () -> session.mResumeRecordingWithBundleCount > 0);
assertThat(session.mResumeRecordingWithBundleCount).isEqualTo(1);
assertBundlesAreEqual(session.mResumeRecordingWithBundleData, bundle);
}
@Test
public void verifyCommandPauseResumeRecording() {
final CountingRecordingSession session = tuneForRecording(CHANNEL_0);
notifyTuned(CHANNEL_0);
mTvRecordingClient.startRecording(CHANNEL_0);
mTvRecordingClient.pauseRecording();
PollingCheck.waitFor(TIME_OUT, () -> session.mPauseRecordingWithBundleCount > 0);
assertThat(session.mPauseRecordingWithBundleCount).isEqualTo(1);
mTvRecordingClient.resumeRecording();
PollingCheck.waitFor(TIME_OUT, () -> session.mResumeRecordingWithBundleCount > 0);
assertThat(session.mPauseRecordingWithBundleCount).isEqualTo(1);
assertBundlesAreEqual(session.mResumeRecordingWithBundleData, Bundle.EMPTY);
}
@Test
public void verifyCommandStopRecording() {
final CountingRecordingSession session = tuneForRecording(CHANNEL_0);
notifyTuned(CHANNEL_0);
mTvRecordingClient.startRecording(CHANNEL_0);
mTvRecordingClient.stopRecording();
PollingCheck.waitFor(TIME_OUT, () -> session.mStopRecordingCount > 0);
assertThat(session.mStopRecordingCount).isEqualTo(1);
}
@Test
public void verifyCommandSendAppPrivateCommandForRecording() {
Bundle bundle = createTestBundle();
final CountingRecordingSession session = tuneForRecording(CHANNEL_0);
final String action = "android.media.tv.cts.TvInputServiceTest.privateCommand";
mTvRecordingClient.sendAppPrivateCommand(action, bundle);
PollingCheck.waitFor(TIME_OUT, () -> session.mAppPrivateCommandCount > 0);
assertThat(session.mAppPrivateCommandCount).isEqualTo(1);
assertBundlesAreEqual(session.mAppPrivateCommandData, bundle);
assertThat(session.mAppPrivateCommandAction).isEqualTo(action);
}
@Test
public void verifyCallbackTuned() {
tuneForRecording(CHANNEL_0);
notifyTuned(CHANNEL_0);
assertThat(mRecordingCallback.mTunedCount).isEqualTo(1);
assertThat(mRecordingCallback.mTunedChannelUri).isEqualTo(CHANNEL_0);
}
@Test
public void verifyCallbackError() {
final CountingRecordingSession session = tuneForRecording(CHANNEL_0);
notifyTuned(CHANNEL_0);
mTvRecordingClient.startRecording(CHANNEL_0);
final int error = TvInputManager.RECORDING_ERROR_UNKNOWN;
session.notifyError(error);
PollingCheck.waitFor(TIME_OUT, () -> mRecordingCallback.mErrorCount > 0);
assertThat(mRecordingCallback.mErrorCount).isEqualTo(1);
assertThat(mRecordingCallback.mError).isEqualTo(error);
}
@Test
public void verifyCallbackRecordingStopped() {
final CountingRecordingSession session = tuneForRecording(CHANNEL_0);
notifyTuned(CHANNEL_0);
mTvRecordingClient.startRecording(CHANNEL_0);
session.notifyRecordingStopped(CHANNEL_0);
PollingCheck.waitFor(TIME_OUT, () -> mRecordingCallback.mRecordingStoppedCount > 0);
assertThat(mRecordingCallback.mRecordingStoppedCount).isEqualTo(1);
assertThat(mRecordingCallback.mRecordedProgramUri).isEqualTo(CHANNEL_0);
}
@Test
public void verifyCallbackConnectionFailed() {
resetCounts();
mTvRecordingClient.tune("invalid_input_id", CHANNEL_0);
PollingCheck.waitFor(TIME_OUT, () -> mRecordingCallback.mConnectionFailedCount > 0);
assertThat(mRecordingCallback.mConnectionFailedCount).isEqualTo(1);
}
@Test
@Ignore("b/216866512")
public void verifyCallbackDisconnected() {
resetCounts();
mTvRecordingClient.tune(mFaultyStubInfo.getId(), CHANNEL_0);
PollingCheck.waitFor(TIME_OUT, () -> mRecordingCallback.mDisconnectedCount > 0);
}
@Test
public void verifyCommandTune() {
resetCounts();
resetPassedValues();
final CountingSession session = tune(CHANNEL_0);
assertWithMessage("session").that(session).isNotNull();
assertWithMessage("tvInputSessionId").that(session.mSessionId).isNotEmpty();
assertWithMessage("mTuneCount").that(session.mTuneCount).isGreaterThan(0);
assertWithMessage("mCreateOverlayView").that(session.mCreateOverlayView).isGreaterThan(0);
assertWithMessage("mTunedChannelUri").that(session.mTunedChannelUri).isEqualTo(CHANNEL_0);
}
@Test
public void verifyCommandTuneWithBundle() {
Bundle bundle = createTestBundle();
resetCounts();
resetPassedValues();
onTvView(tvView -> tvView.tune(mStubInfo.getId(), CHANNEL_0, bundle));
final CountingSession session = waitForSessionCheck(s -> s.mTuneWithBundleCount > 0);
assertThat(session.mTuneCount).isEqualTo(1);
assertThat(session.mTuneWithBundleCount).isEqualTo(1);
assertThat(session.mTunedChannelUri).isEqualTo(CHANNEL_0);
assertBundlesAreEqual(session.mTuneWithBundleData, bundle);
}
@Test
public void verifyCommandSetStreamVolume() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final float volume = 0.8f;
onTvView(tvView -> tvView.setStreamVolume(volume));
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT, () -> session.mSetStreamVolumeCount > 0);
assertThat(session.mSetStreamVolumeCount).isEqualTo(1);
assertThat(session.mStreamVolume).isEqualTo(volume);
}
@Test
public void verifyCommandSetCaptionEnabled() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final boolean enable = true;
onTvView(tvView -> tvView.setCaptionEnabled(enable));
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT, () -> session.mSetCaptionEnabledCount > 0);
assertThat(session.mSetCaptionEnabledCount).isEqualTo(1);
assertThat(session.mCaptionEnabled).isEqualTo(enable);
}
@Test
public void verifyCommandSelectTrack() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
verifyCallbackTracksChanged();
final int dummyTrackType = TEST_TV_TRACK.getType();
final String dummyTrackId = TEST_TV_TRACK.getId();
onTvView(tvView -> tvView.selectTrack(dummyTrackType, dummyTrackId));
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT, () -> session.mSelectTrackCount > 0);
assertThat(session.mSelectTrackCount).isEqualTo(1);
assertThat(session.mSelectTrackType).isEqualTo(dummyTrackType);
assertThat(session.mSelectTrackId).isEqualTo(dummyTrackId);
}
@Test
public void verifyCommandDispatchKeyDown() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final int keyCode = KeyEvent.KEYCODE_Q;
final KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
onTvView(tvView -> tvView.dispatchKeyEvent(event));
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT, () -> session.mKeyDownCount > 0);
assertThat(session.mKeyDownCount).isEqualTo(1);
assertThat(session.mKeyDownCode).isEqualTo(keyCode);
assertKeyEventEquals(session.mKeyDownEvent, event);
}
@Test
public void verifyCommandDispatchKeyMultiple() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final int keyCode = KeyEvent.KEYCODE_Q;
final KeyEvent event = new KeyEvent(KeyEvent.ACTION_MULTIPLE, keyCode);
onTvView(tvView -> tvView.dispatchKeyEvent(event));
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT, () -> session.mKeyMultipleCount > 0);
assertThat(session.mKeyMultipleCount).isEqualTo(1);
assertKeyEventEquals(session.mKeyMultipleEvent, event);
assertThat(session.mKeyMultipleNumber).isEqualTo(event.getRepeatCount());
}
@Test
public void verifyCommandDispatchKeyUp() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final int keyCode = KeyEvent.KEYCODE_Q;
final KeyEvent event = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
onTvView(tvView -> tvView.dispatchKeyEvent(event));
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT, () -> session.mKeyUpCount > 0);
assertThat(session.mKeyUpCount).isEqualTo(1);
assertThat(session.mKeyUpCode).isEqualTo(keyCode);
assertKeyEventEquals(session.mKeyUpEvent, event);
}
@Test
public void verifyCommandDispatchTouchEvent() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final long now = SystemClock.uptimeMillis();
final MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
onTvView(tvView -> tvView.dispatchTouchEvent(event));
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT, () -> session.mTouchEventCount > 0);
assertThat(session.mTouchEventCount).isEqualTo(1);
assertMotionEventEquals(session.mTouchEvent, event);
}
@Test
public void verifyCommandDispatchTrackballEvent() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final long now = SystemClock.uptimeMillis();
final MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
event.setSource(InputDevice.SOURCE_TRACKBALL);
onTvView(tvView -> tvView.dispatchTouchEvent(event));
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT, () -> session.mTrackballEventCount > 0);
assertThat(session.mTrackballEventCount).isEqualTo(1);
assertMotionEventEquals(session.mTrackballEvent, event);
}
@Test
public void verifyCommandDispatchGenericMotionEvent() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final long now = SystemClock.uptimeMillis();
final MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
event.setSource(InputDevice.SOURCE_UNKNOWN);
onTvView(tvView -> tvView.dispatchGenericMotionEvent(event));
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT, () -> session.mGenricMotionEventCount > 0);
assertThat(session.mGenricMotionEventCount).isEqualTo(1);
assertMotionEventEquals(session.mGenricMotionEvent, event);
}
@Test
public void verifyCommandTimeShiftPause() {
final CountingSession session = tune(CHANNEL_0);
onTvView(tvView -> tvView.timeShiftPause());
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT, () -> session.mTimeShiftPauseCount > 0);
assertThat(session.mTimeShiftPauseCount).isEqualTo(1);
}
@Test
public void verifyCommandTimeShiftResume() {
final CountingSession session = tune(CHANNEL_0);
onTvView(tvView -> {
tvView.timeShiftResume();
});
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT, () -> session.mTimeShiftResumeCount > 0);
assertThat(session.mTimeShiftResumeCount).isEqualTo(1);
}
@Test
public void verifyCommandTimeShiftSeekTo() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final long timeMs = 0;
onTvView(tvView -> tvView.timeShiftSeekTo(timeMs));
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT, () -> session.mTimeShiftSeekToCount > 0);
assertThat(session.mTimeShiftSeekToCount).isEqualTo(1);
assertThat(session.mTimeShiftSeekTo).isEqualTo(timeMs);
}
@Test
public void verifyCommandTimeShiftSetPlaybackParams() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final PlaybackParams param = new PlaybackParams().setSpeed(2.0f)
.setAudioFallbackMode(PlaybackParams.AUDIO_FALLBACK_MODE_DEFAULT);
onTvView(tvView -> tvView.timeShiftSetPlaybackParams(param));
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT,
() -> session != null && session.mTimeShiftSetPlaybackParamsCount > 0);
assertThat(session.mTimeShiftSetPlaybackParamsCount).isEqualTo(1);
assertPlaybackParamsEquals(session.mTimeShiftSetPlaybackParams, param);
}
@Test
public void verifyCommandTimeShiftPlay() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final Uri fakeRecordedProgramUri = TvContract.buildRecordedProgramUri(0);
onTvView(tvView -> tvView.timeShiftPlay(mStubInfo.getId(), fakeRecordedProgramUri));
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT, () -> session.mTimeShiftPlayCount > 0);
assertThat(session.mTimeShiftPlayCount).isEqualTo(1);
assertThat(session.mRecordedProgramUri).isEqualTo(fakeRecordedProgramUri);
}
@Test
public void verifyCommandSetTimeShiftPositionCallback() {
tune(CHANNEL_0);
onTvView(tvView -> tvView.setTimeShiftPositionCallback(mTimeShiftPositionCallback));
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT,
() -> mTimeShiftPositionCallback.mTimeShiftCurrentPositionChanged > 0
&& mTimeShiftPositionCallback.mTimeShiftStartPositionChanged > 0);
assertThat(mTimeShiftPositionCallback.mTimeShiftCurrentPositionChanged).isEqualTo(1);
assertThat(mTimeShiftPositionCallback.mTimeShiftStartPositionChanged).isEqualTo(1);
}
@Test
public void verifyCommandOverlayViewSizeChanged() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final int width = 10;
final int height = 20;
// There is a first OverlayViewSizeChange called on initial tune.
assertThat(session.mOverlayViewSizeChangedCount).isEqualTo(1);
onTvView(tvView -> tvView.setLayoutParams(new LinearLayout.LayoutParams(width, height)));
PollingCheck.waitFor(TIME_OUT, () -> session.mOverlayViewSizeChangedCount > 1);
assertThat(session.mOverlayViewSizeChangedCount).isEqualTo(2);
assertThat(session.mOverlayViewSizeChangedWidth).isEqualTo(width);
assertThat(session.mOverlayViewSizeChangedHeight).isEqualTo(height);
}
@Test
public void verifyCommandSendAppPrivateCommand() {
Bundle bundle = createTestBundle();
tune(CHANNEL_0);
final String action = "android.media.tv.cts.TvInputServiceTest.privateCommand";
onTvView(tvView -> tvView.sendAppPrivateCommand(action, bundle));
mInstrumentation.waitForIdleSync();
final CountingSession session = waitForSessionCheck(s -> s.mAppPrivateCommandCount > 0);
assertThat(session.mAppPrivateCommandCount).isEqualTo(1);
assertBundlesAreEqual(session.mAppPrivateCommandData, bundle);
assertThat(session.mAppPrivateCommandAction).isEqualTo(action);
}
@Test
public void verifyCommandSetInteractiveAppNotificationEnabled() {
tune(CHANNEL_0);
final String action =
"android.media.tv.cts.TvInputServiceTest.setInteractiveAppNotificationEnabled";
onTvView(tvView -> tvView.setInteractiveAppNotificationEnabled(true));
mInstrumentation.waitForIdleSync();
final CountingSession session =
waitForSessionCheck(s -> s.mSetInteractiveAppNotificationEnabledCount > 0);
assertThat(session.mSetInteractiveAppNotificationEnabledCount).isEqualTo(1);
assertThat(session.mInteractiveAppNotificationEnabled).isEqualTo(true);
}
@Test
public void verifyCallbackChannelRetuned() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
session.notifyChannelRetuned(CHANNEL_0);
PollingCheck.waitFor(TIME_OUT, () -> mCallback.mChannelRetunedCount > 0);
assertThat(mCallback.mChannelRetunedCount).isEqualTo(1);
assertThat(mCallback.mChannelRetunedUri).isEqualTo(CHANNEL_0);
}
@Test
public void verifyCallbackVideoAvailable() {
final CountingSession session = tune(CHANNEL_0);
resetCounts();
session.notifyVideoAvailable();
PollingCheck.waitFor(TIME_OUT, () -> mCallback.mVideoAvailableCount > 0);
assertThat(mCallback.mVideoAvailableCount).isEqualTo(1);
}
@Test
public void verifyCallbackVideoUnavailable() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final int reason = TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING;
session.notifyVideoUnavailable(reason);
PollingCheck.waitFor(TIME_OUT, () -> mCallback.mVideoUnavailableCount > 0);
assertThat(mCallback.mVideoUnavailableCount).isEqualTo(1);
assertThat(mCallback.mVideoUnavailableReason).isEqualTo(reason);
}
@Test
public void verifyCallbackTracksChanged() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
ArrayList<TvTrackInfo> tracks = new ArrayList<>();
tracks.add(TEST_TV_TRACK);
session.notifyTracksChanged(tracks);
PollingCheck.waitFor(TIME_OUT, () -> mCallback.mTrackChangedCount > 0
&& Objects.equals(mCallback.mTracksChangedTrackList, tracks));
assertThat(mCallback.mTrackChangedCount).isEqualTo(1);
assertThat(mCallback.mTracksChangedTrackList).isEqualTo(tracks);
}
@Test
@Ignore("b/174076887")
public void verifyCallbackVideoSizeChanged() {
final CountingSession session = tune(CHANNEL_0);
resetCounts();
ArrayList<TvTrackInfo> tracks = new ArrayList<>();
tracks.add(TEST_TV_TRACK);
session.notifyTracksChanged(tracks);
mInstrumentation.waitForIdleSync();
PollingCheck.waitFor(TIME_OUT, () -> mCallback.mVideoSizeChanged > 0);
assertThat(mCallback.mVideoSizeChanged).isEqualTo(1);
}
@Test
public void verifyCallbackTrackSelected() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
session.notifyTrackSelected(TEST_TV_TRACK.getType(), TEST_TV_TRACK.getId());
PollingCheck.waitFor(TIME_OUT, () -> mCallback.mTrackSelectedCount > 0);
assertThat(mCallback.mTrackSelectedCount).isEqualTo(1);
assertThat(mCallback.mTrackSelectedType).isEqualTo(TEST_TV_TRACK.getType());
assertThat(mCallback.mTrackSelectedTrackId).isEqualTo(TEST_TV_TRACK.getId());
}
@Test
public void verifyCallbackContentAllowed() {
final CountingSession session = tune(CHANNEL_0);
resetCounts();
session.notifyContentAllowed();
PollingCheck.waitFor(TIME_OUT, () -> mCallback.mContentAllowedCount > 0);
assertThat(mCallback.mContentAllowedCount).isEqualTo(1);
}
@Test
public void verifyCallbackContentBlocked() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG",
"US_TVPG_TV_MA", "US_TVPG_S", "US_TVPG_V");
session.notifyContentBlocked(rating);
PollingCheck.waitFor(TIME_OUT, () -> mCallback.mContentBlockedCount > 0);
assertThat(mCallback.mContentBlockedCount).isEqualTo(1);
assertThat(mCallback.mContentBlockedRating).isEqualTo(rating);
}
@Test
public void verifyCallbackTimeShiftStatusChanged() {
final CountingSession session = tune(CHANNEL_0);
resetPassedValues();
final int status = TvInputManager.TIME_SHIFT_STATUS_AVAILABLE;
session.notifyTimeShiftStatusChanged(status);
PollingCheck.waitFor(TIME_OUT, () -> mCallback.mTimeShiftStatusChangedCount > 0);
assertThat(mCallback.mTimeShiftStatusChangedCount).isEqualTo(1);
assertThat(mCallback.mTimeShiftStatusChangedStatus).isEqualTo(status);
}
@Test
public void verifyCallbackAitInfoUpdated() {
final CountingSession session = tune(CHANNEL_0);
resetCounts();
resetPassedValues();
session.notifyAitInfoUpdated(
new AitInfo(TvInteractiveAppServiceInfo.INTERACTIVE_APP_TYPE_HBBTV, 2));
PollingCheck.waitFor(TIME_OUT, () -> mCallback.mAitInfoUpdatedCount > 0);
assertThat(mCallback.mAitInfoUpdatedCount).isEqualTo(1);
assertThat(mCallback.mAitInfo.getType())
.isEqualTo(TvInteractiveAppServiceInfo.INTERACTIVE_APP_TYPE_HBBTV);
assertThat(mCallback.mAitInfo.getVersion()).isEqualTo(2);
}
@Test
public void verifyCallbackLayoutSurface() {
final CountingSession session = tune(CHANNEL_0);
final int left = 10;
final int top = 20;
final int right = 30;
final int bottom = 40;
session.layoutSurface(left, top, right, bottom);
PollingCheck.waitFor(TIME_OUT, () -> {
final AtomicBoolean retValue = new AtomicBoolean();
onTvView(tvView -> {
int childCount = tvView.getChildCount();
for (int i = 0; i < childCount; ++i) {
View v = tvView.getChildAt(i);
if (v instanceof SurfaceView) {
retValue.set(v.getLeft() == left && v.getTop() == top
&& v.getRight() == right
&& v.getBottom() == bottom
);
break;
}
}
});
mInstrumentation.waitForIdleSync();
return retValue.get();
});
}
public static void assertKeyEventEquals(KeyEvent actual, KeyEvent expected) {
if ((expected == null) != (actual == null)) {
// Fail miss matched nulls early using the StandardSubject
Truth.assertThat(actual).isEqualTo(expected);
} else if (expected != null && actual != null) {
assertThat(actual.getDownTime()).isEqualTo(expected.getDownTime());
assertThat(actual.getEventTime()).isEqualTo(expected.getEventTime());
assertThat(actual.getAction()).isEqualTo(expected.getAction());
assertThat(actual.getKeyCode()).isEqualTo(expected.getKeyCode());
assertThat(actual.getRepeatCount()).isEqualTo(expected.getRepeatCount());
assertThat(actual.getMetaState()).isEqualTo(expected.getMetaState());
assertThat(actual.getDeviceId()).isEqualTo(expected.getDeviceId());
assertThat(actual.getScanCode()).isEqualTo(expected.getScanCode());
assertThat(actual.getFlags()).isEqualTo(expected.getFlags());
assertThat(actual.getSource()).isEqualTo(expected.getSource());
assertThat(actual.getCharacters()).isEqualTo(expected.getCharacters());
}// else both null so do nothing
}
public static void assertMotionEventEquals(MotionEvent actual, MotionEvent expected) {
if ((expected == null) != (actual == null)) {
// Fail miss matched nulls early using the StandardSubject
Truth.assertThat(actual).isEqualTo(expected);
} else if (expected != null && actual != null) {
assertThat(actual).hasDownTime(expected.getDownTime());
assertThat(actual).hasEventTime(expected.getEventTime());
assertThat(actual).hasAction(expected.getAction());
assertThat(actual).x().isEqualTo(expected.getX());
assertThat(actual).y().isEqualTo(expected.getY());
assertThat(actual).pressure().isEqualTo(expected.getPressure());
assertThat(actual).size().isEqualTo(expected.getSize());
assertThat(actual).hasMetaState(expected.getMetaState());
assertThat(actual).xPrecision().isEqualTo(expected.getXPrecision());
assertThat(actual).yPrecision().isEqualTo(expected.getYPrecision());
assertThat(actual).hasDeviceId(expected.getDeviceId());
assertThat(actual).hasEdgeFlags(expected.getEdgeFlags());
assertThat(actual.getSource()).isEqualTo(expected.getSource());
} // else both null so do nothing
}
public static void assertPlaybackParamsEquals(PlaybackParams actual, PlaybackParams expected) {
if ((expected == null) != (actual == null)) {
// Fail miss matched nulls early using the StandardSubject
Truth.assertThat(actual).isEqualTo(expected);
} else if (expected != null && actual != null) {
assertThat(actual.getAudioFallbackMode()).isEqualTo(expected.getAudioFallbackMode());
assertThat(actual.getSpeed()).isEqualTo(expected.getSpeed());
} // else both null so do nothing
}
private static void assertBundlesAreEqual(Bundle actual, Bundle expected) {
if ((expected == null) != (actual == null)) {
// Fail miss matched nulls early using the StandardSubject
Truth.assertThat(actual).isEqualTo(expected);
} else if (expected != null && actual != null) {
assertThat(actual.keySet()).isEqualTo(expected.keySet());
for (String key : expected.keySet()) {
assertThat(actual.get(key)).isEqualTo(expected.get(key));
}
}
}
private void notifyTuned(Uri uri) {
final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
session.notifyTuned(uri);
PollingCheck.waitFor(TIME_OUT, () -> mRecordingCallback.mTunedCount > 0);
}
private void onTvView(Consumer<TvView> tvViewConsumer) {
activityRule.getScenario().onActivity(viewAction(tvViewConsumer));
}
private void resetCounts() {
if (CountingTvInputService.sSession != null) {
CountingTvInputService.sSession.resetCounts();
}
if (CountingTvInputService.sRecordingSession != null) {
CountingTvInputService.sRecordingSession.resetCounts();
}
mCallback.resetCounts();
mTimeShiftPositionCallback.resetCounts();
mRecordingCallback.resetCounts();
}
private void resetPassedValues() {
if (CountingTvInputService.sSession != null) {
CountingTvInputService.sSession.resetPassedValues();
}
if (CountingTvInputService.sRecordingSession != null) {
CountingTvInputService.sRecordingSession.resetPassedValues();
}
mCallback.resetPassedValues();
mRecordingCallback.resetPassedValues();
}
@NonNull
private static PollingCheck.PollingCheckCondition recordingSessionCheck(
ToBooleanFunction<CountingRecordingSession> toBooleanFunction) {
return () -> {
final CountingRecordingSession session = CountingTvInputService.sRecordingSession;
return session != null && toBooleanFunction.apply(session);
};
}
@NonNull
private static PollingCheck.PollingCheckCondition sessionCheck(
ToBooleanFunction<CountingSession> toBooleanFunction) {
return () -> {
final CountingSession session = CountingTvInputService.sSession;
return session != null && toBooleanFunction.apply(session);
};
}
@NonNull
private CountingSession tune(Uri uri) {
onTvView(tvView -> {
tvView.setCallback(mCallback);
tvView.tune(mStubInfo.getId(), CHANNEL_0);
});
return waitForSessionCheck(session -> session.mTuneCount > 0);
}
@NonNull
private CountingRecordingSession tuneForRecording(Uri uri) {
mTvRecordingClient.tune(mStubInfo.getId(), uri);
return waitForRecordingSessionCheck(s -> s.mTuneCount > 0);
}
@NonNull
private CountingRecordingSession tuneForRecording(Uri uri, Bundle bundle) {
mTvRecordingClient.tune(mStubInfo.getId(), uri, bundle);
return waitForRecordingSessionCheck(s -> s.mTuneCount > 0 && s.mTuneWithBundleCount > 0);
}
@NonNull
private static ActivityScenario.ActivityAction<TvViewStubActivity> viewAction(
Consumer<TvView> consumer) {
return activity -> consumer.accept(activity.getTvView());
}
@NonNull
private static CountingSession waitForSessionCheck(
ToBooleanFunction<CountingSession> countingSessionToBooleanFunction) {
PollingCheck.waitFor(TIME_OUT, sessionCheck(countingSessionToBooleanFunction));
return CountingTvInputService.sSession;
}
@NonNull
private static CountingRecordingSession waitForRecordingSessionCheck(
ToBooleanFunction<CountingRecordingSession> toBool) {
PollingCheck.waitFor(TIME_OUT, recordingSessionCheck(toBool));
return CountingTvInputService.sRecordingSession;
}
public static class CountingTvInputService extends StubTvInputService {
static CountingSession sSession;
static CountingRecordingSession sRecordingSession;
@Override
public Session onCreateSession(String inputId) {
return onCreateSession(inputId, null);
}
@Override
public Session onCreateSession(String inputId, String tvInputSessionId) {
if(sSession != null){
Log.w(TAG,"onCreateSession called with sSession set to "+ sSession);
}
sSession = new CountingSession(this, tvInputSessionId);
sSession.setOverlayViewEnabled(true);
return sSession;
}
@Override
public RecordingSession onCreateRecordingSession(String inputId) {
return onCreateRecordingSession(inputId, null);
}
@Override
public RecordingSession onCreateRecordingSession(String inputId, String tvInputSessionId) {
if (sRecordingSession != null) {
Log.w(TAG, "onCreateRecordingSession called with sRecordingSession set to "
+ sRecordingSession);
}
sRecordingSession = new CountingRecordingSession(this, tvInputSessionId);
return sRecordingSession;
}
@Override
public IBinder createExtension() {
super.createExtension();
return null;
}
public static class CountingSession extends Session {
public final String mSessionId;
public volatile int mTuneCount;
public volatile int mTuneWithBundleCount;
public volatile int mSetStreamVolumeCount;
public volatile int mSetCaptionEnabledCount;
public volatile int mSelectTrackCount;
public volatile int mCreateOverlayView;
public volatile int mKeyDownCount;
public volatile int mKeyLongPressCount;
public volatile int mKeyMultipleCount;
public volatile int mKeyUpCount;
public volatile int mTouchEventCount;
public volatile int mTrackballEventCount;
public volatile int mGenricMotionEventCount;
public volatile int mOverlayViewSizeChangedCount;
public volatile int mTimeShiftPauseCount;
public volatile int mTimeShiftResumeCount;
public volatile int mTimeShiftSeekToCount;
public volatile int mTimeShiftSetPlaybackParamsCount;
public volatile int mTimeShiftPlayCount;
public volatile long mTimeShiftGetCurrentPositionCount;
public volatile long mTimeShiftGetStartPositionCount;
public volatile int mAppPrivateCommandCount;
public volatile int mSetInteractiveAppNotificationEnabledCount;
public volatile String mAppPrivateCommandAction;
public volatile Bundle mAppPrivateCommandData;
public volatile Uri mTunedChannelUri;
public volatile Bundle mTuneWithBundleData;
public volatile Float mStreamVolume;
public volatile Boolean mCaptionEnabled;
public volatile Integer mSelectTrackType;
public volatile String mSelectTrackId;
public volatile Integer mKeyDownCode;
public volatile KeyEvent mKeyDownEvent;
public volatile Integer mKeyLongPressCode;
public volatile KeyEvent mKeyLongPressEvent;
public volatile Integer mKeyMultipleCode;
public volatile Integer mKeyMultipleNumber;
public volatile KeyEvent mKeyMultipleEvent;
public volatile Integer mKeyUpCode;
public volatile KeyEvent mKeyUpEvent;
public volatile MotionEvent mTouchEvent;
public volatile MotionEvent mTrackballEvent;
public volatile MotionEvent mGenricMotionEvent;
public volatile Long mTimeShiftSeekTo;
public volatile PlaybackParams mTimeShiftSetPlaybackParams;
public volatile Uri mRecordedProgramUri;
public volatile Integer mOverlayViewSizeChangedWidth;
public volatile Integer mOverlayViewSizeChangedHeight;
public volatile Boolean mInteractiveAppNotificationEnabled;
CountingSession(Context context, @Nullable String sessionId) {
super(context);
mSessionId = sessionId;
}
public void resetCounts() {
mTuneCount = 0;
mTuneWithBundleCount = 0;
mSetStreamVolumeCount = 0;
mSetCaptionEnabledCount = 0;
mSelectTrackCount = 0;
mCreateOverlayView = 0;
mKeyDownCount = 0;
mKeyLongPressCount = 0;
mKeyMultipleCount = 0;
mKeyUpCount = 0;
mTouchEventCount = 0;
mTrackballEventCount = 0;
mGenricMotionEventCount = 0;
mOverlayViewSizeChangedCount = 0;
mTimeShiftPauseCount = 0;
mTimeShiftResumeCount = 0;
mTimeShiftSeekToCount = 0;
mTimeShiftSetPlaybackParamsCount = 0;
mTimeShiftPlayCount = 0;
mTimeShiftGetCurrentPositionCount = 0;
mTimeShiftGetStartPositionCount = 0;
mAppPrivateCommandCount = 0;
mSetInteractiveAppNotificationEnabledCount = 0;
}
public void resetPassedValues() {
mAppPrivateCommandAction = null;
mAppPrivateCommandData = null;
mTunedChannelUri = null;
mTuneWithBundleData = null;
mStreamVolume = null;
mCaptionEnabled = null;
mSelectTrackType = null;
mSelectTrackId = null;
mKeyDownCode = null;
mKeyDownEvent = null;
mKeyLongPressCode = null;
mKeyLongPressEvent = null;
mKeyMultipleCode = null;
mKeyMultipleNumber = null;
mKeyMultipleEvent = null;
mKeyUpCode = null;
mKeyUpEvent = null;
mTouchEvent = null;
mTrackballEvent = null;
mGenricMotionEvent = null;
mTimeShiftSeekTo = null;
mTimeShiftSetPlaybackParams = null;
mRecordedProgramUri = null;
mOverlayViewSizeChangedWidth = null;
mOverlayViewSizeChangedHeight = null;
mInteractiveAppNotificationEnabled = null;
}
@Override
public void onAppPrivateCommand(String action, Bundle data) {
mAppPrivateCommandCount++;
mAppPrivateCommandAction = action;
mAppPrivateCommandData = data;
}
@Override
public void onRelease() {
}
@Override
public boolean onSetSurface(Surface surface) {
return false;
}
@Override
public boolean onTune(Uri channelUri) {
mTuneCount++;
mTunedChannelUri = channelUri;
return false;
}
@Override
public boolean onTune(Uri channelUri, Bundle data) {
mTuneWithBundleCount++;
mTuneWithBundleData = data;
// Also calls {@link #onTune(Uri)} since it will never be called if the
// implementation overrides {@link #onTune(Uri, Bundle)}.
onTune(channelUri);
return false;
}
@Override
public void onSetStreamVolume(float volume) {
mSetStreamVolumeCount++;
mStreamVolume = volume;
}
@Override
public void onSetCaptionEnabled(boolean enabled) {
mSetCaptionEnabledCount++;
mCaptionEnabled = enabled;
}
@Override
public boolean onSelectTrack(int type, String id) {
mSelectTrackCount++;
mSelectTrackType = type;
mSelectTrackId = id;
return false;
}
@Override
public View onCreateOverlayView() {
mCreateOverlayView++;
return null;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
mKeyDownCount++;
mKeyDownCode = keyCode;
mKeyDownEvent = event;
return false;
}
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
mKeyLongPressCount++;
mKeyLongPressCode = keyCode;
mKeyLongPressEvent = event;
return false;
}
@Override
public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
mKeyMultipleCount++;
mKeyMultipleCode = keyCode;
mKeyMultipleNumber = count;
mKeyMultipleEvent = event;
return false;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
mKeyUpCount++;
mKeyUpCode = keyCode;
mKeyUpEvent = event;
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mTouchEventCount++;
mTouchEvent = event;
return false;
}
@Override
public boolean onTrackballEvent(MotionEvent event) {
mTrackballEventCount++;
mTrackballEvent = event;
return false;
}
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
mGenricMotionEventCount++;
mGenricMotionEvent = event;
return false;
}
@Override
public void onTimeShiftPause() {
mTimeShiftPauseCount++;
}
@Override
public void onTimeShiftResume() {
mTimeShiftResumeCount++;
}
@Override
public void onTimeShiftSeekTo(long timeMs) {
mTimeShiftSeekToCount++;
mTimeShiftSeekTo = timeMs;
}
@Override
public void onTimeShiftSetPlaybackParams(PlaybackParams param) {
mTimeShiftSetPlaybackParamsCount++;
mTimeShiftSetPlaybackParams = param;
}
@Override
public void onTimeShiftPlay(Uri recordedProgramUri) {
mTimeShiftPlayCount++;
mRecordedProgramUri = recordedProgramUri;
}
@Override
public long onTimeShiftGetCurrentPosition() {
return ++mTimeShiftGetCurrentPositionCount;
}
@Override
public long onTimeShiftGetStartPosition() {
return ++mTimeShiftGetStartPositionCount;
}
@Override
public void onOverlayViewSizeChanged(int width, int height) {
mOverlayViewSizeChangedCount++;
mOverlayViewSizeChangedWidth = width;
mOverlayViewSizeChangedHeight = height;
}
@Override
public void onSetInteractiveAppNotificationEnabled(boolean enabled) {
mSetInteractiveAppNotificationEnabledCount++;
mInteractiveAppNotificationEnabled = enabled;
}
}
public static class CountingRecordingSession extends RecordingSession {
public final String mSessionId;
public volatile int mTuneCount;
public volatile int mTuneWithBundleCount;
public volatile int mReleaseCount;
public volatile int mStartRecordingCount;
public volatile int mStartRecordingWithBundleCount;
public volatile int mPauseRecordingWithBundleCount;
public volatile int mResumeRecordingWithBundleCount;
public volatile int mStopRecordingCount;
public volatile int mAppPrivateCommandCount;
public volatile Uri mTunedChannelUri;
public volatile Bundle mTuneWithBundleData;
public volatile Uri mProgramHint;
public volatile Bundle mStartRecordingWithBundleData;
public volatile Bundle mPauseRecordingWithBundleData;
public volatile Bundle mResumeRecordingWithBundleData;
public volatile String mAppPrivateCommandAction;
public volatile Bundle mAppPrivateCommandData;
CountingRecordingSession(Context context, @Nullable String sessionId) {
super(context);
mSessionId = sessionId;
}
public void resetCounts() {
mTuneCount = 0;
mTuneWithBundleCount = 0;
mReleaseCount = 0;
mStartRecordingCount = 0;
mStartRecordingWithBundleCount = 0;
mPauseRecordingWithBundleCount = 0;
mResumeRecordingWithBundleCount = 0;
mStopRecordingCount = 0;
mAppPrivateCommandCount = 0;
}
public void resetPassedValues() {
mTunedChannelUri = null;
mTuneWithBundleData = null;
mProgramHint = null;
mStartRecordingWithBundleData = null;
mPauseRecordingWithBundleData = null;
mResumeRecordingWithBundleData = null;
mAppPrivateCommandAction = null;
mAppPrivateCommandData = null;
}
@Override
public void onTune(Uri channelUri) {
mTuneCount++;
mTunedChannelUri = channelUri;
}
@Override
public void onTune(Uri channelUri, Bundle data) {
mTuneWithBundleCount++;
mTuneWithBundleData = data;
// Also calls {@link #onTune(Uri)} since it will never be called if the
// implementation overrides {@link #onTune(Uri, Bundle)}.
onTune(channelUri);
}
@Override
public void onRelease() {
mReleaseCount++;
}
@Override
public void onStartRecording(Uri programHint) {
mStartRecordingCount++;
mProgramHint = programHint;
}
@Override
public void onStartRecording(Uri programHint, Bundle data) {
mStartRecordingWithBundleCount++;
mProgramHint = programHint;
mStartRecordingWithBundleData = data;
// Also calls {@link #onStartRecording(Uri)} since it will never be called if the
// implementation overrides {@link #onStartRecording(Uri, Bundle)}.
onStartRecording(programHint);
}
@Override
public void onPauseRecording(Bundle data) {
mPauseRecordingWithBundleCount++;
mPauseRecordingWithBundleData = data;
}
@Override
public void onResumeRecording(Bundle data) {
mResumeRecordingWithBundleCount++;
mResumeRecordingWithBundleData = data;
}
@Override
public void onStopRecording() {
mStopRecordingCount++;
}
@Override
public void onAppPrivateCommand(String action, Bundle data) {
mAppPrivateCommandCount++;
mAppPrivateCommandAction = action;
mAppPrivateCommandData = data;
}
}
}
private static class StubRecordingCallback extends TvRecordingClient.RecordingCallback {
private int mTunedCount;
private int mRecordingStoppedCount;
private int mErrorCount;
private int mConnectionFailedCount;
private int mDisconnectedCount;
private Uri mTunedChannelUri;
private Uri mRecordedProgramUri;
private Integer mError;
@Override
public void onTuned(Uri channelUri) {
mTunedCount++;
mTunedChannelUri = channelUri;
}
@Override
public void onRecordingStopped(Uri recordedProgramUri) {
mRecordingStoppedCount++;
mRecordedProgramUri = recordedProgramUri;
}
@Override
public void onError(int error) {
mErrorCount++;
mError = error;
}
@Override
public void onConnectionFailed(String inputId) {
mConnectionFailedCount++;
}
@Override
public void onDisconnected(String inputId) {
mDisconnectedCount++;
}
public void resetCounts() {
mTunedCount = 0;
mRecordingStoppedCount = 0;
mErrorCount = 0;
mConnectionFailedCount = 0;
mDisconnectedCount = 0;
}
public void resetPassedValues() {
mTunedChannelUri = null;
mRecordedProgramUri = null;
mError = null;
}
}
// Copied from {@link com.android.internal.util.ToBooleanFunction}
/**
* Represents a function that produces an boolean-valued result. This is the
* {@code boolean}-producing primitive specialization for {@link Function}.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #apply(Object)}.
*
* @param <T> the type of the input to the function
*
* @see Function
*/
@FunctionalInterface
private interface ToBooleanFunction<T> {
/**
* Applies this function to the given argument.
*
* @param value the function argument
* @return the function result
*/
boolean apply(T value);
}
}