DetailsFragmentTest.java revision 6525e063fbbd691a8553f4fc77f3960f93bea34d
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 */
16package android.support.v17.leanback.app;
17
18import static junit.framework.TestCase.assertFalse;
19
20import static org.junit.Assert.assertEquals;
21import static org.junit.Assert.assertTrue;
22
23import android.animation.PropertyValuesHolder;
24import android.app.Fragment;
25import android.content.Intent;
26import android.graphics.Bitmap;
27import android.graphics.BitmapFactory;
28import android.graphics.Rect;
29import android.graphics.drawable.ColorDrawable;
30import android.graphics.drawable.Drawable;
31import android.net.Uri;
32import android.os.Bundle;
33import android.support.test.InstrumentationRegistry;
34import android.support.test.filters.LargeTest;
35import android.support.v17.leanback.R;
36import android.support.v17.leanback.graphics.FitWidthBitmapDrawable;
37import android.support.v17.leanback.media.MediaPlayerGlue;
38import android.support.v17.leanback.testutils.PollingCheck;
39import android.support.v17.leanback.widget.DetailsParallax;
40import android.support.v17.leanback.widget.DetailsParallaxDrawable;
41import android.support.v17.leanback.widget.ParallaxTarget;
42import android.support.v17.leanback.widget.RecyclerViewParallax;
43import android.support.v17.leanback.widget.VerticalGridView;
44import android.view.KeyEvent;
45import android.view.View;
46
47import org.junit.Test;
48import org.junit.runner.RunWith;
49import org.junit.runners.JUnit4;
50
51/**
52 * Unit tests for {@link DetailsFragment}.
53 */
54@RunWith(JUnit4.class)
55@LargeTest
56public class DetailsFragmentTest extends SingleFragmentTestBase {
57
58    static final int PARALLAX_VERTICAL_OFFSET = -300;
59
60    public static class DetailsFragmentParallax extends DetailsTestFragment {
61
62        private DetailsParallaxDrawable mParallaxDrawable;
63
64        public DetailsFragmentParallax() {
65            super();
66            mMinVerticalOffset = PARALLAX_VERTICAL_OFFSET;
67        }
68
69        @Override
70        public void onCreate(Bundle savedInstanceState) {
71            super.onCreate(savedInstanceState);
72            Drawable coverDrawable = new FitWidthBitmapDrawable();
73            mParallaxDrawable = new DetailsParallaxDrawable(
74                    getActivity(),
75                    getParallax(),
76                    coverDrawable,
77                    new ParallaxTarget.PropertyValuesHolderTarget(
78                            coverDrawable,
79                            PropertyValuesHolder.ofInt("verticalOffset", 0, mMinVerticalOffset)
80                    )
81            );
82
83            BackgroundManager backgroundManager = BackgroundManager.getInstance(getActivity());
84            backgroundManager.attach(getActivity().getWindow());
85            backgroundManager.setDrawable(mParallaxDrawable);
86        }
87
88        @Override
89        public void onStart() {
90            super.onStart();
91            setItem(new PhotoItem("Hello world", "Fake content goes here",
92                    android.support.v17.leanback.test.R.drawable.spiderman));
93        }
94
95        @Override
96        public void onResume() {
97            super.onResume();
98            Bitmap bitmap = BitmapFactory.decodeResource(getActivity().getResources(),
99                    android.support.v17.leanback.test.R.drawable.spiderman);
100            ((FitWidthBitmapDrawable) mParallaxDrawable.getCoverDrawable()).setBitmap(bitmap);
101        }
102
103        DetailsParallaxDrawable getParallaxDrawable() {
104            return mParallaxDrawable;
105        }
106    }
107
108    @Test
109    public void parallaxSetupTest() {
110        launchAndWaitActivity(DetailsFragmentTest.DetailsFragmentParallax.class,
111                new SingleFragmentTestBase.Options().uiVisibility(
112                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
113
114        double delta = 0.0002;
115        DetailsParallax dpm = ((DetailsFragment) mActivity.getTestFragment()).getParallax();
116
117        RecyclerViewParallax.ChildPositionProperty frameTop =
118                (RecyclerViewParallax.ChildPositionProperty) dpm.getOverviewRowTop();
119        assertEquals(0f, frameTop.getFraction(), delta);
120        assertEquals(0f, frameTop.getAdapterPosition(), delta);
121
122
123        RecyclerViewParallax.ChildPositionProperty frameBottom =
124                (RecyclerViewParallax.ChildPositionProperty) dpm.getOverviewRowBottom();
125        assertEquals(1f, frameBottom.getFraction(), delta);
126        assertEquals(0f, frameBottom.getAdapterPosition(), delta);
127    }
128
129    @Test
130    public void parallaxTest() throws Throwable {
131        launchAndWaitActivity(DetailsFragmentParallax.class,
132                new Options().uiVisibility(
133                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
134
135        final DetailsFragmentParallax detailsFragment =
136                (DetailsFragmentParallax) mActivity.getTestFragment();
137        DetailsParallaxDrawable drawable =
138                detailsFragment.getParallaxDrawable();
139        final FitWidthBitmapDrawable bitmapDrawable = (FitWidthBitmapDrawable)
140                drawable.getCoverDrawable();
141
142        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
143            @Override
144            public boolean canProceed() {
145                return detailsFragment.getRowsFragment().getAdapter() != null
146                        && detailsFragment.getRowsFragment().getAdapter().size() > 1;
147            }
148        });
149
150        final VerticalGridView verticalGridView = detailsFragment.getRowsFragment()
151                .getVerticalGridView();
152        final int windowHeight = verticalGridView.getHeight();
153        final int windowWidth = verticalGridView.getWidth();
154        // make sure background manager attached to window is same size as VerticalGridView
155        // i.e. no status bar.
156        assertEquals(windowHeight, mActivity.getWindow().getDecorView().getHeight());
157        assertEquals(windowWidth, mActivity.getWindow().getDecorView().getWidth());
158
159        final View detailsFrame = verticalGridView.findViewById(R.id.details_frame);
160
161        assertEquals(windowWidth, bitmapDrawable.getBounds().width());
162
163        final Rect detailsFrameRect = new Rect();
164        detailsFrameRect.set(0, 0, detailsFrame.getWidth(), detailsFrame.getHeight());
165        verticalGridView.offsetDescendantRectToMyCoords(detailsFrame, detailsFrameRect);
166
167        assertEquals(Math.min(windowHeight, detailsFrameRect.top),
168                bitmapDrawable.getBounds().height());
169        assertEquals(0, bitmapDrawable.getVerticalOffset());
170
171        assertTrue("TitleView is visible", detailsFragment.getView()
172                .findViewById(R.id.browse_title_group).getVisibility() == View.VISIBLE);
173
174        activityTestRule.runOnUiThread(new Runnable() {
175            @Override
176            public void run() {
177                verticalGridView.scrollToPosition(1);
178            }
179        });
180
181        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
182            @Override
183            public boolean canProceed() {
184                return bitmapDrawable.getVerticalOffset() == PARALLAX_VERTICAL_OFFSET
185                        && detailsFragment.getView()
186                        .findViewById(R.id.browse_title_group).getVisibility() != View.VISIBLE;
187            }
188        });
189
190        detailsFrameRect.set(0, 0, detailsFrame.getWidth(), detailsFrame.getHeight());
191        verticalGridView.offsetDescendantRectToMyCoords(detailsFrame, detailsFrameRect);
192
193        assertEquals(0, bitmapDrawable.getBounds().top);
194        assertEquals(Math.max(detailsFrameRect.top, 0), bitmapDrawable.getBounds().bottom);
195        assertEquals(windowWidth, bitmapDrawable.getBounds().width());
196
197        ColorDrawable colorDrawable = (ColorDrawable) (drawable.getChildAt(1).getDrawable());
198        assertEquals(windowWidth, colorDrawable.getBounds().width());
199        assertEquals(detailsFrameRect.bottom, colorDrawable.getBounds().top);
200        assertEquals(windowHeight, colorDrawable.getBounds().bottom);
201    }
202
203    public static class DetailsFragmentWithVideo extends DetailsTestFragment {
204
205        final DetailsFragmentBackgroundController mDetailsBackground =
206                new DetailsFragmentBackgroundController(this);
207        MediaPlayerGlue mGlue;
208
209        public DetailsFragmentWithVideo() {
210            mTimeToLoadOverviewRow = mTimeToLoadRelatedRow = 100;
211        }
212
213        @Override
214        public void onCreate(Bundle savedInstanceState) {
215            super.onCreate(savedInstanceState);
216            mDetailsBackground.enableParallax();
217            mGlue = new MediaPlayerGlue(getActivity());
218            mDetailsBackground.setupVideoPlayback(mGlue);
219
220            mGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
221            mGlue.setArtist("A Googleer");
222            mGlue.setTitle("Diving with Sharks");
223            mGlue.setMediaSource(
224                    Uri.parse("android.resource://android.support.v17.leanback.test/raw/video"));
225        }
226
227        @Override
228        public void onStart() {
229            super.onStart();
230            Bitmap bitmap = BitmapFactory.decodeResource(getActivity().getResources(),
231                    android.support.v17.leanback.test.R.drawable.spiderman);
232            mDetailsBackground.setCoverBitmap(bitmap);
233        }
234
235        @Override
236        public void onStop() {
237            mDetailsBackground.setCoverBitmap(null);
238            super.onStop();
239        }
240    }
241
242    public static class DetailsFragmentWithVideo1 extends DetailsFragmentWithVideo {
243
244        @Override
245        public void onCreate(Bundle savedInstanceState) {
246            super.onCreate(savedInstanceState);
247            setItem(new PhotoItem("Hello world", "Fake content goes here",
248                    android.support.v17.leanback.test.R.drawable.spiderman));
249        }
250    }
251
252    public static class DetailsFragmentWithVideo2 extends DetailsFragmentWithVideo {
253
254        @Override
255        public void onStart() {
256            super.onStart();
257            setItem(new PhotoItem("Hello world", "Fake content goes here",
258                    android.support.v17.leanback.test.R.drawable.spiderman));
259        }
260    }
261
262    private void navigateBetweenRowsAndVideoUsingRequestFocusInternal(Class cls)
263            throws Throwable {
264        launchAndWaitActivity(cls,
265                new Options().uiVisibility(
266                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
267
268        final DetailsFragmentWithVideo detailsFragment =
269                (DetailsFragmentWithVideo) mActivity.getTestFragment();
270        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
271            @Override
272            public boolean canProceed() {
273                return detailsFragment.mVideoFragment != null
274                        && detailsFragment.mVideoFragment.getView() != null
275                        && detailsFragment.mGlue.isMediaPlaying();
276            }
277        });
278
279        final int screenHeight = detailsFragment.getRowsFragment().getVerticalGridView()
280                .getHeight();
281        final View firstRow = detailsFragment.getRowsFragment().getVerticalGridView().getChildAt(0);
282        final int originalFirstRowTop = firstRow.getTop();
283        assertTrue(firstRow.hasFocus());
284        assertTrue(firstRow.getTop() > 0 && firstRow.getTop() < screenHeight);
285        assertTrue(detailsFragment.isShowingTitle());
286
287        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
288            @Override
289            public void run() {
290                detailsFragment.mVideoFragment.getView().requestFocus();
291            }
292        });
293        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
294            @Override
295            public boolean canProceed() {
296                return firstRow.getTop() >= screenHeight;
297            }
298        });
299        assertFalse(detailsFragment.isShowingTitle());
300
301        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
302            @Override
303            public void run() {
304                detailsFragment.getRowsFragment().getVerticalGridView().requestFocus();
305            }
306        });
307        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
308            @Override
309            public boolean canProceed() {
310                return firstRow.getTop() == originalFirstRowTop;
311            }
312        });
313        assertTrue(detailsFragment.isShowingTitle());
314    }
315
316    @Test
317    public void navigateBetweenRowsAndVideoUsingRequestFocus1() throws Throwable {
318        navigateBetweenRowsAndVideoUsingRequestFocusInternal(DetailsFragmentWithVideo1.class);
319    }
320
321    @Test
322    public void navigateBetweenRowsAndVideoUsingRequestFocus2() throws Throwable {
323        navigateBetweenRowsAndVideoUsingRequestFocusInternal(DetailsFragmentWithVideo2.class);
324    }
325
326    private void navigateBetweenRowsAndVideoUsingDPADInternal(Class cls) throws Throwable {
327        launchAndWaitActivity(cls,
328                new Options().uiVisibility(
329                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
330
331        final DetailsFragmentWithVideo detailsFragment =
332                (DetailsFragmentWithVideo) mActivity.getTestFragment();
333        // wait video playing
334        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
335            @Override
336            public boolean canProceed() {
337                return detailsFragment.mVideoFragment != null
338                        && detailsFragment.mVideoFragment.getView() != null
339                        && detailsFragment.mGlue.isMediaPlaying();
340            }
341        });
342
343        final int screenHeight = detailsFragment.getRowsFragment().getVerticalGridView()
344                .getHeight();
345        final View firstRow = detailsFragment.getRowsFragment().getVerticalGridView().getChildAt(0);
346        final int originalFirstRowTop = firstRow.getTop();
347        assertTrue(firstRow.hasFocus());
348        assertTrue(firstRow.getTop() > 0 && firstRow.getTop() < screenHeight);
349        assertTrue(detailsFragment.isShowingTitle());
350
351        // navigate to video
352        sendKeys(KeyEvent.KEYCODE_DPAD_UP);
353        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
354            @Override
355            public boolean canProceed() {
356                return firstRow.getTop() >= screenHeight;
357            }
358        });
359
360        // wait auto hide play controls done:
361        PollingCheck.waitFor(8000, new PollingCheck.PollingCheckCondition() {
362            @Override
363            public boolean canProceed() {
364                return ((PlaybackFragment) detailsFragment.mVideoFragment).mBgAlpha == 0;
365            }
366        });
367
368        // navigate to details
369        sendKeys(KeyEvent.KEYCODE_BACK);
370        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
371            @Override
372            public boolean canProceed() {
373                return firstRow.getTop() == originalFirstRowTop;
374            }
375        });
376        assertTrue(detailsFragment.isShowingTitle());
377    }
378
379    @Test
380    public void navigateBetweenRowsAndVideoUsingDPAD1() throws Throwable {
381        navigateBetweenRowsAndVideoUsingDPADInternal(DetailsFragmentWithVideo1.class);
382    }
383
384    @Test
385    public void navigateBetweenRowsAndVideoUsingDPAD2() throws Throwable {
386        navigateBetweenRowsAndVideoUsingDPADInternal(DetailsFragmentWithVideo2.class);
387    }
388
389    private void fragmentOnStartWithVideoInternal(Class cls) throws Throwable {
390        launchAndWaitActivity(cls,
391                new Options().uiVisibility(
392                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
393
394        final DetailsFragmentWithVideo detailsFragment =
395                (DetailsFragmentWithVideo) mActivity.getTestFragment();
396        // wait video playing
397        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
398            @Override
399            public boolean canProceed() {
400                return detailsFragment.mVideoFragment != null
401                        && detailsFragment.mVideoFragment.getView() != null
402                        && detailsFragment.mGlue.isMediaPlaying();
403            }
404        });
405
406        final int screenHeight = detailsFragment.getRowsFragment().getVerticalGridView()
407                .getHeight();
408        final View firstRow = detailsFragment.getRowsFragment().getVerticalGridView().getChildAt(0);
409        final int originalFirstRowTop = firstRow.getTop();
410        assertTrue(firstRow.hasFocus());
411        assertTrue(firstRow.getTop() > 0 && firstRow.getTop() < screenHeight);
412        assertTrue(detailsFragment.isShowingTitle());
413
414        // navigate to video
415        sendKeys(KeyEvent.KEYCODE_DPAD_UP);
416        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
417            @Override
418            public boolean canProceed() {
419                return firstRow.getTop() >= screenHeight;
420            }
421        });
422
423        // start an empty activity
424        InstrumentationRegistry.getInstrumentation().runOnMainSync(
425                new Runnable() {
426                    @Override
427                    public void run() {
428                        Intent intent = new Intent(mActivity, SingleFragmentTestActivity.class);
429                        intent.putExtra(SingleFragmentTestActivity.EXTRA_FRAGMENT_NAME,
430                                Fragment.class.getName());
431                        mActivity.startActivity(intent);
432                    }
433                }
434        );
435        PollingCheck.waitFor(2000, new PollingCheck.PollingCheckCondition() {
436            @Override
437            public boolean canProceed() {
438                return !detailsFragment.isResumed();
439            }
440        });
441        // pop empty activity, have to wait 1000 before sending BACK key or the key will lose
442        // nowhere.
443        Thread.sleep(1000);
444        sendKeys(KeyEvent.KEYCODE_BACK);
445
446        PollingCheck.waitFor(2000, new PollingCheck.PollingCheckCondition() {
447            @Override
448            public boolean canProceed() {
449                return detailsFragment.isResumed();
450            }
451        });
452        assertTrue(detailsFragment.mVideoFragment.getView().hasFocus());
453    }
454
455    @Test
456    public void fragmentOnStartWithVideo1() throws Throwable {
457        fragmentOnStartWithVideoInternal(DetailsFragmentWithVideo1.class);
458    }
459
460    @Test
461    public void fragmentOnStartWithVideo2() throws Throwable {
462        fragmentOnStartWithVideoInternal(DetailsFragmentWithVideo2.class);
463    }
464
465    @Test
466    public void navigateBetweenRowsAndTitle() throws Throwable {
467        launchAndWaitActivity(DetailsTestFragment.class, new Options().uiVisibility(
468                View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN), 0);
469        final DetailsTestFragment detailsFragment =
470                (DetailsTestFragment) mActivity.getTestFragment();
471
472        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
473            @Override
474            public void run() {
475                detailsFragment.setOnSearchClickedListener(new View.OnClickListener() {
476                    @Override
477                    public void onClick(View view) {
478                    }
479                });
480                detailsFragment.setItem(new PhotoItem("Hello world", "Fake content goes here",
481                        android.support.v17.leanback.test.R.drawable.spiderman));
482            }
483        });
484
485        PollingCheck.waitFor(4000, new PollingCheck.PollingCheckCondition() {
486            @Override
487            public boolean canProceed() {
488                return detailsFragment.getRowsFragment().getVerticalGridView().getChildCount() > 0;
489            }
490        });
491        final View firstRow = detailsFragment.getRowsFragment().getVerticalGridView().getChildAt(0);
492        final int originalFirstRowTop = firstRow.getTop();
493        final int screenHeight = detailsFragment.getRowsFragment().getVerticalGridView()
494                .getHeight();
495
496        assertTrue(firstRow.hasFocus());
497        assertTrue(detailsFragment.isShowingTitle());
498        assertTrue(firstRow.getTop() > 0 && firstRow.getTop() < screenHeight);
499
500        sendKeys(KeyEvent.KEYCODE_DPAD_UP);
501        PollingCheck.waitFor(new PollingCheck.ViewStableOnScreen(firstRow));
502        assertTrue(detailsFragment.isShowingTitle());
503        assertTrue(detailsFragment.getTitleView().hasFocus());
504        assertEquals(originalFirstRowTop, firstRow.getTop());
505
506        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
507        PollingCheck.waitFor(new PollingCheck.ViewStableOnScreen(firstRow));
508        assertTrue(detailsFragment.isShowingTitle());
509        assertTrue(firstRow.hasFocus());
510        assertEquals(originalFirstRowTop, firstRow.getTop());
511    }
512}
513