Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package androidx.leanback.app;
     17 
     18 import static org.junit.Assert.assertEquals;
     19 import static org.junit.Assert.assertNull;
     20 import static org.junit.Assert.assertSame;
     21 import static org.junit.Assert.assertTrue;
     22 import static org.mockito.Matchers.any;
     23 import static org.mockito.Mockito.times;
     24 import static org.mockito.Mockito.verify;
     25 
     26 import android.graphics.drawable.Drawable;
     27 import android.os.Bundle;
     28 import android.support.test.filters.FlakyTest;
     29 import android.support.test.filters.MediumTest;
     30 import android.support.test.filters.Suppress;
     31 import android.support.test.runner.AndroidJUnit4;
     32 import android.view.KeyEvent;
     33 import android.view.View;
     34 
     35 import androidx.leanback.media.PlaybackControlGlue;
     36 import androidx.leanback.media.PlaybackGlue;
     37 import androidx.leanback.testutils.PollingCheck;
     38 import androidx.leanback.widget.ControlButtonPresenterSelector;
     39 import androidx.leanback.widget.ListRow;
     40 import androidx.leanback.widget.OnItemViewClickedListener;
     41 import androidx.leanback.widget.OnItemViewSelectedListener;
     42 import androidx.leanback.widget.PlaybackControlsRow;
     43 import androidx.leanback.widget.PlaybackControlsRowPresenter;
     44 import androidx.leanback.widget.Presenter;
     45 import androidx.leanback.widget.Row;
     46 import androidx.leanback.widget.RowPresenter;
     47 import androidx.leanback.widget.SparseArrayObjectAdapter;
     48 
     49 import org.junit.Test;
     50 import org.junit.runner.RunWith;
     51 import org.mockito.ArgumentCaptor;
     52 import org.mockito.Mockito;
     53 
     54 @MediumTest
     55 @RunWith(AndroidJUnit4.class)
     56 public class PlaybackSupportFragmentTest extends SingleSupportFragmentTestBase {
     57 
     58     private static final String TAG = "PlaybackSupportFragmentTest";
     59     private static final long TRANSITION_LENGTH = 1000;
     60 
     61     @Test
     62     public void testDetachCalledWhenDestroyFragment() throws Throwable {
     63         final SingleSupportFragmentTestActivity activity =
     64                 launchAndWaitActivity(PlaybackTestSupportFragment.class, 1000);
     65         final PlaybackTestSupportFragment fragment = (PlaybackTestSupportFragment) activity.getTestFragment();
     66         PlaybackGlue glue = fragment.getGlue();
     67         activityTestRule.runOnUiThread(new Runnable() {
     68             @Override
     69             public void run() {
     70                 activity.finish();
     71             }
     72         });
     73         PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() {
     74             @Override
     75             public boolean canProceed() {
     76                 return fragment.mDestroyCalled;
     77             }
     78         });
     79         assertNull(glue.getHost());
     80     }
     81 
     82     @Test
     83     public void testSelectedListener() throws Throwable {
     84         SingleSupportFragmentTestActivity activity =
     85                 launchAndWaitActivity(PlaybackTestSupportFragment.class, 1000);
     86         PlaybackTestSupportFragment fragment = (PlaybackTestSupportFragment) activity.getTestFragment();
     87 
     88         assertTrue(fragment.getView().hasFocus());
     89 
     90         OnItemViewSelectedListener selectedListener = Mockito.mock(
     91                 OnItemViewSelectedListener.class);
     92         fragment.setOnItemViewSelectedListener(selectedListener);
     93 
     94 
     95         PlaybackControlsRow controlsRow = fragment.getGlue().getControlsRow();
     96         SparseArrayObjectAdapter primaryActionsAdapter = (SparseArrayObjectAdapter)
     97                 controlsRow.getPrimaryActionsAdapter();
     98 
     99         PlaybackControlsRow.MultiAction playPause = (PlaybackControlsRow.MultiAction)
    100                 primaryActionsAdapter.lookup(PlaybackControlGlue.ACTION_PLAY_PAUSE);
    101 
    102         PlaybackControlsRow.MultiAction rewind = (PlaybackControlsRow.MultiAction)
    103                 primaryActionsAdapter.lookup(PlaybackControlGlue.ACTION_REWIND);
    104 
    105         PlaybackControlsRow.MultiAction thumbsUp = (PlaybackControlsRow.MultiAction)
    106                 primaryActionsAdapter.lookup(PlaybackControlGlue.ACTION_CUSTOM_LEFT_FIRST);
    107 
    108         ArgumentCaptor<Presenter.ViewHolder> itemVHCaptor =
    109                 ArgumentCaptor.forClass(Presenter.ViewHolder.class);
    110         ArgumentCaptor<Object> itemCaptor = ArgumentCaptor.forClass(Object.class);
    111         ArgumentCaptor<RowPresenter.ViewHolder> rowVHCaptor =
    112                 ArgumentCaptor.forClass(RowPresenter.ViewHolder.class);
    113         ArgumentCaptor<Row> rowCaptor = ArgumentCaptor.forClass(Row.class);
    114 
    115 
    116         // First navigate left within PlaybackControlsRow items.
    117         verify(selectedListener, times(0)).onItemSelected(any(Presenter.ViewHolder.class),
    118                 any(Object.class), any(RowPresenter.ViewHolder.class), any(Row.class));
    119         sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
    120         verify(selectedListener, times(1)).onItemSelected(itemVHCaptor.capture(),
    121                 itemCaptor.capture(), rowVHCaptor.capture(), rowCaptor.capture());
    122         assertSame("Same controls row should be passed to the listener", controlsRow,
    123                 rowCaptor.getValue());
    124         assertSame("The selected action should be rewind", rewind, itemCaptor.getValue());
    125 
    126         sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
    127         verify(selectedListener, times(2)).onItemSelected(itemVHCaptor.capture(),
    128                 itemCaptor.capture(), rowVHCaptor.capture(), rowCaptor.capture());
    129         assertSame("Same controls row should be passed to the listener", controlsRow,
    130                 rowCaptor.getValue());
    131         assertSame("The selected action should be thumbsUp", thumbsUp, itemCaptor.getValue());
    132 
    133         // Now navigate down to a ListRow item.
    134         ListRow listRow0 = (ListRow) fragment.getAdapter().get(1);
    135 
    136         sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
    137         waitForScrollIdle(fragment.getVerticalGridView());
    138         verify(selectedListener, times(3)).onItemSelected(itemVHCaptor.capture(),
    139                 itemCaptor.capture(), rowVHCaptor.capture(), rowCaptor.capture());
    140         assertSame("Same list row should be passed to the listener", listRow0,
    141                 rowCaptor.getValue());
    142         // Depending on the focusSearch algorithm, one of the items in the first ListRow must be
    143         // selected.
    144         boolean listRowItemPassed = (itemCaptor.getValue() == listRow0.getAdapter().get(0)
    145                 || itemCaptor.getValue() == listRow0.getAdapter().get(1));
    146         assertTrue("None of the items in the first ListRow are passed to the selected listener.",
    147                 listRowItemPassed);
    148     }
    149 
    150     @Test
    151     public void testClickedListener() throws Throwable {
    152         SingleSupportFragmentTestActivity activity =
    153                 launchAndWaitActivity(PlaybackTestSupportFragment.class, 1000);
    154         PlaybackTestSupportFragment fragment = (PlaybackTestSupportFragment) activity.getTestFragment();
    155 
    156         assertTrue(fragment.getView().hasFocus());
    157 
    158         OnItemViewClickedListener clickedListener = Mockito.mock(OnItemViewClickedListener.class);
    159         fragment.setOnItemViewClickedListener(clickedListener);
    160 
    161 
    162         PlaybackControlsRow controlsRow = fragment.getGlue().getControlsRow();
    163         SparseArrayObjectAdapter primaryActionsAdapter = (SparseArrayObjectAdapter)
    164                 controlsRow.getPrimaryActionsAdapter();
    165 
    166         PlaybackControlsRow.MultiAction playPause = (PlaybackControlsRow.MultiAction)
    167                 primaryActionsAdapter.lookup(PlaybackControlGlue.ACTION_PLAY_PAUSE);
    168 
    169         PlaybackControlsRow.MultiAction rewind = (PlaybackControlsRow.MultiAction)
    170                 primaryActionsAdapter.lookup(PlaybackControlGlue.ACTION_REWIND);
    171 
    172         PlaybackControlsRow.MultiAction thumbsUp = (PlaybackControlsRow.MultiAction)
    173                 primaryActionsAdapter.lookup(PlaybackControlGlue.ACTION_CUSTOM_LEFT_FIRST);
    174 
    175         ArgumentCaptor<Presenter.ViewHolder> itemVHCaptor =
    176                 ArgumentCaptor.forClass(Presenter.ViewHolder.class);
    177         ArgumentCaptor<Object> itemCaptor = ArgumentCaptor.forClass(Object.class);
    178         ArgumentCaptor<RowPresenter.ViewHolder> rowVHCaptor =
    179                 ArgumentCaptor.forClass(RowPresenter.ViewHolder.class);
    180         ArgumentCaptor<Row> rowCaptor = ArgumentCaptor.forClass(Row.class);
    181 
    182 
    183         // First navigate left within PlaybackControlsRow items.
    184         verify(clickedListener, times(0)).onItemClicked(any(Presenter.ViewHolder.class),
    185                 any(Object.class), any(RowPresenter.ViewHolder.class), any(Row.class));
    186         sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
    187         verify(clickedListener, times(1)).onItemClicked(itemVHCaptor.capture(),
    188                 itemCaptor.capture(), rowVHCaptor.capture(), rowCaptor.capture());
    189         assertSame("Same controls row should be passed to the listener", controlsRow,
    190                 rowCaptor.getValue());
    191         assertSame("The clicked action should be playPause", playPause, itemCaptor.getValue());
    192 
    193         sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
    194         verify(clickedListener, times(1)).onItemClicked(any(Presenter.ViewHolder.class),
    195                 any(Object.class), any(RowPresenter.ViewHolder.class), any(Row.class));
    196         sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
    197         verify(clickedListener, times(2)).onItemClicked(itemVHCaptor.capture(),
    198                 itemCaptor.capture(), rowVHCaptor.capture(), rowCaptor.capture());
    199         assertSame("Same controls row should be passed to the listener", controlsRow,
    200                 rowCaptor.getValue());
    201         assertSame("The clicked action should be rewind", rewind, itemCaptor.getValue());
    202 
    203         sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
    204         verify(clickedListener, times(2)).onItemClicked(any(Presenter.ViewHolder.class),
    205                 any(Object.class), any(RowPresenter.ViewHolder.class), any(Row.class));
    206         sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
    207         verify(clickedListener, times(3)).onItemClicked(itemVHCaptor.capture(),
    208                 itemCaptor.capture(), rowVHCaptor.capture(), rowCaptor.capture());
    209         assertSame("Same controls row should be passed to the listener", controlsRow,
    210                 rowCaptor.getValue());
    211         assertSame("The clicked action should be thumbsUp", thumbsUp, itemCaptor.getValue());
    212 
    213         // Now navigate down to a ListRow item.
    214         ListRow listRow0 = (ListRow) fragment.getAdapter().get(1);
    215 
    216         sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
    217         waitForScrollIdle(fragment.getVerticalGridView());
    218         verify(clickedListener, times(3)).onItemClicked(any(Presenter.ViewHolder.class),
    219                 any(Object.class), any(RowPresenter.ViewHolder.class), any(Row.class));
    220         sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
    221         verify(clickedListener, times(4)).onItemClicked(itemVHCaptor.capture(),
    222                 itemCaptor.capture(), rowVHCaptor.capture(), rowCaptor.capture());
    223         assertSame("Same list row should be passed to the listener", listRow0,
    224                 rowCaptor.getValue());
    225         boolean listRowItemPassed = (itemCaptor.getValue() == listRow0.getAdapter().get(0)
    226                 || itemCaptor.getValue() == listRow0.getAdapter().get(1));
    227         assertTrue("None of the items in the first ListRow are passed to the click listener.",
    228                 listRowItemPassed);
    229     }
    230 
    231     @FlakyTest
    232     @Suppress
    233     @Test
    234     public void alignmentRowToBottom() throws Throwable {
    235         SingleSupportFragmentTestActivity activity =
    236                 launchAndWaitActivity(PlaybackTestSupportFragment.class, 1000);
    237         final PlaybackTestSupportFragment fragment = (PlaybackTestSupportFragment) activity.getTestFragment();
    238 
    239         assertTrue(fragment.getAdapter().size() > 2);
    240 
    241         View playRow = fragment.getVerticalGridView().getChildAt(0);
    242         assertTrue(playRow.hasFocus());
    243         assertEquals(playRow.getResources().getDimensionPixelSize(
    244                 androidx.leanback.test.R.dimen.lb_playback_controls_padding_bottom),
    245                 fragment.getVerticalGridView().getHeight() - playRow.getBottom());
    246 
    247         activityTestRule.runOnUiThread(new Runnable() {
    248             @Override
    249             public void run() {
    250                 fragment.getVerticalGridView().setSelectedPositionSmooth(
    251                         fragment.getAdapter().size() - 1);
    252             }
    253         });
    254         waitForScrollIdle(fragment.getVerticalGridView());
    255 
    256         View lastRow = fragment.getVerticalGridView().getChildAt(
    257                 fragment.getVerticalGridView().getChildCount() - 1);
    258         assertEquals(fragment.getAdapter().size() - 1,
    259                 fragment.getVerticalGridView().getChildAdapterPosition(lastRow));
    260         assertTrue(lastRow.hasFocus());
    261         assertEquals(lastRow.getResources().getDimensionPixelSize(
    262                 androidx.leanback.test.R.dimen.lb_playback_controls_padding_bottom),
    263                 fragment.getVerticalGridView().getHeight() - lastRow.getBottom());
    264     }
    265 
    266     public static class PurePlaybackSupportFragment extends PlaybackSupportFragment {
    267         @Override
    268         public void onCreate(Bundle savedInstanceState) {
    269             super.onCreate(savedInstanceState);
    270             setFadingEnabled(false);
    271             PlaybackControlsRow row = new PlaybackControlsRow();
    272             SparseArrayObjectAdapter primaryAdapter = new SparseArrayObjectAdapter(
    273                     new ControlButtonPresenterSelector());
    274             primaryAdapter.set(0, new PlaybackControlsRow.SkipPreviousAction(getActivity()));
    275             primaryAdapter.set(1, new PlaybackControlsRow.PlayPauseAction(getActivity()));
    276             primaryAdapter.set(2, new PlaybackControlsRow.SkipNextAction(getActivity()));
    277             row.setPrimaryActionsAdapter(primaryAdapter);
    278             row.setSecondaryActionsAdapter(null);
    279             setPlaybackRow(row);
    280             setPlaybackRowPresenter(new PlaybackControlsRowPresenter());
    281         }
    282     }
    283 
    284     @Test
    285     public void setupRowAndPresenterWithoutGlue() {
    286         SingleSupportFragmentTestActivity activity =
    287                 launchAndWaitActivity(PurePlaybackSupportFragment.class, 1000);
    288         final PurePlaybackSupportFragment fragment = (PurePlaybackSupportFragment)
    289                 activity.getTestFragment();
    290 
    291         assertTrue(fragment.getAdapter().size() == 1);
    292         View playRow = fragment.getVerticalGridView().getChildAt(0);
    293         assertTrue(playRow.hasFocus());
    294         assertEquals(playRow.getResources().getDimensionPixelSize(
    295                 androidx.leanback.test.R.dimen.lb_playback_controls_padding_bottom),
    296                 fragment.getVerticalGridView().getHeight() - playRow.getBottom());
    297     }
    298 
    299     public static class ControlGlueFragment extends PlaybackSupportFragment {
    300         @Override
    301         public void onCreate(Bundle savedInstanceState) {
    302             super.onCreate(savedInstanceState);
    303             int[] ffspeeds = new int[] {PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0,
    304                     PlaybackControlGlue.PLAYBACK_SPEED_FAST_L1};
    305             PlaybackGlue glue = new PlaybackControlGlue(
    306                     getActivity(), ffspeeds) {
    307                 @Override
    308                 public boolean hasValidMedia() {
    309                     return true;
    310                 }
    311 
    312                 @Override
    313                 public boolean isMediaPlaying() {
    314                     return false;
    315                 }
    316 
    317                 @Override
    318                 public CharSequence getMediaTitle() {
    319                     return "Title";
    320                 }
    321 
    322                 @Override
    323                 public CharSequence getMediaSubtitle() {
    324                     return "SubTitle";
    325                 }
    326 
    327                 @Override
    328                 public int getMediaDuration() {
    329                     return 100;
    330                 }
    331 
    332                 @Override
    333                 public Drawable getMediaArt() {
    334                     return null;
    335                 }
    336 
    337                 @Override
    338                 public long getSupportedActions() {
    339                     return PlaybackControlGlue.ACTION_PLAY_PAUSE;
    340                 }
    341 
    342                 @Override
    343                 public int getCurrentSpeedId() {
    344                     return PlaybackControlGlue.PLAYBACK_SPEED_PAUSED;
    345                 }
    346 
    347                 @Override
    348                 public int getCurrentPosition() {
    349                     return 50;
    350                 }
    351             };
    352             glue.setHost(new PlaybackSupportFragmentGlueHost(this));
    353         }
    354     }
    355 
    356     @Test
    357     public void setupWithControlGlue() throws Throwable {
    358         SingleSupportFragmentTestActivity activity =
    359                 launchAndWaitActivity(ControlGlueFragment.class, 1000);
    360         final ControlGlueFragment fragment = (ControlGlueFragment)
    361                 activity.getTestFragment();
    362 
    363         assertTrue(fragment.getAdapter().size() == 1);
    364 
    365         View playRow = fragment.getVerticalGridView().getChildAt(0);
    366         assertTrue(playRow.hasFocus());
    367         assertEquals(playRow.getResources().getDimensionPixelSize(
    368                 androidx.leanback.test.R.dimen.lb_playback_controls_padding_bottom),
    369                 fragment.getVerticalGridView().getHeight() - playRow.getBottom());
    370     }
    371 }
    372