Home | History | Annotate | Download | only in pictureinpicture
      1 /*
      2  * Copyright (C) 2017 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 
     17 package com.example.android.pictureinpicture;
     18 
     19 import android.content.pm.ActivityInfo;
     20 import android.media.session.PlaybackState;
     21 import android.support.test.InstrumentationRegistry;
     22 import android.support.test.espresso.UiController;
     23 import android.support.test.espresso.ViewAction;
     24 import android.support.test.rule.ActivityTestRule;
     25 import android.support.test.runner.AndroidJUnit4;
     26 import android.support.v4.media.session.PlaybackStateCompat;
     27 import android.view.View;
     28 
     29 import com.example.android.pictureinpicture.widget.MovieView;
     30 
     31 import org.hamcrest.Description;
     32 import org.hamcrest.Matcher;
     33 import org.hamcrest.TypeSafeMatcher;
     34 import org.junit.Rule;
     35 import org.junit.Test;
     36 import org.junit.runner.RunWith;
     37 
     38 import static android.support.test.espresso.Espresso.onView;
     39 import static android.support.test.espresso.action.ViewActions.click;
     40 import static android.support.test.espresso.assertion.ViewAssertions.matches;
     41 import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
     42 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
     43 import static android.support.test.espresso.matcher.ViewMatchers.withId;
     44 import static org.hamcrest.Matchers.not;
     45 import static org.hamcrest.core.AllOf.allOf;
     46 import static org.hamcrest.core.Is.is;
     47 import static org.hamcrest.core.IsEqual.equalTo;
     48 import static org.junit.Assert.assertNotNull;
     49 import static org.junit.Assert.assertThat;
     50 import static org.junit.Assert.assertTrue;
     51 
     52 
     53 @RunWith(AndroidJUnit4.class)
     54 public class MediaSessionPlaybackActivityTest {
     55 
     56     @Rule
     57     public ActivityTestRule<MediaSessionPlaybackActivity> rule =
     58             new ActivityTestRule<>(MediaSessionPlaybackActivity.class);
     59 
     60     @Test
     61     public void movie_playingOnPip() throws Throwable {
     62         // The movie should be playing on start
     63         onView(withId(R.id.movie))
     64                 .check(matches(allOf(isDisplayed(), isPlaying())))
     65                 .perform(showControls());
     66         // Click on the button to enter Picture-in-Picture mode
     67         onView(withId(R.id.minimize)).perform(click());
     68         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     69         // The Activity is paused. We cannot use Espresso to test paused activities.
     70         rule.runOnUiThread(new Runnable() {
     71             @Override
     72             public void run() {
     73                 // We are now in Picture-in-Picture mode
     74                 assertTrue(rule.getActivity().isInPictureInPictureMode());
     75                 final MovieView view = rule.getActivity().findViewById(R.id.movie);
     76                 assertNotNull(view);
     77                 // The video should still be playing
     78                 assertTrue(view.isPlaying());
     79 
     80                 // The media session state should be playing.
     81                 assertMediaStateIs(PlaybackStateCompat.STATE_PLAYING);
     82             }
     83         });
     84     }
     85 
     86     @Test
     87     public void movie_pauseAndResume() throws Throwable {
     88         // The movie should be playing on start
     89         onView(withId(R.id.movie))
     90                 .check(matches(allOf(isDisplayed(), isPlaying())))
     91                 .perform(showControls());
     92         // Pause
     93         onView(withId(R.id.toggle)).perform(click());
     94         onView(withId(R.id.movie)).check(matches((not(isPlaying()))));
     95         // The media session state should be paused.
     96         assertMediaStateIs(PlaybackStateCompat.STATE_PAUSED);
     97         // Resume
     98         onView(withId(R.id.toggle)).perform(click());
     99         onView(withId(R.id.movie)).check(matches(isPlaying()));
    100         // The media session state should be playing.
    101         assertMediaStateIs(PlaybackStateCompat.STATE_PLAYING);
    102     }
    103 
    104     @Test
    105     public void fullscreen_enabledOnLandscape() throws Throwable {
    106         rule.runOnUiThread(new Runnable() {
    107             @Override
    108             public void run() {
    109                 rule.getActivity()
    110                         .setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    111             }
    112         });
    113         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
    114         rule.runOnUiThread(new Runnable() {
    115             @Override
    116             public void run() {
    117                 final View decorView = rule.getActivity().getWindow().getDecorView();
    118                 assertThat(decorView.getSystemUiVisibility(),
    119                         hasFlag(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN));
    120             }
    121         });
    122     }
    123 
    124     @Test
    125     public void fullscreen_disabledOnPortrait() throws Throwable {
    126         rule.runOnUiThread(new Runnable() {
    127             @Override
    128             public void run() {
    129                 rule.getActivity()
    130                         .setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    131             }
    132         });
    133         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
    134         rule.runOnUiThread(new Runnable() {
    135             @Override
    136             public void run() {
    137                 final View decorView = rule.getActivity().getWindow().getDecorView();
    138                 assertThat(decorView.getSystemUiVisibility(),
    139                         not(hasFlag(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)));
    140             }
    141         });
    142     }
    143 
    144     private void assertMediaStateIs(@PlaybackStateCompat.State int expectedState) {
    145         PlaybackState state = rule.getActivity().getMediaController().getPlaybackState();
    146         assertNotNull(state);
    147         assertThat(
    148                 "MediaSession is not in the correct state",
    149                 state.getState(),
    150                 is(equalTo(expectedState)));
    151     }
    152 
    153     private static Matcher<? super View> isPlaying() {
    154         return new TypeSafeMatcher<View>() {
    155             @Override
    156             protected boolean matchesSafely(View view) {
    157                 return ((MovieView) view).isPlaying();
    158             }
    159 
    160             @Override
    161             public void describeTo(Description description) {
    162                 description.appendText("MovieView is playing");
    163             }
    164         };
    165     }
    166 
    167     private static ViewAction showControls() {
    168         return new ViewAction() {
    169             @Override
    170             public Matcher<View> getConstraints() {
    171                 return isAssignableFrom(MovieView.class);
    172             }
    173 
    174             @Override
    175             public String getDescription() {
    176                 return "Show controls of MovieView";
    177             }
    178 
    179             @Override
    180             public void perform(UiController uiController, View view) {
    181                 uiController.loopMainThreadUntilIdle();
    182                 ((MovieView) view).showControls();
    183                 uiController.loopMainThreadUntilIdle();
    184             }
    185         };
    186     }
    187 
    188     private static Matcher<? super Integer> hasFlag(final int flag) {
    189         return new TypeSafeMatcher<Integer>() {
    190             @Override
    191             protected boolean matchesSafely(Integer i) {
    192                 return (i & flag) == flag;
    193             }
    194 
    195             @Override
    196             public void describeTo(Description description) {
    197                 description.appendText("Flag integer contains " + flag);
    198             }
    199         };
    200     }
    201 
    202 }
    203