blob: 14c829d4bdc85a88ed00d3808b5f869c75b150dd [file] [log] [blame]
/*
* Copyright (C) 2017 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 androidx.leanback.widget;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.ColorDrawable;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewParent;
import androidx.leanback.media.PlaybackTransportControlGlue;
import androidx.leanback.media.PlayerAdapter;
import androidx.leanback.widget.PlaybackSeekDataProvider.ResultCallback;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import java.util.Arrays;
@SmallTest
public class PlaybackTransportRowPresenterTest {
Context mContext;
PlaybackTransportControlGlue mGlue;
PlaybackGlueHostImplWithViewHolder mHost;
PlayerAdapter mImpl;
PlaybackTransportRowPresenter.ViewHolder mViewHolder;
AbstractDetailsDescriptionPresenter.ViewHolder mDescriptionViewHolder;
int mNumbThumbs;
@Before
public void setUp() {
mContext = new ContextThemeWrapper(
InstrumentationRegistry.getInstrumentation().getTargetContext(),
androidx.leanback.test.R.style.Theme_Leanback);
mHost = new PlaybackGlueHostImplWithViewHolder(mContext);
mImpl = Mockito.mock(PlayerAdapter.class);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
mGlue = new PlaybackTransportControlGlue(mContext, mImpl) {
@Override
protected void onCreatePrimaryActions(ArrayObjectAdapter
primaryActionsAdapter) {
super.onCreatePrimaryActions(primaryActionsAdapter);
primaryActionsAdapter.add(
new PlaybackControlsRow.ClosedCaptioningAction(mContext));
}
@Override
protected void onCreateSecondaryActions(ArrayObjectAdapter
secondaryActionsAdapter) {
secondaryActionsAdapter.add(
new PlaybackControlsRow.HighQualityAction(mContext));
secondaryActionsAdapter.add(
new PlaybackControlsRow.PictureInPictureAction(mContext));
}
};
mGlue.setHost(mHost);
}
});
mViewHolder = (PlaybackTransportRowPresenter.ViewHolder) mHost.mViewHolder;
mDescriptionViewHolder = (AbstractDetailsDescriptionPresenter.ViewHolder)
mViewHolder.mDescriptionViewHolder;
mNumbThumbs = mViewHolder.mThumbsBar.getChildCount();
assertTrue((mNumbThumbs & 1) != 0);
}
void sendKeyUIThread(int keyCode) {
sendKeyUIThread(keyCode, 1);
}
void sendKeyUIThread(final int keyCode, final int repeat) {
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
mHost.sendKeyDownUp(keyCode, repeat);
}
});
}
void verifyGetThumbCalls(int firstHeroIndex, int lastHeroIndex,
PlaybackSeekDataProvider provider, long[] positions) {
int firstThumbIndex = Math.max(firstHeroIndex - (mNumbThumbs / 2), 0);
int lastThumbIndex = Math.min(lastHeroIndex + (mNumbThumbs / 2), positions.length - 1);
for (int i = firstThumbIndex; i <= lastThumbIndex; i++) {
Mockito.verify(provider, times(1)).getThumbnail(eq(i), any(ResultCallback.class));
}
Mockito.verify(provider, times(0)).getThumbnail(
eq(firstThumbIndex - 1), any(ResultCallback.class));
Mockito.verify(provider, times(0)).getThumbnail(
eq(firstThumbIndex - 2), any(ResultCallback.class));
Mockito.verify(provider, times(0)).getThumbnail(
eq(lastThumbIndex + 1), any(ResultCallback.class));
Mockito.verify(provider, times(0)).getThumbnail(
eq(lastThumbIndex + 2), any(ResultCallback.class));
}
void verifyAtHeroIndexWithDifferentPosition(long position, int heroIndex) {
assertEquals(position, mGlue.getControlsRow().getCurrentPosition());
assertEquals(mViewHolder.mThumbHeroIndex, heroIndex);
}
void verifyAtHeroIndex(long[] positions, int heroIndex) {
verifyAtHeroIndex(positions, heroIndex, null);
}
void verifyAtHeroIndex(long[] positions, int heroIndex, Bitmap[] thumbs) {
assertEquals(positions[heroIndex], mGlue.getControlsRow().getCurrentPosition());
assertEquals(mViewHolder.mThumbHeroIndex, heroIndex);
if (thumbs != null) {
int start = Math.max(0, mViewHolder.mThumbHeroIndex - mNumbThumbs / 2);
int end = Math.min(positions.length - 1, mViewHolder.mThumbHeroIndex + mNumbThumbs / 2);
verifyThumbBitmaps(thumbs, start, end,
mViewHolder.mThumbsBar, start + mNumbThumbs / 2 - mViewHolder.mThumbHeroIndex,
end + mNumbThumbs / 2 - mViewHolder.mThumbHeroIndex);
}
}
void verifyThumbBitmaps(Bitmap[] thumbs, int start, int end,
ThumbsBar thumbsBar, int childStart, int childEnd) {
assertEquals(end - start, childEnd - childStart);
for (int i = start; i <= end; i++) {
assertSame(thumbs[i], thumbsBar.getThumbBitmap(childStart + (i - start)));
}
for (int i = 0; i < childStart; i++) {
assertNull(thumbsBar.getThumbBitmap(i));
}
for (int i = childEnd + 1; i < mNumbThumbs; i++) {
assertNull(thumbsBar.getThumbBitmap(i));
}
}
@Test
public void progressUpdating() {
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn(123L);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(321L);
mGlue.play();
Mockito.verify(mImpl, times(1)).play();
mGlue.pause();
Mockito.verify(mImpl, times(1)).pause();
mGlue.seekTo(1231);
Mockito.verify(mImpl, times(1)).seekTo(1231);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
assertEquals(123L, mGlue.getCurrentPosition());
assertEquals(20000L, mGlue.getDuration());
assertEquals(321L, mGlue.getBufferedPosition());
assertEquals(123L, mViewHolder.mCurrentTimeInMs);
assertEquals(20000L, mViewHolder.mTotalTimeInMs);
assertEquals(321L, mViewHolder.mSecondaryProgressInMs);
when(mImpl.getCurrentPosition()).thenReturn(124L);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
assertEquals(124L, mGlue.getControlsRow().getCurrentPosition());
assertEquals(124L, mViewHolder.mCurrentTimeInMs);
when(mImpl.getBufferedPosition()).thenReturn(333L);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
assertEquals(333L, mGlue.getControlsRow().getBufferedPosition());
assertEquals(333L, mViewHolder.mSecondaryProgressInMs);
when(mImpl.getDuration()).thenReturn((long) (Integer.MAX_VALUE) * 2);
mImpl.getCallback().onDurationChanged(mImpl);
assertEquals((long) (Integer.MAX_VALUE) * 2, mGlue.getControlsRow().getDuration());
assertEquals((long) (Integer.MAX_VALUE) * 2, mViewHolder.mTotalTimeInMs);
}
@Test
public void mediaInfo() {
final ColorDrawable art = new ColorDrawable();
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
mGlue.setTitle("xyz");
mGlue.setSubtitle("zyx");
mGlue.setArt(art);
}
});
assertEquals("xyz", mDescriptionViewHolder.mTitle.getText());
assertEquals("zyx", mDescriptionViewHolder.mSubtitle.getText());
assertSame(art, mViewHolder.mImageView.getDrawable());
}
static boolean isDescendant(View view, View descendant) {
while (descendant != view) {
ViewParent p = descendant.getParent();
if (!(p instanceof View)) {
return false;
}
descendant = (View) p;
}
return true;
}
@Test
public void navigateRightInPrimary() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
mViewHolder.mControlsVh.mControlBar.getChildAt(0).requestFocus();
}
});
View view = mViewHolder.view.findFocus();
assertTrue(isDescendant(mViewHolder.mControlsVh.mControlBar.getChildAt(0), view));
assertTrue(isDescendant(mViewHolder.mControlsVh.mControlBar.getChildAt(1),
view.focusSearch(View.FOCUS_RIGHT)));
}
@Test
public void navigateRightInSecondary() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
mViewHolder.mSecondaryControlsVh.mControlBar.getChildAt(0).requestFocus();
}
});
View view = mViewHolder.view.findFocus();
assertTrue(isDescendant(mViewHolder.mSecondaryControlsVh.mControlBar.getChildAt(0), view));
assertTrue(isDescendant(mViewHolder.mSecondaryControlsVh.mControlBar.getChildAt(1),
view.focusSearch(View.FOCUS_RIGHT)));
}
@Test
public void navigatePrimaryDownToProgress() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
mViewHolder.mControlsVh.mControlBar.getChildAt(0).requestFocus();
}
});
View view = mViewHolder.view.findFocus();
assertTrue(isDescendant(mViewHolder.mControlsVh.mControlBar.getChildAt(0), view));
assertSame(mViewHolder.mProgressBar, view.focusSearch(View.FOCUS_DOWN));
}
@Test
public void navigateProgressUpToPrimary() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
mViewHolder.mProgressBar.requestFocus();
}
});
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
mViewHolder.mProgressBar.focusSearch(View.FOCUS_UP).requestFocus();
}
});
View view = mViewHolder.view.findFocus();
assertTrue(isDescendant(mViewHolder.mControlsVh.mControlBar.getChildAt(0), view));
}
@Test
public void navigateProgressDownToSecondary() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
mViewHolder.mProgressBar.requestFocus();
}
});
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
mViewHolder.mProgressBar.focusSearch(View.FOCUS_DOWN).requestFocus();
}
});
View view = mViewHolder.view.findFocus();
assertTrue(isDescendant(mViewHolder.mSecondaryControlsVh.mControlBar.getChildAt(0), view));
}
@Test
public void navigateSecondaryUpToProgress() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
mViewHolder.mSecondaryControlsVh.mControlBar.getChildAt(0).requestFocus();
}
});
View view = mViewHolder.view.findFocus();
assertTrue(isDescendant(mViewHolder.mSecondaryControlsVh.mControlBar.getChildAt(0), view));
assertSame(mViewHolder.mProgressBar, view.focusSearch(View.FOCUS_UP));
}
@Test
public void seekAndConfirm() {
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn(0L);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(321L);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
PlaybackSeekProviderSample provider = Mockito.spy(
new PlaybackSeekProviderSample(10000L, 101));
final long[] positions = provider.getSeekPositions();
mGlue.setSeekProvider(provider);
mViewHolder.mProgressBar.requestFocus();
assertTrue(mViewHolder.mProgressBar.hasFocus());
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndex(positions, 1);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndex(positions, 2);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_CENTER);
Mockito.verify(mImpl).seekTo(positions[2]);
verifyGetThumbCalls(1, 2, provider, positions);
}
@Test
public void playSeekToZero() {
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn(0L);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(321L);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
PlaybackSeekProviderSample provider = Mockito.spy(
new PlaybackSeekProviderSample(10000L, 101));
final long[] positions = provider.getSeekPositions();
mGlue.setSeekProvider(provider);
// start play
mGlue.play();
verify(mImpl).play();
// focus to seek bar
mViewHolder.mProgressBar.requestFocus();
assertTrue(mViewHolder.mProgressBar.hasFocus());
// using DPAD_RIGHT to initiate seeking
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndex(positions, 2);
// press DPAD_CENTER to seek to new position and continue play
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_CENTER);
verify(mImpl).seekTo(positions[2]);
verify(mImpl).play();
// press DPAD_LEFT seek to 0
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
verifyAtHeroIndex(positions, 0);
// press DPAD_CENTER to continue play from 0
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_CENTER);
verify(mImpl).seekTo(0);
verify(mImpl).play();
}
@Test
public void playSeekAndCancel() {
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn(0L);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(321L);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
PlaybackSeekProviderSample provider = Mockito.spy(
new PlaybackSeekProviderSample(10000L, 101));
final long[] positions = provider.getSeekPositions();
mGlue.setSeekProvider(provider);
// start play
mGlue.play();
verify(mImpl).play();
// focus to seek bar
mViewHolder.mProgressBar.requestFocus();
assertTrue(mViewHolder.mProgressBar.hasFocus());
// using DPAD_RIGHT to initiate seeking
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndex(positions, 2);
// press DPAD_CENTER to seek to new position and continue play
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_CENTER);
verify(mImpl).seekTo(positions[2]);
verify(mImpl).play();
// press DPAD_LEFT seek to 0
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
verifyAtHeroIndex(positions, 0);
// press BACK to cancel and continue play from position before seek
sendKeyUIThread(KeyEvent.KEYCODE_BACK);
verify(mImpl).seekTo(positions[2]);
verify(mImpl).play();
}
@Test
public void seekHoldKeyDown() {
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn(4489L);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(4489L);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
PlaybackSeekProviderSample provider = Mockito.spy(
new PlaybackSeekProviderSample(10000L, 101));
final long[] positions = provider.getSeekPositions();
mGlue.setSeekProvider(provider);
mViewHolder.mProgressBar.requestFocus();
assertTrue(mViewHolder.mProgressBar.hasFocus());
int insertPosition = -1 - Arrays.binarySearch(positions, 4489L);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT, 5);
verifyAtHeroIndex(positions, insertPosition + 4);
verifyGetThumbCalls(insertPosition, insertPosition + 4, provider, positions);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT, 5);
verifyAtHeroIndex(positions, insertPosition - 1);
}
@Test
public void seekAndCancel() {
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn(0L);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(321L);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
PlaybackSeekProviderSample provider = Mockito.spy(
new PlaybackSeekProviderSample(10000L, 101));
final long[] positions = provider.getSeekPositions();
mGlue.setSeekProvider(provider);
mViewHolder.mProgressBar.requestFocus();
assertTrue(mViewHolder.mProgressBar.hasFocus());
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndex(positions, 1);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndex(positions, 2);
sendKeyUIThread(KeyEvent.KEYCODE_BACK);
Mockito.verify(mImpl, times(0)).seekTo(anyInt());
verifyGetThumbCalls(1, 2, provider, positions);
}
@Test
public void seekUpBetweenTwoKeyPosition() {
PlaybackSeekProviderSample provider = Mockito.spy(
new PlaybackSeekProviderSample(10000L, 101));
final long[] positions = provider.getSeekPositions();
// initially select between 0 and 1
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn((positions[0] + positions[1]) / 2);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(321L);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
mGlue.setSeekProvider(provider);
mViewHolder.mProgressBar.requestFocus();
assertTrue(mViewHolder.mProgressBar.hasFocus());
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndex(positions, 1);
verifyGetThumbCalls(1, 1, provider, positions);
}
@Test
public void seekDownBetweenTwoKeyPosition() {
PlaybackSeekProviderSample provider = Mockito.spy(
new PlaybackSeekProviderSample(10000L, 101));
final long[] positions = provider.getSeekPositions();
assertTrue(positions[0] == 0);
// initially select between 0 and 1
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn((positions[0] + positions[1]) / 2);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(321L);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
mGlue.setSeekProvider(provider);
mViewHolder.mProgressBar.requestFocus();
assertTrue(mViewHolder.mProgressBar.hasFocus());
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
verifyAtHeroIndex(positions, 0);
verifyGetThumbCalls(0, 0, provider, positions);
}
@Test
public void seekDownOutOfKeyPositions() {
PlaybackSeekProviderSample provider = Mockito.spy(
new PlaybackSeekProviderSample(1000L, 10000L, 101));
final long[] positions = provider.getSeekPositions();
assertTrue(positions[0] > 0);
// initially select between 0 and 1
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn((positions[0] + positions[1]) / 2);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(321L);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
mGlue.setSeekProvider(provider);
mViewHolder.mProgressBar.requestFocus();
assertTrue(mViewHolder.mProgressBar.hasFocus());
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
verifyAtHeroIndex(positions, 0);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
verifyAtHeroIndexWithDifferentPosition(0, 0);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
verifyAtHeroIndexWithDifferentPosition(0, 0);
verifyGetThumbCalls(0, 0, provider, positions);
}
@Test
public void seekDownAheadOfKeyPositions() {
PlaybackSeekProviderSample provider = Mockito.spy(
new PlaybackSeekProviderSample(1000L, 10000L, 101));
final long[] positions = provider.getSeekPositions();
assertTrue(positions[0] > 0);
// initially select between 0 and 1
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn(positions[0] / 2);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(321L);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
mGlue.setSeekProvider(provider);
mViewHolder.mProgressBar.requestFocus();
assertTrue(mViewHolder.mProgressBar.hasFocus());
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
verifyAtHeroIndexWithDifferentPosition(0, 0);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndex(positions, 0);
verifyGetThumbCalls(0, 0, provider, positions);
}
@Test
public void seekUpAheadOfKeyPositions() {
PlaybackSeekProviderSample provider = Mockito.spy(
new PlaybackSeekProviderSample(1000L, 10000L, 101));
final long[] positions = provider.getSeekPositions();
assertTrue(positions[0] > 0);
// initially select between 0 and 1
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn(positions[0] / 2);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(321L);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
mGlue.setSeekProvider(provider);
mViewHolder.mProgressBar.requestFocus();
assertTrue(mViewHolder.mProgressBar.hasFocus());
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndex(positions, 0);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
verifyAtHeroIndexWithDifferentPosition(0, 0);
verifyGetThumbCalls(0, 0, provider, positions);
}
@Test
public void seekUpOutOfKeyPositions() {
PlaybackSeekProviderSample provider = Mockito.spy(
new PlaybackSeekProviderSample(10000L, 101));
final long[] positions = provider.getSeekPositions();
// initially select between nth-1 and nth
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn((positions[positions.length - 2]
+ positions[positions.length - 1]) / 2);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(321L);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
mGlue.setSeekProvider(provider);
mViewHolder.mProgressBar.requestFocus();
assertTrue(mViewHolder.mProgressBar.hasFocus());
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndex(positions, positions.length - 1);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
verifyAtHeroIndex(positions, positions.length - 2);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndex(positions, positions.length - 1);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndexWithDifferentPosition(20000L, positions.length - 1);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndexWithDifferentPosition(20000L, positions.length - 1);
verifyGetThumbCalls(positions.length - 2, positions.length - 1, provider, positions);
}
@Test
public void seekUpAfterKeyPositions() {
PlaybackSeekProviderSample provider = Mockito.spy(
new PlaybackSeekProviderSample(10000L, 101));
final long[] positions = provider.getSeekPositions();
// initially select after last item
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn(positions[positions.length - 1] + 100);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(321L);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
mGlue.setSeekProvider(provider);
mViewHolder.mProgressBar.requestFocus();
assertTrue(mViewHolder.mProgressBar.hasFocus());
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndexWithDifferentPosition(20000L, positions.length - 1);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
verifyAtHeroIndex(positions, positions.length - 1);
verifyGetThumbCalls(positions.length - 1, positions.length - 1, provider, positions);
}
@Test
public void seekDownAfterKeyPositions() {
PlaybackSeekProviderSample provider = Mockito.spy(
new PlaybackSeekProviderSample(10000L, 101));
final long[] positions = provider.getSeekPositions();
// initially select after last item
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn(positions[positions.length - 1] + 100);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(321L);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
mGlue.setSeekProvider(provider);
mViewHolder.mProgressBar.requestFocus();
assertTrue(mViewHolder.mProgressBar.hasFocus());
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_LEFT);
verifyAtHeroIndex(positions, positions.length - 1);
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndexWithDifferentPosition(20000L, positions.length - 1);
verifyGetThumbCalls(positions.length - 1, positions.length - 1, provider, positions);
}
@Test
public void thumbLoadedInCallback() {
when(mImpl.isPrepared()).thenReturn(true);
when(mImpl.getCurrentPosition()).thenReturn(0L);
when(mImpl.getDuration()).thenReturn(20000L);
when(mImpl.getBufferedPosition()).thenReturn(321L);
mImpl.getCallback().onCurrentPositionChanged(mImpl);
mImpl.getCallback().onDurationChanged(mImpl);
mImpl.getCallback().onBufferedPositionChanged(mImpl);
final Bitmap[] thumbs = new Bitmap[101];
for (int i = 0; i < 101; i++) {
thumbs[i] = Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888);
}
PlaybackSeekProviderSample provider = new PlaybackSeekProviderSample(10000L, 101) {
@Override
public void getThumbnail(int index, ResultCallback callback) {
callback.onThumbnailLoaded(thumbs[index], index);
}
};
final long[] positions = provider.getSeekPositions();
mGlue.setSeekProvider(provider);
mViewHolder.mProgressBar.requestFocus();
assertTrue(mViewHolder.mProgressBar.hasFocus());
sendKeyUIThread(KeyEvent.KEYCODE_DPAD_RIGHT);
verifyAtHeroIndex(positions, 1, thumbs);
}
}