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