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