Unverified Commit b88794d1 authored by Araz Abishov's avatar Araz Abishov Committed by Sebastian Kaspari
Browse files

Closes #1862: Adds support for picture-in-picture mode

parent 966719ff
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package mozilla.components.feature.session
import android.annotation.TargetApi
import android.app.Activity
import android.app.PictureInPictureParams
import android.content.pm.PackageManager
import android.os.Build
import mozilla.components.browser.session.SessionManager
/**
* A simple implementation of Picture-in-picture mode if on a supported platform.
*
* @param sessionManager Session Manager for observing the selected session's fullscreen mode changes.
* @param activity the activity with the EngineView for calling PIP mode when required; the AndroidX Fragment
* doesn't support this.
* @param pipChanged a change listener that allows the calling app to perform changes based on PIP mode.
*/
class PictureInPictureFeature(
private val sessionManager: SessionManager,
private val activity: Activity,
private val pipChanged: ((Boolean) -> Unit?)? = null
) {
private val hasSystemFeature = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
activity.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
fun onHomePressed(): Boolean {
if (!hasSystemFeature) {
return false
}
val fullScreenMode = sessionManager.selectedSession?.fullScreenMode ?: false
return fullScreenMode && enterPipModeCompat()
}
@TargetApi(Build.VERSION_CODES.O)
fun enterPipModeCompat() = when {
!hasSystemFeature -> false
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ->
activity.enterPictureInPictureMode(PictureInPictureParams.Builder().build())
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> {
activity.enterPictureInPictureMode()
true
}
else -> false
}
fun onPictureInPictureModeChanged(enabled: Boolean) = pipChanged?.invoke(enabled)
}
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package mozilla.components.feature.session
import android.app.Activity
import android.content.pm.PackageManager
import android.os.Build
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import mozilla.components.support.test.any
import mozilla.components.support.test.mock
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.verifyZeroInteractions
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
private interface PipChangedCallback : (Boolean) -> Unit
@RunWith(RobolectricTestRunner::class)
class PictureInPictureFeatureTest {
private val sessionManager: SessionManager = mock()
private val selectedSession: Session = mock()
private val activity: Activity = Mockito.mock(Activity::class.java, Mockito.RETURNS_DEEP_STUBS)
@Before
fun setUp() {
`when`(activity.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE))
.thenReturn(true)
}
@Test
@Config(sdk = [Build.VERSION_CODES.M])
fun `on home pressed without system feature on android m and lower`() {
`when`(activity.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE))
.thenReturn(false)
val pictureInPictureFeature = spy(PictureInPictureFeature(sessionManager, activity))
assertFalse(pictureInPictureFeature.onHomePressed())
verifyZeroInteractions(sessionManager)
verifyZeroInteractions(activity.packageManager)
verify(pictureInPictureFeature, never()).enterPipModeCompat()
}
@Test
@Config(sdk = [Build.VERSION_CODES.N])
fun `on home pressed without system feature on android n and above`() {
`when`(activity.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE))
.thenReturn(false)
val pictureInPictureFeature = spy(PictureInPictureFeature(sessionManager, activity))
assertFalse(pictureInPictureFeature.onHomePressed())
verifyZeroInteractions(sessionManager)
verify(activity.packageManager).hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
verify(pictureInPictureFeature, never()).enterPipModeCompat()
}
@Test
fun `on home pressed without a selected session`() {
val pictureInPictureFeature = spy(PictureInPictureFeature(sessionManager, activity))
assertFalse(pictureInPictureFeature.onHomePressed())
verify(sessionManager).selectedSession
verify(activity.packageManager).hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
verify(pictureInPictureFeature, never()).enterPipModeCompat()
}
@Test
fun `on home pressed with a selected session without a fullscreen mode`() {
val pictureInPictureFeature = spy(PictureInPictureFeature(sessionManager, activity))
`when`(selectedSession.fullScreenMode).thenReturn(false)
`when`(sessionManager.selectedSession).thenReturn(selectedSession)
assertFalse(pictureInPictureFeature.onHomePressed())
verify(sessionManager).selectedSession
verify(selectedSession).fullScreenMode
verify(pictureInPictureFeature, never()).enterPipModeCompat()
}
@Test
fun `on home pressed with a selected session in fullscreen and without pip mode`() {
val pictureInPictureFeature = spy(PictureInPictureFeature(sessionManager, activity))
`when`(selectedSession.fullScreenMode).thenReturn(true)
`when`(sessionManager.selectedSession).thenReturn(selectedSession)
doReturn(false).`when`(pictureInPictureFeature).enterPipModeCompat()
assertFalse(pictureInPictureFeature.onHomePressed())
verify(sessionManager).selectedSession
verify(selectedSession).fullScreenMode
verify(pictureInPictureFeature).enterPipModeCompat()
}
@Test
fun `on home pressed with a selected session in fullscreen and with pip mode`() {
val pictureInPictureFeature = spy(PictureInPictureFeature(sessionManager, activity))
`when`(selectedSession.fullScreenMode).thenReturn(true)
`when`(sessionManager.selectedSession).thenReturn(selectedSession)
doReturn(true).`when`(pictureInPictureFeature).enterPipModeCompat()
assertTrue(pictureInPictureFeature.onHomePressed())
verify(sessionManager).selectedSession
verify(selectedSession).fullScreenMode
verify(pictureInPictureFeature).enterPipModeCompat()
}
@Test
@Config(sdk = [Build.VERSION_CODES.M])
fun `enter pip mode compat on android m and below`() {
val pictureInPictureFeature = PictureInPictureFeature(sessionManager, activity)
assertFalse(pictureInPictureFeature.enterPipModeCompat())
}
@Test
@Config(sdk = [Build.VERSION_CODES.O])
fun `enter pip mode compat without system feature on android o`() {
`when`(activity.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE))
.thenReturn(false)
val pictureInPictureFeature = PictureInPictureFeature(sessionManager, activity)
assertFalse(pictureInPictureFeature.enterPipModeCompat())
verify(activity, never()).enterPictureInPictureMode(any())
verify(activity, never()).enterPictureInPictureMode()
}
@Test
@Config(sdk = [Build.VERSION_CODES.O])
fun `enter pip mode compat on android o and above`() {
val pictureInPictureFeature = PictureInPictureFeature(sessionManager, activity)
`when`(activity.enterPictureInPictureMode(any())).thenReturn(true)
assertTrue(pictureInPictureFeature.enterPipModeCompat())
verify(activity).enterPictureInPictureMode(any())
}
@Test
@Config(sdk = [Build.VERSION_CODES.N])
fun `enter pip mode compat on android n and above`() {
val pictureInPictureFeature = PictureInPictureFeature(sessionManager, activity)
assertTrue(pictureInPictureFeature.enterPipModeCompat())
verify(activity).enterPictureInPictureMode()
}
@Test
fun `on pip mode changed`() {
val pipChangedCallback: PipChangedCallback = mock()
val pipFeature = PictureInPictureFeature(sessionManager, activity, pipChangedCallback)
pipFeature.onPictureInPictureModeChanged(true)
verify(pipChangedCallback).invoke(true)
pipFeature.onPictureInPictureModeChanged(false)
verify(pipChangedCallback).invoke(false)
verifyNoMoreInteractions(sessionManager)
verify(activity, never()).enterPictureInPictureMode(any())
verify(activity, never()).enterPictureInPictureMode()
}
}
......@@ -46,6 +46,9 @@ permalink: /changelog/
* **support-ktx**
* Added `File.truncateDirectory()` to remove all files (and sub directories) in a directory.
* **feature-session**
* Adds support for the picture-in-picture mode in `PictureInPictureFeature`.
# 0.46.0
* [Commits](https://github.com/mozilla-mobile/android-components/compare/v0.45.0...v0.46.0)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment