For #2818 - Adds Picture-in-Picture feature
parent
d3a5fe5eb4
commit
56eb2ec1d7
|
@ -72,6 +72,8 @@
|
||||||
android:name=".HomeActivity"
|
android:name=".HomeActivity"
|
||||||
android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize|layoutDirection|smallestScreenSize|screenLayout"
|
android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize|layoutDirection|smallestScreenSize|screenLayout"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
|
android:resizeableActivity="true"
|
||||||
|
android:supportsPictureInPicture="true"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
@ -106,6 +108,8 @@
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:persistableMode="persistNever"
|
android:persistableMode="persistNever"
|
||||||
android:taskAffinity=""
|
android:taskAffinity=""
|
||||||
|
android:resizeableActivity="true"
|
||||||
|
android:supportsPictureInPicture="true"
|
||||||
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
|
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
|
|
@ -48,4 +48,9 @@ object FeatureFlags {
|
||||||
* https://github.com/mozilla-mobile/fenix/issues/9059
|
* https://github.com/mozilla-mobile/fenix/issues/9059
|
||||||
*/
|
*/
|
||||||
val webPushIntegration = Config.channel.isNightlyOrDebug
|
val webPushIntegration = Config.channel.isNightlyOrDebug
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables picture-in-picture feature
|
||||||
|
*/
|
||||||
|
val pictureInPicture = Config.channel.isNightlyOrDebug
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,6 +219,16 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
|
||||||
super.onBackPressed()
|
super.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final override fun onUserLeaveHint() {
|
||||||
|
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach {
|
||||||
|
if (it is UserInteractionHandler && it.onHomePressed()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onUserLeaveHint()
|
||||||
|
}
|
||||||
|
|
||||||
protected open fun getBreadcrumbMessage(destination: NavDestination): String {
|
protected open fun getBreadcrumbMessage(destination: NavDestination): String {
|
||||||
val fragmentName = resources.getResourceEntryName(destination.id)
|
val fragmentName = resources.getResourceEntryName(destination.id)
|
||||||
return "Changing to fragment $fragmentName, isCustomTab: false"
|
return "Changing to fragment $fragmentName, isCustomTab: false"
|
||||||
|
|
|
@ -31,6 +31,7 @@ import kotlinx.coroutines.withContext
|
||||||
import mozilla.appservices.places.BookmarkRoot
|
import mozilla.appservices.places.BookmarkRoot
|
||||||
import mozilla.components.browser.session.Session
|
import mozilla.components.browser.session.Session
|
||||||
import mozilla.components.browser.session.SessionManager
|
import mozilla.components.browser.session.SessionManager
|
||||||
|
import mozilla.components.browser.session.runWithSessionIdOrSelected
|
||||||
import mozilla.components.concept.engine.prompt.ShareData
|
import mozilla.components.concept.engine.prompt.ShareData
|
||||||
import mozilla.components.feature.accounts.FxaCapability
|
import mozilla.components.feature.accounts.FxaCapability
|
||||||
import mozilla.components.feature.accounts.FxaWebChannelFeature
|
import mozilla.components.feature.accounts.FxaWebChannelFeature
|
||||||
|
@ -45,6 +46,7 @@ import mozilla.components.feature.prompts.PromptFeature
|
||||||
import mozilla.components.feature.prompts.share.ShareDelegate
|
import mozilla.components.feature.prompts.share.ShareDelegate
|
||||||
import mozilla.components.feature.readerview.ReaderViewFeature
|
import mozilla.components.feature.readerview.ReaderViewFeature
|
||||||
import mozilla.components.feature.session.FullScreenFeature
|
import mozilla.components.feature.session.FullScreenFeature
|
||||||
|
import mozilla.components.feature.session.PictureInPictureFeature
|
||||||
import mozilla.components.feature.session.SessionFeature
|
import mozilla.components.feature.session.SessionFeature
|
||||||
import mozilla.components.feature.session.SessionUseCases
|
import mozilla.components.feature.session.SessionUseCases
|
||||||
import mozilla.components.feature.session.SwipeRefreshFeature
|
import mozilla.components.feature.session.SwipeRefreshFeature
|
||||||
|
@ -123,6 +125,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
||||||
private val swipeRefreshFeature = ViewBoundFeatureWrapper<SwipeRefreshFeature>()
|
private val swipeRefreshFeature = ViewBoundFeatureWrapper<SwipeRefreshFeature>()
|
||||||
private val webchannelIntegration = ViewBoundFeatureWrapper<FxaWebChannelFeature>()
|
private val webchannelIntegration = ViewBoundFeatureWrapper<FxaWebChannelFeature>()
|
||||||
private val sitePermissionWifiIntegration = ViewBoundFeatureWrapper<SitePermissionsWifiIntegration>()
|
private val sitePermissionWifiIntegration = ViewBoundFeatureWrapper<SitePermissionsWifiIntegration>()
|
||||||
|
private var pipFeature: PictureInPictureFeature? = null
|
||||||
|
|
||||||
var customTabSessionId: String? = null
|
var customTabSessionId: String? = null
|
||||||
|
|
||||||
|
@ -324,6 +327,15 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
||||||
view = view
|
view = view
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (FeatureFlags.pictureInPicture) {
|
||||||
|
pipFeature = PictureInPictureFeature(
|
||||||
|
requireComponents.core.sessionManager,
|
||||||
|
requireActivity(),
|
||||||
|
customTabSessionId,
|
||||||
|
::pipModeChanged
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
appLinksFeature.set(
|
appLinksFeature.set(
|
||||||
feature = AppLinksFeature(
|
feature = AppLinksFeature(
|
||||||
context,
|
context,
|
||||||
|
@ -423,39 +435,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
||||||
feature = FullScreenFeature(
|
feature = FullScreenFeature(
|
||||||
sessionManager,
|
sessionManager,
|
||||||
SessionUseCases(sessionManager),
|
SessionUseCases(sessionManager),
|
||||||
customTabSessionId
|
customTabSessionId,
|
||||||
) { inFullScreen ->
|
::fullScreenChanged
|
||||||
if (inFullScreen) {
|
),
|
||||||
FenixSnackbar.make(
|
|
||||||
view = view,
|
|
||||||
duration = Snackbar.LENGTH_SHORT,
|
|
||||||
isDisplayedOnBrowserFragment = true
|
|
||||||
)
|
|
||||||
.setText(getString(R.string.full_screen_notification))
|
|
||||||
.show()
|
|
||||||
activity?.enterToImmersiveMode()
|
|
||||||
browserToolbarView.view.visibility = View.GONE
|
|
||||||
|
|
||||||
if (FeatureFlags.dynamicBottomToolbar) {
|
|
||||||
engineView.setDynamicToolbarMaxHeight(0)
|
|
||||||
browserToolbarView.expand()
|
|
||||||
// Without this, fullscreen has a margin at the top.
|
|
||||||
engineView.setVerticalClipping(0)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
activity?.exitImmersiveModeIfNeeded()
|
|
||||||
(activity as? HomeActivity)?.let { activity ->
|
|
||||||
activity.themeManager.applyStatusBarTheme(activity)
|
|
||||||
}
|
|
||||||
browserToolbarView.view.visibility = View.VISIBLE
|
|
||||||
if (FeatureFlags.dynamicBottomToolbar) {
|
|
||||||
engineView.setDynamicToolbarMaxHeight(toolbarHeight)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!FeatureFlags.dynamicBottomToolbar) {
|
|
||||||
updateLayoutMargins(inFullScreen)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
owner = this,
|
owner = this,
|
||||||
view = view
|
view = view
|
||||||
)
|
)
|
||||||
|
@ -593,7 +575,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
||||||
if (findNavController().currentDestination?.id != R.id.searchFragment) {
|
if (findNavController().currentDestination?.id != R.id.searchFragment) {
|
||||||
view?.hideKeyboard()
|
view?.hideKeyboard()
|
||||||
}
|
}
|
||||||
fullScreenFeature.onBackPressed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
|
@ -813,6 +794,63 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onHomePressed(): Boolean {
|
||||||
|
if (pipFeature?.onHomePressed() == true) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pipModeChanged(enabled: Boolean) {
|
||||||
|
val fullScreenMode =
|
||||||
|
requireComponents.core.sessionManager.runWithSessionIdOrSelected(customTabSessionId) { session ->
|
||||||
|
session.fullScreenMode
|
||||||
|
}
|
||||||
|
// If we're exiting PIP mode and we're in fullscreen mode, then we should exit fullscreen mode as well.
|
||||||
|
if (!enabled && fullScreenMode) {
|
||||||
|
onBackPressed()
|
||||||
|
fullScreenChanged(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun onPictureInPictureModeChanged(enabled: Boolean) {
|
||||||
|
pipFeature?.onPictureInPictureModeChanged(enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fullScreenChanged(inFullScreen: Boolean) {
|
||||||
|
if (inFullScreen) {
|
||||||
|
FenixSnackbar.make(
|
||||||
|
view = view!!,
|
||||||
|
duration = Snackbar.LENGTH_SHORT,
|
||||||
|
isDisplayedOnBrowserFragment = true
|
||||||
|
)
|
||||||
|
.setText(getString(R.string.full_screen_notification))
|
||||||
|
.show()
|
||||||
|
activity?.enterToImmersiveMode()
|
||||||
|
browserToolbarView.view.visibility = View.GONE
|
||||||
|
|
||||||
|
if (FeatureFlags.dynamicBottomToolbar) {
|
||||||
|
engineView.setDynamicToolbarMaxHeight(0)
|
||||||
|
browserToolbarView.expand()
|
||||||
|
// Without this, fullscreen has a margin at the top.
|
||||||
|
engineView.setVerticalClipping(0)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
activity?.exitImmersiveModeIfNeeded()
|
||||||
|
(activity as? HomeActivity)?.let { activity ->
|
||||||
|
activity.themeManager.applyStatusBarTheme(activity)
|
||||||
|
}
|
||||||
|
browserToolbarView.view.visibility = View.VISIBLE
|
||||||
|
if (FeatureFlags.dynamicBottomToolbar) {
|
||||||
|
val toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
|
||||||
|
engineView.setDynamicToolbarMaxHeight(toolbarHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!FeatureFlags.dynamicBottomToolbar) {
|
||||||
|
updateLayoutMargins(inFullScreen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dereference these views when the fragment view is destroyed to prevent memory leaks
|
* Dereference these views when the fragment view is destroyed to prevent memory leaks
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue