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.assertFalse;
     20 import static org.junit.Assert.assertNull;
     21 import static org.junit.Assert.assertTrue;
     22 
     23 import android.animation.PropertyValuesHolder;
     24 import android.content.Intent;
     25 import android.graphics.Bitmap;
     26 import android.graphics.BitmapFactory;
     27 import android.graphics.Rect;
     28 import android.graphics.drawable.ColorDrawable;
     29 import android.graphics.drawable.Drawable;
     30 import android.net.Uri;
     31 import android.os.Build;
     32 import android.os.Bundle;
     33 import android.os.SystemClock;
     34 import android.support.test.InstrumentationRegistry;
     35 import android.support.test.filters.LargeTest;
     36 import android.support.test.filters.SdkSuppress;
     37 import android.view.KeyEvent;
     38 import android.view.View;
     39 
     40 import androidx.fragment.app.Fragment;
     41 import androidx.leanback.R;
     42 import androidx.leanback.graphics.FitWidthBitmapDrawable;
     43 import androidx.leanback.media.MediaPlayerGlue;
     44 import androidx.leanback.media.PlaybackGlueHost;
     45 import androidx.leanback.testutils.PollingCheck;
     46 import androidx.leanback.transition.TransitionHelper;
     47 import androidx.leanback.util.StateMachine;
     48 import androidx.leanback.widget.DetailsParallax;
     49 import androidx.leanback.widget.DetailsParallaxDrawable;
     50 import androidx.leanback.widget.ParallaxTarget;
     51 import androidx.leanback.widget.RecyclerViewParallax;
     52 import androidx.leanback.widget.VerticalGridView;
     53 
     54 import org.junit.Test;
     55 import org.junit.runner.RunWith;
     56 import org.junit.runners.JUnit4;
     57 
     58 /**
     59  * Unit tests for {@link DetailsSupportFragment}.
     60  */
     61 @RunWith(JUnit4.class)
     62 @LargeTest
     63 public class DetailsSupportFragmentTest extends SingleSupportFragmentTestBase {
     64 
     65     static final int PARALLAX_VERTICAL_OFFSET = -300;
     66 
     67     static int getCoverDrawableAlpha(DetailsSupportFragmentBackgroundController controller) {
     68         return ((FitWidthBitmapDrawable) controller.mParallaxDrawable.getCoverDrawable())
     69                 .getAlpha();
     70     }
     71 
     72     public static class DetailsSupportFragmentParallax extends DetailsTestSupportFragment {
     73 
     74         private DetailsParallaxDrawable mParallaxDrawable;
     75 
     76         public DetailsSupportFragmentParallax() {
     77             super();
     78             mMinVerticalOffset = PARALLAX_VERTICAL_OFFSET;
     79         }
     80 
     81         @Override
     82         public void onCreate(Bundle savedInstanceState) {
     83             super.onCreate(savedInstanceState);
     84             Drawable coverDrawable = new FitWidthBitmapDrawable();
     85             mParallaxDrawable = new DetailsParallaxDrawable(
     86                     getActivity(),
     87                     getParallax(),
     88                     coverDrawable,
     89                     new ParallaxTarget.PropertyValuesHolderTarget(
     90                             coverDrawable,
     91                             PropertyValuesHolder.ofInt("verticalOffset", 0, mMinVerticalOffset)
     92                     )
     93             );
     94 
     95             BackgroundManager backgroundManager = BackgroundManager.getInstance(getActivity());
     96             backgroundManager.attach(getActivity().getWindow());
     97             backgroundManager.setDrawable(mParallaxDrawable);
     98         }
     99 
    100         @Override
    101         public void onStart() {
    102             super.onStart();
    103             setItem(new PhotoItem("Hello world", "Fake content goes here",
    104                     androidx.leanback.test.R.drawable.spiderman));
    105         }
    106 
    107         @Override
    108         public void onResume() {
    109             super.onResume();
    110             Bitmap bitmap = BitmapFactory.decodeResource(getActivity().getResources(),
    111                     androidx.leanback.test.R.drawable.spiderman);
    112             ((FitWidthBitmapDrawable) mParallaxDrawable.getCoverDrawable()).setBitmap(bitmap);
    113         }
    114 
    115         DetailsParallaxDrawable getParallaxDrawable() {
    116             return mParallaxDrawable;
    117         }
    118     }
    119 
    120     @Test
    121     public void parallaxSetupTest() {
    122         SingleSupportFragmentTestActivity activity =
    123                 launchAndWaitActivity(DetailsSupportFragmentTest.DetailsSupportFragmentParallax.class,
    124                 new SingleSupportFragmentTestBase.Options().uiVisibility(
    125                         View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
    126 
    127         double delta = 0.0002;
    128         DetailsParallax dpm = ((DetailsSupportFragment) activity.getTestFragment()).getParallax();
    129 
    130         RecyclerViewParallax.ChildPositionProperty frameTop =
    131                 (RecyclerViewParallax.ChildPositionProperty) dpm.getOverviewRowTop();
    132         assertEquals(0f, frameTop.getFraction(), delta);
    133         assertEquals(0f, frameTop.getAdapterPosition(), delta);
    134 
    135 
    136         RecyclerViewParallax.ChildPositionProperty frameBottom =
    137                 (RecyclerViewParallax.ChildPositionProperty) dpm.getOverviewRowBottom();
    138         assertEquals(1f, frameBottom.getFraction(), delta);
    139         assertEquals(0f, frameBottom.getAdapterPosition(), delta);
    140     }
    141 
    142     @Test
    143     public void parallaxTest() throws Throwable {
    144         SingleSupportFragmentTestActivity activity = launchAndWaitActivity(DetailsSupportFragmentParallax.class,
    145                 new Options().uiVisibility(
    146                         View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
    147 
    148         final DetailsSupportFragmentParallax detailsFragment =
    149                 (DetailsSupportFragmentParallax) activity.getTestFragment();
    150         DetailsParallaxDrawable drawable =
    151                 detailsFragment.getParallaxDrawable();
    152         final FitWidthBitmapDrawable bitmapDrawable = (FitWidthBitmapDrawable)
    153                 drawable.getCoverDrawable();
    154 
    155         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    156             @Override
    157             public boolean canProceed() {
    158                 return detailsFragment.getRowsSupportFragment().getAdapter() != null
    159                         && detailsFragment.getRowsSupportFragment().getAdapter().size() > 1;
    160             }
    161         });
    162 
    163         final VerticalGridView verticalGridView = detailsFragment.getRowsSupportFragment()
    164                 .getVerticalGridView();
    165         final int windowHeight = verticalGridView.getHeight();
    166         final int windowWidth = verticalGridView.getWidth();
    167         // make sure background manager attached to window is same size as VerticalGridView
    168         // i.e. no status bar.
    169         assertEquals(windowHeight, activity.getWindow().getDecorView().getHeight());
    170         assertEquals(windowWidth, activity.getWindow().getDecorView().getWidth());
    171 
    172         final View detailsFrame = verticalGridView.findViewById(R.id.details_frame);
    173 
    174         assertEquals(windowWidth, bitmapDrawable.getBounds().width());
    175 
    176         final Rect detailsFrameRect = new Rect();
    177         detailsFrameRect.set(0, 0, detailsFrame.getWidth(), detailsFrame.getHeight());
    178         verticalGridView.offsetDescendantRectToMyCoords(detailsFrame, detailsFrameRect);
    179 
    180         assertEquals(Math.min(windowHeight, detailsFrameRect.top),
    181                 bitmapDrawable.getBounds().height());
    182         assertEquals(0, bitmapDrawable.getVerticalOffset());
    183 
    184         assertTrue("TitleView is visible", detailsFragment.getView()
    185                 .findViewById(R.id.browse_title_group).getVisibility() == View.VISIBLE);
    186 
    187         activityTestRule.runOnUiThread(new Runnable() {
    188             @Override
    189             public void run() {
    190                 verticalGridView.scrollToPosition(1);
    191             }
    192         });
    193 
    194         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    195             @Override
    196             public boolean canProceed() {
    197                 return bitmapDrawable.getVerticalOffset() == PARALLAX_VERTICAL_OFFSET
    198                         && detailsFragment.getView()
    199                         .findViewById(R.id.browse_title_group).getVisibility() != View.VISIBLE;
    200             }
    201         });
    202 
    203         detailsFrameRect.set(0, 0, detailsFrame.getWidth(), detailsFrame.getHeight());
    204         verticalGridView.offsetDescendantRectToMyCoords(detailsFrame, detailsFrameRect);
    205 
    206         assertEquals(0, bitmapDrawable.getBounds().top);
    207         assertEquals(Math.max(detailsFrameRect.top, 0), bitmapDrawable.getBounds().bottom);
    208         assertEquals(windowWidth, bitmapDrawable.getBounds().width());
    209 
    210         ColorDrawable colorDrawable = (ColorDrawable) (drawable.getChildAt(1).getDrawable());
    211         assertEquals(windowWidth, colorDrawable.getBounds().width());
    212         assertEquals(detailsFrameRect.bottom, colorDrawable.getBounds().top);
    213         assertEquals(windowHeight, colorDrawable.getBounds().bottom);
    214     }
    215 
    216     public static class DetailsSupportFragmentWithVideo extends DetailsTestSupportFragment {
    217 
    218         final DetailsSupportFragmentBackgroundController mDetailsBackground =
    219                 new DetailsSupportFragmentBackgroundController(this);
    220         MediaPlayerGlue mGlue;
    221 
    222         public DetailsSupportFragmentWithVideo() {
    223             mTimeToLoadOverviewRow = mTimeToLoadRelatedRow = 100;
    224         }
    225 
    226         @Override
    227         public void onCreate(Bundle savedInstanceState) {
    228             super.onCreate(savedInstanceState);
    229             mDetailsBackground.enableParallax();
    230             mGlue = new MediaPlayerGlue(getActivity());
    231             mDetailsBackground.setupVideoPlayback(mGlue);
    232 
    233             mGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
    234             mGlue.setArtist("A Googleer");
    235             mGlue.setTitle("Diving with Sharks");
    236             mGlue.setMediaSource(
    237                     Uri.parse("android.resource://androidx.leanback.test/raw/video"));
    238         }
    239 
    240         @Override
    241         public void onStart() {
    242             super.onStart();
    243             Bitmap bitmap = BitmapFactory.decodeResource(getActivity().getResources(),
    244                     androidx.leanback.test.R.drawable.spiderman);
    245             mDetailsBackground.setCoverBitmap(bitmap);
    246         }
    247 
    248         @Override
    249         public void onStop() {
    250             mDetailsBackground.setCoverBitmap(null);
    251             super.onStop();
    252         }
    253     }
    254 
    255     public static class DetailsSupportFragmentWithVideo1 extends DetailsSupportFragmentWithVideo {
    256 
    257         @Override
    258         public void onCreate(Bundle savedInstanceState) {
    259             super.onCreate(savedInstanceState);
    260             setItem(new PhotoItem("Hello world", "Fake content goes here",
    261                     androidx.leanback.test.R.drawable.spiderman));
    262         }
    263     }
    264 
    265     public static class DetailsSupportFragmentWithVideo2 extends DetailsSupportFragmentWithVideo {
    266 
    267         @Override
    268         public void onStart() {
    269             super.onStart();
    270             setItem(new PhotoItem("Hello world", "Fake content goes here",
    271                     androidx.leanback.test.R.drawable.spiderman));
    272         }
    273     }
    274 
    275     private void navigateBetweenRowsAndVideoUsingRequestFocusInternal(Class cls)
    276             throws Throwable {
    277         SingleSupportFragmentTestActivity activity = launchAndWaitActivity(cls,
    278                 new Options().uiVisibility(
    279                         View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
    280 
    281         final DetailsSupportFragmentWithVideo detailsFragment =
    282                 (DetailsSupportFragmentWithVideo) activity.getTestFragment();
    283         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    284             @Override
    285             public boolean canProceed() {
    286                 return detailsFragment.mVideoSupportFragment != null
    287                         && detailsFragment.mVideoSupportFragment.getView() != null
    288                         && detailsFragment.mGlue.isMediaPlaying();
    289             }
    290         });
    291 
    292         final int screenHeight = detailsFragment.getRowsSupportFragment().getVerticalGridView()
    293                 .getHeight();
    294         final View firstRow = detailsFragment.getRowsSupportFragment().getVerticalGridView().getChildAt(0);
    295         final int originalFirstRowTop = firstRow.getTop();
    296         assertTrue(firstRow.hasFocus());
    297         assertTrue(firstRow.getTop() > 0 && firstRow.getTop() < screenHeight);
    298         assertTrue(detailsFragment.isShowingTitle());
    299 
    300         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
    301             @Override
    302             public void run() {
    303                 detailsFragment.mVideoSupportFragment.getView().requestFocus();
    304             }
    305         });
    306         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    307             @Override
    308             public boolean canProceed() {
    309                 return firstRow.getTop() >= screenHeight;
    310             }
    311         });
    312         assertFalse(detailsFragment.isShowingTitle());
    313 
    314         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
    315             @Override
    316             public void run() {
    317                 detailsFragment.getRowsSupportFragment().getVerticalGridView().requestFocus();
    318             }
    319         });
    320         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    321             @Override
    322             public boolean canProceed() {
    323                 return firstRow.getTop() == originalFirstRowTop;
    324             }
    325         });
    326         assertTrue(detailsFragment.isShowingTitle());
    327     }
    328 
    329     @Test
    330     public void navigateBetweenRowsAndVideoUsingRequestFocus1() throws Throwable {
    331         navigateBetweenRowsAndVideoUsingRequestFocusInternal(DetailsSupportFragmentWithVideo1.class);
    332     }
    333 
    334     @Test
    335     public void navigateBetweenRowsAndVideoUsingRequestFocus2() throws Throwable {
    336         navigateBetweenRowsAndVideoUsingRequestFocusInternal(DetailsSupportFragmentWithVideo2.class);
    337     }
    338 
    339     private void navigateBetweenRowsAndVideoUsingDPADInternal(Class cls) throws Throwable {
    340         SingleSupportFragmentTestActivity activity = launchAndWaitActivity(cls,
    341                 new Options().uiVisibility(
    342                         View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
    343 
    344         final DetailsSupportFragmentWithVideo detailsFragment =
    345                 (DetailsSupportFragmentWithVideo) activity.getTestFragment();
    346         // wait video playing
    347         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    348             @Override
    349             public boolean canProceed() {
    350                 return detailsFragment.mVideoSupportFragment != null
    351                         && detailsFragment.mVideoSupportFragment.getView() != null
    352                         && detailsFragment.mGlue.isMediaPlaying();
    353             }
    354         });
    355 
    356         final int screenHeight = detailsFragment.getRowsSupportFragment().getVerticalGridView()
    357                 .getHeight();
    358         final View firstRow = detailsFragment.getRowsSupportFragment().getVerticalGridView().getChildAt(0);
    359         final int originalFirstRowTop = firstRow.getTop();
    360         assertTrue(firstRow.hasFocus());
    361         assertTrue(firstRow.getTop() > 0 && firstRow.getTop() < screenHeight);
    362         assertTrue(detailsFragment.isShowingTitle());
    363 
    364         // navigate to video
    365         sendKeys(KeyEvent.KEYCODE_DPAD_UP);
    366         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    367             @Override
    368             public boolean canProceed() {
    369                 return firstRow.getTop() >= screenHeight;
    370             }
    371         });
    372 
    373         // wait auto hide play controls done:
    374         PollingCheck.waitFor(8000, new PollingCheck.PollingCheckCondition() {
    375             @Override
    376             public boolean canProceed() {
    377                 return ((PlaybackSupportFragment) detailsFragment.mVideoSupportFragment).mBgAlpha == 0;
    378             }
    379         });
    380 
    381         // navigate to details
    382         sendKeys(KeyEvent.KEYCODE_BACK);
    383         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    384             @Override
    385             public boolean canProceed() {
    386                 return firstRow.getTop() == originalFirstRowTop;
    387             }
    388         });
    389         assertTrue(detailsFragment.isShowingTitle());
    390     }
    391 
    392     @Test
    393     public void navigateBetweenRowsAndVideoUsingDPAD1() throws Throwable {
    394         navigateBetweenRowsAndVideoUsingDPADInternal(DetailsSupportFragmentWithVideo1.class);
    395     }
    396 
    397     @Test
    398     public void navigateBetweenRowsAndVideoUsingDPAD2() throws Throwable {
    399         navigateBetweenRowsAndVideoUsingDPADInternal(DetailsSupportFragmentWithVideo2.class);
    400     }
    401 
    402     public static class EmptyFragmentClass extends Fragment {
    403         @Override
    404         public void onStart() {
    405             super.onStart();
    406             getActivity().finish();
    407         }
    408     }
    409 
    410     private void fragmentOnStartWithVideoInternal(Class cls) throws Throwable {
    411         final SingleSupportFragmentTestActivity activity = launchAndWaitActivity(cls,
    412                 new Options().uiVisibility(
    413                         View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
    414 
    415         final DetailsSupportFragmentWithVideo detailsFragment =
    416                 (DetailsSupportFragmentWithVideo) activity.getTestFragment();
    417         // wait video playing
    418         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    419             @Override
    420             public boolean canProceed() {
    421                 return detailsFragment.mVideoSupportFragment != null
    422                         && detailsFragment.mVideoSupportFragment.getView() != null
    423                         && detailsFragment.mGlue.isMediaPlaying();
    424             }
    425         });
    426 
    427         final int screenHeight = detailsFragment.getRowsSupportFragment().getVerticalGridView()
    428                 .getHeight();
    429         final View firstRow = detailsFragment.getRowsSupportFragment().getVerticalGridView().getChildAt(0);
    430         final int originalFirstRowTop = firstRow.getTop();
    431         assertTrue(firstRow.hasFocus());
    432         assertTrue(firstRow.getTop() > 0 && firstRow.getTop() < screenHeight);
    433         assertTrue(detailsFragment.isShowingTitle());
    434 
    435         // navigate to video
    436         sendKeys(KeyEvent.KEYCODE_DPAD_UP);
    437         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    438             @Override
    439             public boolean canProceed() {
    440                 return firstRow.getTop() >= screenHeight;
    441             }
    442         });
    443 
    444         // start an empty activity
    445         InstrumentationRegistry.getInstrumentation().runOnMainSync(
    446                 new Runnable() {
    447                     @Override
    448                     public void run() {
    449                         Intent intent = new Intent(activity, SingleSupportFragmentTestActivity.class);
    450                         intent.putExtra(SingleSupportFragmentTestActivity.EXTRA_FRAGMENT_NAME,
    451                                 EmptyFragmentClass.class.getName());
    452                         activity.startActivity(intent);
    453                     }
    454                 }
    455         );
    456         PollingCheck.waitFor(2000, new PollingCheck.PollingCheckCondition() {
    457             @Override
    458             public boolean canProceed() {
    459                 return detailsFragment.isResumed();
    460             }
    461         });
    462         assertTrue(detailsFragment.mVideoSupportFragment.getView().hasFocus());
    463     }
    464 
    465     @Test
    466     public void fragmentOnStartWithVideo1() throws Throwable {
    467         fragmentOnStartWithVideoInternal(DetailsSupportFragmentWithVideo1.class);
    468     }
    469 
    470     @Test
    471     public void fragmentOnStartWithVideo2() throws Throwable {
    472         fragmentOnStartWithVideoInternal(DetailsSupportFragmentWithVideo2.class);
    473     }
    474 
    475     @Test
    476     public void navigateBetweenRowsAndTitle() throws Throwable {
    477         SingleSupportFragmentTestActivity activity =
    478                 launchAndWaitActivity(DetailsTestSupportFragment.class, new Options().uiVisibility(
    479                 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
    480         final DetailsTestSupportFragment detailsFragment =
    481                 (DetailsTestSupportFragment) activity.getTestFragment();
    482 
    483         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
    484             @Override
    485             public void run() {
    486                 detailsFragment.setOnSearchClickedListener(new View.OnClickListener() {
    487                     @Override
    488                     public void onClick(View view) {
    489                     }
    490                 });
    491                 detailsFragment.setItem(new PhotoItem("Hello world", "Fake content goes here",
    492                         androidx.leanback.test.R.drawable.spiderman));
    493             }
    494         });
    495 
    496         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    497             @Override
    498             public boolean canProceed() {
    499                 return detailsFragment.getRowsSupportFragment().getVerticalGridView().getChildCount() > 0;
    500             }
    501         });
    502         final View firstRow = detailsFragment.getRowsSupportFragment().getVerticalGridView().getChildAt(0);
    503         final int originalFirstRowTop = firstRow.getTop();
    504         final int screenHeight = detailsFragment.getRowsSupportFragment().getVerticalGridView()
    505                 .getHeight();
    506 
    507         assertTrue(firstRow.hasFocus());
    508         assertTrue(detailsFragment.isShowingTitle());
    509         assertTrue(firstRow.getTop() > 0 && firstRow.getTop() < screenHeight);
    510 
    511         sendKeys(KeyEvent.KEYCODE_DPAD_UP);
    512         PollingCheck.waitFor(new PollingCheck.ViewStableOnScreen(firstRow));
    513         assertTrue(detailsFragment.isShowingTitle());
    514         assertTrue(detailsFragment.getTitleView().hasFocus());
    515         assertEquals(originalFirstRowTop, firstRow.getTop());
    516 
    517         sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
    518         PollingCheck.waitFor(new PollingCheck.ViewStableOnScreen(firstRow));
    519         assertTrue(detailsFragment.isShowingTitle());
    520         assertTrue(firstRow.hasFocus());
    521         assertEquals(originalFirstRowTop, firstRow.getTop());
    522     }
    523 
    524     public static class DetailsSupportFragmentWithNoVideo extends DetailsTestSupportFragment {
    525 
    526         final DetailsSupportFragmentBackgroundController mDetailsBackground =
    527                 new DetailsSupportFragmentBackgroundController(this);
    528 
    529         public DetailsSupportFragmentWithNoVideo() {
    530             mTimeToLoadOverviewRow = mTimeToLoadRelatedRow = 100;
    531         }
    532 
    533         @Override
    534         public void onCreate(Bundle savedInstanceState) {
    535             super.onCreate(savedInstanceState);
    536             mDetailsBackground.enableParallax();
    537 
    538             setItem(new PhotoItem("Hello world", "Fake content goes here",
    539                     androidx.leanback.test.R.drawable.spiderman));
    540         }
    541 
    542         @Override
    543         public void onStart() {
    544             super.onStart();
    545             Bitmap bitmap = BitmapFactory.decodeResource(getActivity().getResources(),
    546                     androidx.leanback.test.R.drawable.spiderman);
    547             mDetailsBackground.setCoverBitmap(bitmap);
    548         }
    549 
    550         @Override
    551         public void onStop() {
    552             mDetailsBackground.setCoverBitmap(null);
    553             super.onStop();
    554         }
    555     }
    556 
    557     @Test
    558     public void lateSetupVideo() {
    559         final SingleSupportFragmentTestActivity activity =
    560                 launchAndWaitActivity(DetailsSupportFragmentWithNoVideo.class, new Options().uiVisibility(
    561                 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
    562         final DetailsSupportFragmentWithNoVideo detailsFragment =
    563                 (DetailsSupportFragmentWithNoVideo) activity.getTestFragment();
    564 
    565         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    566             @Override
    567             public boolean canProceed() {
    568                 return detailsFragment.getRowsSupportFragment().getVerticalGridView().getChildCount() > 0;
    569             }
    570         });
    571         final View firstRow = detailsFragment.getRowsSupportFragment().getVerticalGridView().getChildAt(0);
    572         final int screenHeight = detailsFragment.getRowsSupportFragment().getVerticalGridView()
    573                 .getHeight();
    574 
    575         assertTrue(firstRow.hasFocus());
    576         assertTrue(detailsFragment.isShowingTitle());
    577         assertTrue(firstRow.getTop() > 0 && firstRow.getTop() < screenHeight);
    578 
    579         sendKeys(KeyEvent.KEYCODE_DPAD_UP);
    580         assertTrue(firstRow.hasFocus());
    581 
    582         SystemClock.sleep(1000);
    583         InstrumentationRegistry.getInstrumentation().runOnMainSync(
    584                 new Runnable() {
    585                     @Override
    586                     public void run() {
    587                         final MediaPlayerGlue glue = new MediaPlayerGlue(activity);
    588                         detailsFragment.mDetailsBackgroundController.setupVideoPlayback(glue);
    589                         glue.setMode(MediaPlayerGlue.REPEAT_ALL);
    590                         glue.setArtist("A Googleer");
    591                         glue.setTitle("Diving with Sharks");
    592                         glue.setMediaSource(Uri.parse(
    593                                 "android.resource://androidx.leanback.test/raw/video"));
    594                     }
    595                 }
    596         );
    597 
    598         // after setup Video Playback the DPAD up will navigate to Video Fragment.
    599         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    600                 @Override
    601                     public boolean canProceed() {
    602                         return detailsFragment.mVideoSupportFragment != null
    603                                 && detailsFragment.mVideoSupportFragment.getView() != null;
    604                 }
    605         });
    606         sendKeys(KeyEvent.KEYCODE_DPAD_UP);
    607         assertTrue(detailsFragment.mVideoSupportFragment.getView().hasFocus());
    608         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    609             @Override
    610             public boolean canProceed() {
    611                 return ((MediaPlayerGlue) detailsFragment.mDetailsBackgroundController
    612                         .getPlaybackGlue()).isMediaPlaying();
    613             }
    614         });
    615         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    616             @Override
    617             public boolean canProceed() {
    618                 return 0 == getCoverDrawableAlpha(detailsFragment.mDetailsBackgroundController);
    619             }
    620         });
    621 
    622         // wait a little bit to replace with new Glue
    623         SystemClock.sleep(1000);
    624         InstrumentationRegistry.getInstrumentation().runOnMainSync(
    625                 new Runnable() {
    626                     @Override
    627                     public void run() {
    628                         final MediaPlayerGlue glue2 = new MediaPlayerGlue(activity);
    629                         detailsFragment.mDetailsBackgroundController.setupVideoPlayback(glue2);
    630                         glue2.setMode(MediaPlayerGlue.REPEAT_ALL);
    631                         glue2.setArtist("A Googleer");
    632                         glue2.setTitle("Diving with Sharks");
    633                         glue2.setMediaSource(Uri.parse(
    634                                 "android.resource://androidx.leanback.test/raw/video"));
    635                     }
    636                 }
    637         );
    638 
    639         // test switchToRows() and switchToVideo()
    640         InstrumentationRegistry.getInstrumentation().runOnMainSync(
    641                 new Runnable() {
    642                     @Override
    643                     public void run() {
    644                         detailsFragment.mDetailsBackgroundController.switchToRows();
    645                     }
    646                 }
    647         );
    648         assertTrue(detailsFragment.mRowsSupportFragment.getView().hasFocus());
    649         PollingCheck.waitFor(new PollingCheck.ViewStableOnScreen(firstRow));
    650         InstrumentationRegistry.getInstrumentation().runOnMainSync(
    651                 new Runnable() {
    652                     @Override
    653                     public void run() {
    654                         detailsFragment.mDetailsBackgroundController.switchToVideo();
    655                     }
    656                 }
    657         );
    658         assertTrue(detailsFragment.mVideoSupportFragment.getView().hasFocus());
    659         PollingCheck.waitFor(new PollingCheck.ViewStableOnScreen(firstRow));
    660     }
    661 
    662     @Test
    663     public void sharedGlueHost() {
    664         final SingleSupportFragmentTestActivity activity =
    665                 launchAndWaitActivity(DetailsSupportFragmentWithNoVideo.class, new Options().uiVisibility(
    666                         View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
    667         final DetailsSupportFragmentWithNoVideo detailsFragment =
    668                 (DetailsSupportFragmentWithNoVideo) activity.getTestFragment();
    669 
    670         SystemClock.sleep(1000);
    671         InstrumentationRegistry.getInstrumentation().runOnMainSync(
    672                 new Runnable() {
    673                     @Override
    674                     public void run() {
    675                         final MediaPlayerGlue glue1 = new MediaPlayerGlue(activity);
    676                         detailsFragment.mDetailsBackgroundController.setupVideoPlayback(glue1);
    677                         glue1.setArtist("A Googleer");
    678                         glue1.setTitle("Diving with Sharks");
    679                         glue1.setMediaSource(Uri.parse(
    680                                 "android.resource://androidx.leanback.test/raw/video"));
    681                     }
    682                 }
    683         );
    684 
    685         // after setup Video Playback the DPAD up will navigate to Video Fragment.
    686         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    687             @Override
    688             public boolean canProceed() {
    689                 return detailsFragment.mVideoSupportFragment != null
    690                         && detailsFragment.mVideoSupportFragment.getView() != null;
    691             }
    692         });
    693 
    694         final MediaPlayerGlue glue1 = (MediaPlayerGlue) detailsFragment
    695                 .mDetailsBackgroundController
    696                 .getPlaybackGlue();
    697         PlaybackGlueHost playbackGlueHost = glue1.getHost();
    698 
    699         // wait a little bit to replace with new Glue
    700         SystemClock.sleep(1000);
    701         InstrumentationRegistry.getInstrumentation().runOnMainSync(
    702                 new Runnable() {
    703                     @Override
    704                     public void run() {
    705                         final MediaPlayerGlue glue2 = new MediaPlayerGlue(activity);
    706                         detailsFragment.mDetailsBackgroundController.setupVideoPlayback(glue2);
    707                         glue2.setArtist("A Googleer");
    708                         glue2.setTitle("Diving with Sharks");
    709                         glue2.setMediaSource(Uri.parse(
    710                                 "android.resource://androidx.leanback.test/raw/video"));
    711                     }
    712                 }
    713         );
    714 
    715         // wait for new glue to get its glue host
    716         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    717             @Override
    718             public boolean canProceed() {
    719                 MediaPlayerGlue mediaPlayerGlue = (MediaPlayerGlue) detailsFragment
    720                         .mDetailsBackgroundController
    721                         .getPlaybackGlue();
    722                 return mediaPlayerGlue != null && mediaPlayerGlue != glue1
    723                         && mediaPlayerGlue.getHost() != null;
    724             }
    725         });
    726 
    727         final MediaPlayerGlue glue2 = (MediaPlayerGlue) detailsFragment
    728                 .mDetailsBackgroundController
    729                 .getPlaybackGlue();
    730 
    731         assertTrue(glue1.getHost() == null);
    732         assertTrue(glue2.getHost() == playbackGlueHost);
    733     }
    734 
    735     @Test
    736     public void clearVideo() {
    737         final SingleSupportFragmentTestActivity activity =
    738                 launchAndWaitActivity(DetailsSupportFragmentWithNoVideo.class, new Options().uiVisibility(
    739                 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
    740         final DetailsSupportFragmentWithNoVideo detailsFragment =
    741                 (DetailsSupportFragmentWithNoVideo) activity.getTestFragment();
    742 
    743         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    744             @Override
    745             public boolean canProceed() {
    746                 return detailsFragment.getRowsSupportFragment().getVerticalGridView().getChildCount() > 0;
    747             }
    748         });
    749         final View firstRow = detailsFragment.getRowsSupportFragment().getVerticalGridView().getChildAt(0);
    750         final int screenHeight = detailsFragment.getRowsSupportFragment().getVerticalGridView()
    751                 .getHeight();
    752 
    753         assertTrue(firstRow.hasFocus());
    754         assertTrue(detailsFragment.isShowingTitle());
    755         assertTrue(firstRow.getTop() > 0 && firstRow.getTop() < screenHeight);
    756 
    757         SystemClock.sleep(1000);
    758         InstrumentationRegistry.getInstrumentation().runOnMainSync(
    759                 new Runnable() {
    760                     @Override
    761                     public void run() {
    762                         final MediaPlayerGlue glue = new MediaPlayerGlue(activity);
    763                         detailsFragment.mDetailsBackgroundController.setupVideoPlayback(glue);
    764                         glue.setMode(MediaPlayerGlue.REPEAT_ALL);
    765                         glue.setArtist("A Googleer");
    766                         glue.setTitle("Diving with Sharks");
    767                         glue.setMediaSource(Uri.parse(
    768                                 "android.resource://androidx.leanback.test/raw/video"));
    769                     }
    770                 }
    771         );
    772 
    773         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    774             @Override
    775             public boolean canProceed() {
    776                 return ((MediaPlayerGlue) detailsFragment.mDetailsBackgroundController
    777                         .getPlaybackGlue()).isMediaPlaying();
    778             }
    779         });
    780         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    781             @Override
    782             public boolean canProceed() {
    783                 return 0 == getCoverDrawableAlpha(detailsFragment.mDetailsBackgroundController);
    784             }
    785         });
    786 
    787         // wait a little bit then reset glue
    788         SystemClock.sleep(1000);
    789         InstrumentationRegistry.getInstrumentation().runOnMainSync(
    790                 new Runnable() {
    791                     @Override
    792                     public void run() {
    793                         detailsFragment.mDetailsBackgroundController.setupVideoPlayback(null);
    794                     }
    795                 }
    796         );
    797         // background should fade in upon reset playback
    798         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    799             @Override
    800             public boolean canProceed() {
    801                 return 255 == getCoverDrawableAlpha(detailsFragment.mDetailsBackgroundController);
    802             }
    803         });
    804     }
    805 
    806     public static class DetailsSupportFragmentWithNoItem extends DetailsTestSupportFragment {
    807 
    808         final DetailsSupportFragmentBackgroundController mDetailsBackground =
    809                 new DetailsSupportFragmentBackgroundController(this);
    810 
    811         public DetailsSupportFragmentWithNoItem() {
    812             mTimeToLoadOverviewRow = mTimeToLoadRelatedRow = 100;
    813         }
    814 
    815         @Override
    816         public void onCreate(Bundle savedInstanceState) {
    817             super.onCreate(savedInstanceState);
    818             mDetailsBackground.enableParallax();
    819         }
    820 
    821         @Override
    822         public void onStart() {
    823             super.onStart();
    824             Bitmap bitmap = BitmapFactory.decodeResource(getActivity().getResources(),
    825                     androidx.leanback.test.R.drawable.spiderman);
    826             mDetailsBackground.setCoverBitmap(bitmap);
    827         }
    828 
    829         @Override
    830         public void onStop() {
    831             mDetailsBackground.setCoverBitmap(null);
    832             super.onStop();
    833         }
    834     }
    835 
    836     @Test
    837     public void noInitialItem() {
    838         SingleSupportFragmentTestActivity activity =
    839                 launchAndWaitActivity(DetailsSupportFragmentWithNoItem.class, new Options().uiVisibility(
    840                 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
    841         final DetailsSupportFragmentWithNoItem detailsFragment =
    842                 (DetailsSupportFragmentWithNoItem) activity.getTestFragment();
    843 
    844         final int recyclerViewHeight = detailsFragment.getRowsSupportFragment().getVerticalGridView()
    845                 .getHeight();
    846         assertTrue(recyclerViewHeight > 0);
    847 
    848         assertEquals(255, getCoverDrawableAlpha(detailsFragment.mDetailsBackgroundController));
    849         Drawable coverDrawable = detailsFragment.mDetailsBackgroundController.getCoverDrawable();
    850         assertEquals(0, coverDrawable.getBounds().top);
    851         assertEquals(recyclerViewHeight, coverDrawable.getBounds().bottom);
    852         Drawable bottomDrawable = detailsFragment.mDetailsBackgroundController.getBottomDrawable();
    853         assertEquals(recyclerViewHeight, bottomDrawable.getBounds().top);
    854         assertEquals(recyclerViewHeight, bottomDrawable.getBounds().bottom);
    855     }
    856 
    857     public static class DetailsSupportFragmentSwitchToVideoInOnCreate extends DetailsTestSupportFragment {
    858 
    859         final DetailsSupportFragmentBackgroundController mDetailsBackground =
    860                 new DetailsSupportFragmentBackgroundController(this);
    861 
    862         public DetailsSupportFragmentSwitchToVideoInOnCreate() {
    863             mTimeToLoadOverviewRow = mTimeToLoadRelatedRow = 100;
    864         }
    865 
    866         @Override
    867         public void onCreate(Bundle savedInstanceState) {
    868             super.onCreate(savedInstanceState);
    869             mDetailsBackground.enableParallax();
    870             mDetailsBackground.switchToVideo();
    871         }
    872 
    873         @Override
    874         public void onStart() {
    875             super.onStart();
    876             Bitmap bitmap = BitmapFactory.decodeResource(getActivity().getResources(),
    877                     androidx.leanback.test.R.drawable.spiderman);
    878             mDetailsBackground.setCoverBitmap(bitmap);
    879         }
    880 
    881         @Override
    882         public void onStop() {
    883             mDetailsBackground.setCoverBitmap(null);
    884             super.onStop();
    885         }
    886     }
    887 
    888     @Test
    889     public void switchToVideoInOnCreate() {
    890         final SingleSupportFragmentTestActivity activity =
    891                 launchAndWaitActivity(DetailsSupportFragmentSwitchToVideoInOnCreate.class,
    892                 new Options().uiVisibility(
    893                         View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
    894         final DetailsSupportFragmentSwitchToVideoInOnCreate detailsFragment =
    895                 (DetailsSupportFragmentSwitchToVideoInOnCreate) activity.getTestFragment();
    896 
    897         // the pending enter transition flag should be automatically cleared
    898         assertEquals(StateMachine.STATUS_INVOKED,
    899                 detailsFragment.STATE_ENTER_TRANSITION_COMPLETE.getStatus());
    900         assertNull(TransitionHelper.getEnterTransition(activity.getWindow()));
    901         assertEquals(0, getCoverDrawableAlpha(detailsFragment.mDetailsBackgroundController));
    902         assertTrue(detailsFragment.getRowsSupportFragment().getView().hasFocus());
    903         //SystemClock.sleep(5000);
    904         assertFalse(detailsFragment.isShowingTitle());
    905 
    906         SystemClock.sleep(1000);
    907         assertNull(detailsFragment.mVideoSupportFragment);
    908         InstrumentationRegistry.getInstrumentation().runOnMainSync(
    909                 new Runnable() {
    910                     @Override
    911                     public void run() {
    912                         final MediaPlayerGlue glue = new MediaPlayerGlue(activity);
    913                         detailsFragment.mDetailsBackgroundController.setupVideoPlayback(glue);
    914                         glue.setMode(MediaPlayerGlue.REPEAT_ALL);
    915                         glue.setArtist("A Googleer");
    916                         glue.setTitle("Diving with Sharks");
    917                         glue.setMediaSource(Uri.parse(
    918                                 "android.resource://androidx.leanback.test/raw/video"));
    919                     }
    920                 }
    921         );
    922         // once the video fragment is created it would be immediately assigned focus
    923         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    924             @Override
    925             public boolean canProceed() {
    926                 return detailsFragment.mVideoSupportFragment != null
    927                         && detailsFragment.mVideoSupportFragment.getView() != null
    928                         && detailsFragment.mVideoSupportFragment.getView().hasFocus();
    929             }
    930         });
    931         // wait auto hide play controls done:
    932         PollingCheck.waitFor(8000, new PollingCheck.PollingCheckCondition() {
    933             @Override
    934             public boolean canProceed() {
    935                 return ((PlaybackSupportFragment) detailsFragment.mVideoSupportFragment).mBgAlpha == 0;
    936             }
    937         });
    938 
    939         // switchToRows does nothing if there is no row
    940         InstrumentationRegistry.getInstrumentation().runOnMainSync(
    941                 new Runnable() {
    942                     @Override
    943                     public void run() {
    944                         detailsFragment.mDetailsBackgroundController.switchToRows();
    945                     }
    946                 }
    947         );
    948         assertTrue(detailsFragment.mVideoSupportFragment.getView().hasFocus());
    949 
    950         // create item, it should be layout outside screen
    951         InstrumentationRegistry.getInstrumentation().runOnMainSync(
    952                 new Runnable() {
    953                     @Override
    954                     public void run() {
    955                         detailsFragment.setItem(new PhotoItem("Hello world",
    956                                 "Fake content goes here",
    957                                 androidx.leanback.test.R.drawable.spiderman));
    958                     }
    959                 }
    960         );
    961         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    962             @Override
    963             public boolean canProceed() {
    964                 return detailsFragment.getVerticalGridView().getChildCount() > 0
    965                         && detailsFragment.getVerticalGridView().getChildAt(0).getTop()
    966                         >= detailsFragment.getVerticalGridView().getHeight();
    967             }
    968         });
    969 
    970         // pressing BACK will return to details row
    971         sendKeys(KeyEvent.KEYCODE_BACK);
    972         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
    973             @Override
    974             public boolean canProceed() {
    975                 return detailsFragment.getVerticalGridView().getChildAt(0).getTop()
    976                         < (detailsFragment.getVerticalGridView().getHeight() * 0.7f);
    977             }
    978         });
    979         assertTrue(detailsFragment.getVerticalGridView().getChildAt(0).hasFocus());
    980     }
    981 
    982     @Test
    983     public void switchToVideoBackToQuit() {
    984         final SingleSupportFragmentTestActivity activity =
    985                 launchAndWaitActivity(DetailsSupportFragmentSwitchToVideoInOnCreate.class,
    986                 new Options().uiVisibility(
    987                         View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
    988         final DetailsSupportFragmentSwitchToVideoInOnCreate detailsFragment =
    989                 (DetailsSupportFragmentSwitchToVideoInOnCreate) activity.getTestFragment();
    990 
    991         // the pending enter transition flag should be automatically cleared
    992         assertEquals(StateMachine.STATUS_INVOKED,
    993                 detailsFragment.STATE_ENTER_TRANSITION_COMPLETE.getStatus());
    994         assertNull(TransitionHelper.getEnterTransition(activity.getWindow()));
    995         assertEquals(0, getCoverDrawableAlpha(detailsFragment.mDetailsBackgroundController));
    996         assertTrue(detailsFragment.getRowsSupportFragment().getView().hasFocus());
    997         assertFalse(detailsFragment.isShowingTitle());
    998 
    999         SystemClock.sleep(1000);
   1000         assertNull(detailsFragment.mVideoSupportFragment);
   1001         InstrumentationRegistry.getInstrumentation().runOnMainSync(
   1002                 new Runnable() {
   1003                     @Override
   1004                     public void run() {
   1005                         final MediaPlayerGlue glue = new MediaPlayerGlue(activity);
   1006                         detailsFragment.mDetailsBackgroundController.setupVideoPlayback(glue);
   1007                         glue.setMode(MediaPlayerGlue.REPEAT_ALL);
   1008                         glue.setArtist("A Googleer");
   1009                         glue.setTitle("Diving with Sharks");
   1010                         glue.setMediaSource(Uri.parse(
   1011                                 "android.resource://androidx.leanback.test/raw/video"));
   1012                     }
   1013                 }
   1014         );
   1015         // once the video fragment is created it would be immediately assigned focus
   1016         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
   1017             @Override
   1018             public boolean canProceed() {
   1019                 return detailsFragment.mVideoSupportFragment != null
   1020                         && detailsFragment.mVideoSupportFragment.getView() != null
   1021                         && detailsFragment.mVideoSupportFragment.getView().hasFocus();
   1022             }
   1023         });
   1024         // wait auto hide play controls done:
   1025         PollingCheck.waitFor(8000, new PollingCheck.PollingCheckCondition() {
   1026             @Override
   1027             public boolean canProceed() {
   1028                 return ((PlaybackSupportFragment) detailsFragment.mVideoSupportFragment).mBgAlpha == 0;
   1029             }
   1030         });
   1031 
   1032         // before any details row is presented, pressing BACK will quit the activity
   1033         sendKeys(KeyEvent.KEYCODE_BACK);
   1034         PollingCheck.waitFor(4000, new PollingCheck.ActivityDestroy(activity));
   1035     }
   1036 
   1037     public static class DetailsSupportFragmentSwitchToVideoAndPrepareEntranceTransition
   1038             extends DetailsTestSupportFragment {
   1039 
   1040         final DetailsSupportFragmentBackgroundController mDetailsBackground =
   1041                 new DetailsSupportFragmentBackgroundController(this);
   1042 
   1043         public DetailsSupportFragmentSwitchToVideoAndPrepareEntranceTransition() {
   1044             mTimeToLoadOverviewRow = mTimeToLoadRelatedRow = 100;
   1045         }
   1046 
   1047         @Override
   1048         public void onCreate(Bundle savedInstanceState) {
   1049             super.onCreate(savedInstanceState);
   1050             mDetailsBackground.enableParallax();
   1051             mDetailsBackground.switchToVideo();
   1052             prepareEntranceTransition();
   1053         }
   1054 
   1055         @Override
   1056         public void onViewCreated(View view, Bundle savedInstanceState) {
   1057             super.onViewCreated(view, savedInstanceState);
   1058         }
   1059 
   1060         @Override
   1061         public void onStart() {
   1062             super.onStart();
   1063             Bitmap bitmap = BitmapFactory.decodeResource(getActivity().getResources(),
   1064                     androidx.leanback.test.R.drawable.spiderman);
   1065             mDetailsBackground.setCoverBitmap(bitmap);
   1066         }
   1067 
   1068         @Override
   1069         public void onStop() {
   1070             mDetailsBackground.setCoverBitmap(null);
   1071             super.onStop();
   1072         }
   1073     }
   1074 
   1075     @Test
   1076     public void switchToVideoInOnCreateAndPrepareEntranceTransition() {
   1077         SingleSupportFragmentTestActivity activity = launchAndWaitActivity(
   1078                 DetailsSupportFragmentSwitchToVideoAndPrepareEntranceTransition.class,
   1079                 new Options().uiVisibility(
   1080                         View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
   1081         final DetailsSupportFragmentSwitchToVideoAndPrepareEntranceTransition detailsFragment =
   1082                 (DetailsSupportFragmentSwitchToVideoAndPrepareEntranceTransition)
   1083                         activity.getTestFragment();
   1084 
   1085         assertEquals(StateMachine.STATUS_INVOKED,
   1086                 detailsFragment.STATE_ENTRANCE_COMPLETE.getStatus());
   1087     }
   1088 
   1089     public static class DetailsSupportFragmentEntranceTransition
   1090             extends DetailsTestSupportFragment {
   1091 
   1092         final DetailsSupportFragmentBackgroundController mDetailsBackground =
   1093                 new DetailsSupportFragmentBackgroundController(this);
   1094 
   1095         public DetailsSupportFragmentEntranceTransition() {
   1096             mTimeToLoadOverviewRow = mTimeToLoadRelatedRow = 100;
   1097         }
   1098 
   1099         @Override
   1100         public void onCreate(Bundle savedInstanceState) {
   1101             super.onCreate(savedInstanceState);
   1102             mDetailsBackground.enableParallax();
   1103             prepareEntranceTransition();
   1104         }
   1105 
   1106         @Override
   1107         public void onStart() {
   1108             super.onStart();
   1109             Bitmap bitmap = BitmapFactory.decodeResource(getActivity().getResources(),
   1110                     androidx.leanback.test.R.drawable.spiderman);
   1111             mDetailsBackground.setCoverBitmap(bitmap);
   1112         }
   1113 
   1114         @Override
   1115         public void onStop() {
   1116             mDetailsBackground.setCoverBitmap(null);
   1117             super.onStop();
   1118         }
   1119     }
   1120 
   1121     @Test
   1122     public void entranceTransitionBlocksSwitchToVideo() {
   1123         SingleSupportFragmentTestActivity activity =
   1124                 launchAndWaitActivity(DetailsSupportFragmentEntranceTransition.class,
   1125                 new Options().uiVisibility(
   1126                         View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
   1127         final DetailsSupportFragmentEntranceTransition detailsFragment =
   1128                 (DetailsSupportFragmentEntranceTransition)
   1129                         activity.getTestFragment();
   1130 
   1131         if (Build.VERSION.SDK_INT < 21) {
   1132             // when enter transition is not supported, mCanUseHost is immmediately true
   1133             assertTrue(detailsFragment.mDetailsBackgroundController.mCanUseHost);
   1134         } else {
   1135             // calling switchToVideo() between prepareEntranceTransition and entrance transition
   1136             // finishes will be ignored.
   1137             InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
   1138                 @Override
   1139                 public void run() {
   1140                     detailsFragment.mDetailsBackgroundController.switchToVideo();
   1141                 }
   1142             });
   1143             assertFalse(detailsFragment.mDetailsBackgroundController.mCanUseHost);
   1144         }
   1145         assertEquals(255, getCoverDrawableAlpha(detailsFragment.mDetailsBackgroundController));
   1146         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
   1147             @Override
   1148             public void run() {
   1149                 detailsFragment.setItem(new PhotoItem("Hello world", "Fake content goes here",
   1150                         androidx.leanback.test.R.drawable.spiderman));
   1151                 detailsFragment.startEntranceTransition();
   1152             }
   1153         });
   1154         // once Entrance transition is finished, mCanUseHost will be true
   1155         // and we can switchToVideo and fade out the background.
   1156         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
   1157             @Override
   1158             public boolean canProceed() {
   1159                 return detailsFragment.mDetailsBackgroundController.mCanUseHost;
   1160             }
   1161         });
   1162         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
   1163             @Override
   1164             public void run() {
   1165                 detailsFragment.mDetailsBackgroundController.switchToVideo();
   1166             }
   1167         });
   1168         PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
   1169             @Override
   1170             public boolean canProceed() {
   1171                 return 0 == getCoverDrawableAlpha(detailsFragment.mDetailsBackgroundController);
   1172             }
   1173         });
   1174     }
   1175 
   1176     public static class DetailsSupportFragmentEntranceTransitionTimeout extends DetailsTestSupportFragment {
   1177 
   1178         public DetailsSupportFragmentEntranceTransitionTimeout() {
   1179         }
   1180 
   1181         @Override
   1182         public void onCreate(Bundle savedInstanceState) {
   1183             super.onCreate(savedInstanceState);
   1184             prepareEntranceTransition();
   1185         }
   1186 
   1187     }
   1188 
   1189     @Test
   1190     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
   1191     public void startEntranceTransitionAfterDestroyed() {
   1192         SingleSupportFragmentTestActivity activity = launchAndWaitActivity(
   1193                 DetailsSupportFragmentEntranceTransition.class, new Options().uiVisibility(
   1194                         View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN),
   1195                 1000);
   1196         final DetailsSupportFragmentEntranceTransition detailsFragment =
   1197                 (DetailsSupportFragmentEntranceTransition)
   1198                         activity.getTestFragment();
   1199 
   1200         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
   1201             @Override
   1202             public void run() {
   1203                 detailsFragment.setItem(new PhotoItem("Hello world", "Fake content goes here",
   1204                         androidx.leanback.test.R.drawable.spiderman));
   1205             }
   1206         });
   1207         SystemClock.sleep(100);
   1208         activity.finish();
   1209         PollingCheck.waitFor(new PollingCheck.ActivityDestroy(activity));
   1210         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
   1211             @Override
   1212             public void run() {
   1213                 detailsFragment.startEntranceTransition();
   1214             }
   1215         });
   1216     }
   1217 }
   1218