| /* |
| * 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.AttributionSource; |
| 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 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; |
| 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; |
| |
| /** |
| * 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 Context mContext; |
| 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(); |
| mContext = mInstrumentation.getTargetContext(); |
| mTvRecordingClient = |
| new TvRecordingClient(mContext, "TvInputServiceTest", mRecordingCallback, null); |
| mManager = (TvInputManager) mContext.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.overrideTvAppAttributionSource(mContext.getAttributionSource()); |
| 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 Session onCreateSession( |
| String inputId, String tvInputSessionId, AttributionSource tvAppAttributionSource) { |
| // todo: add AttributionSource equal check |
| return onCreateSession(inputId, tvInputSessionId); |
| } |
| |
| @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); |
| } |
| |
| } |