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 androidx.test.InstrumentationRegistry
     21 import androidx.test.espresso.Espresso.onView
     22 import androidx.test.espresso.UiController
     23 import androidx.test.espresso.ViewAction
     24 import androidx.test.espresso.action.ViewActions.click
     25 import androidx.test.espresso.assertion.ViewAssertions.matches
     26 import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
     27 import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
     28 import androidx.test.espresso.matcher.ViewMatchers.withId
     29 import androidx.test.rule.ActivityTestRule
     30 import androidx.test.runner.AndroidJUnit4
     31 import android.support.v4.media.session.PlaybackStateCompat
     32 import android.view.View
     33 import com.example.android.pictureinpicture.widget.MovieView
     34 import org.hamcrest.Description
     35 import org.hamcrest.Matcher
     36 import org.hamcrest.Matchers.not
     37 import org.hamcrest.TypeSafeMatcher
     38 import org.hamcrest.core.AllOf.allOf
     39 import org.hamcrest.core.Is.`is`
     40 import org.hamcrest.core.IsEqual.equalTo
     41 import org.junit.Assert.assertNotNull
     42 import org.junit.Assert.assertThat
     43 import org.junit.Assert.assertTrue
     44 import org.junit.Rule
     45 import org.junit.Test
     46 import org.junit.runner.RunWith
     47 
     48 
     49 @RunWith(AndroidJUnit4::class)
     50 class MediaSessionPlaybackActivityTest {
     51 
     52     @Rule @JvmField
     53     val rule = ActivityTestRule(MediaSessionPlaybackActivity::class.java)
     54 
     55     @Test
     56     fun movie_playingOnPip() {
     57         // The movie should be playing on start
     58         onView(withId(R.id.movie))
     59                 .check(matches(allOf(isDisplayed(), isPlaying())))
     60                 .perform(showControls())
     61         // Click on the button to enter Picture-in-Picture mode
     62         onView(withId(R.id.minimize)).perform(click())
     63         InstrumentationRegistry.getInstrumentation().waitForIdleSync()
     64         // The Activity is paused. We cannot use Espresso to test paused activities.
     65         rule.runOnUiThread {
     66             // We are now in Picture-in-Picture mode
     67             assertTrue(rule.activity.isInPictureInPictureMode)
     68             val view = rule.activity.findViewById<MovieView>(R.id.movie)
     69             assertNotNull(view)
     70             // The video should still be playing
     71             assertTrue(view.isPlaying)
     72 
     73             // The media session state should be playing.
     74             assertMediaStateIs(PlaybackStateCompat.STATE_PLAYING)
     75         }
     76     }
     77 
     78     @Test
     79     fun movie_pauseAndResume() {
     80         // The movie should be playing on start
     81         onView(withId(R.id.movie))
     82                 .check(matches(allOf(isDisplayed(), isPlaying())))
     83                 .perform(showControls())
     84         // Pause
     85         onView(withId(R.id.toggle)).perform(click())
     86         onView(withId(R.id.movie)).check(matches(not(isPlaying())))
     87         // The media session state should be paused.
     88         assertMediaStateIs(PlaybackStateCompat.STATE_PAUSED)
     89         // Resume
     90         onView(withId(R.id.toggle)).perform(click())
     91         onView(withId(R.id.movie)).check(matches(isPlaying()))
     92         // The media session state should be playing.
     93         assertMediaStateIs(PlaybackStateCompat.STATE_PLAYING)
     94     }
     95 
     96     @Test
     97     fun fullscreen_enabledOnLandscape() {
     98         rule.runOnUiThread { rule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE }
     99         InstrumentationRegistry.getInstrumentation().waitForIdleSync()
    100         rule.runOnUiThread {
    101             val decorView = rule.activity.window.decorView
    102             assertThat(decorView.systemUiVisibility,
    103                     hasFlag(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN))
    104         }
    105     }
    106 
    107     @Test
    108     fun fullscreen_disabledOnPortrait() {
    109         rule.runOnUiThread {
    110             rule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
    111         }
    112         InstrumentationRegistry.getInstrumentation().waitForIdleSync()
    113         rule.runOnUiThread {
    114             val decorView = rule.activity.window.decorView
    115             assertThat(decorView.systemUiVisibility,
    116                     not(hasFlag(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)))
    117         }
    118     }
    119 
    120     private fun assertMediaStateIs(@PlaybackStateCompat.State expectedState: Int) {
    121         val state = rule.activity.mediaController.playbackState
    122         assertNotNull(state)
    123         assertThat(
    124                 "MediaSession is not in the correct state",
    125                 state?.state,
    126                 `is`<Int>(equalTo<Int>(expectedState)))
    127     }
    128 
    129     private fun isPlaying(): Matcher<View> {
    130         return object : TypeSafeMatcher<View>() {
    131             override fun matchesSafely(view: View): Boolean {
    132                 return (view as MovieView).isPlaying
    133             }
    134 
    135             override fun describeTo(description: Description) {
    136                 description.appendText("MovieView is playing")
    137             }
    138         }
    139     }
    140 
    141     private fun showControls(): ViewAction {
    142         return object : ViewAction {
    143             override fun getConstraints(): Matcher<View> {
    144                 return isAssignableFrom(MovieView::class.java)
    145             }
    146 
    147             override fun getDescription(): String {
    148                 return "Show controls of MovieView"
    149             }
    150 
    151             override fun perform(uiController: UiController, view: View) {
    152                 uiController.loopMainThreadUntilIdle()
    153                 (view as MovieView).showControls()
    154                 uiController.loopMainThreadUntilIdle()
    155             }
    156         }
    157     }
    158 
    159     private fun hasFlag(flag: Int): Matcher<in Int> {
    160         return object : TypeSafeMatcher<Int>() {
    161             override fun matchesSafely(i: Int?): Boolean {
    162                 return i?.and(flag) == flag
    163             }
    164 
    165             override fun describeTo(description: Description) {
    166                 description.appendText("Flag integer contains " + flag)
    167             }
    168         }
    169     }
    170 
    171 }
    172