|
|
@ -6,6 +6,7 @@ package org.mozilla.fenix.browser
|
|
|
|
|
|
|
|
|
|
|
|
import android.content.Context
|
|
|
|
import android.content.Context
|
|
|
|
import android.content.Intent
|
|
|
|
import android.content.Intent
|
|
|
|
|
|
|
|
import android.content.res.Configuration
|
|
|
|
import android.os.Build
|
|
|
|
import android.os.Build
|
|
|
|
import android.os.Bundle
|
|
|
|
import android.os.Bundle
|
|
|
|
import android.view.Gravity
|
|
|
|
import android.view.Gravity
|
|
|
@ -37,13 +38,15 @@ import kotlinx.coroutines.flow.mapNotNull
|
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
import kotlinx.coroutines.launch
|
|
|
|
import kotlinx.coroutines.withContext
|
|
|
|
import kotlinx.coroutines.withContext
|
|
|
|
import mozilla.appservices.places.BookmarkRoot
|
|
|
|
import mozilla.appservices.places.BookmarkRoot
|
|
|
|
import mozilla.components.browser.session.Session
|
|
|
|
|
|
|
|
import mozilla.components.browser.state.action.ContentAction
|
|
|
|
import mozilla.components.browser.state.action.ContentAction
|
|
|
|
|
|
|
|
import mozilla.components.browser.state.selector.findCustomTab
|
|
|
|
import mozilla.components.browser.state.selector.findCustomTabOrSelectedTab
|
|
|
|
import mozilla.components.browser.state.selector.findCustomTabOrSelectedTab
|
|
|
|
import mozilla.components.browser.state.selector.findTab
|
|
|
|
import mozilla.components.browser.state.selector.findTab
|
|
|
|
|
|
|
|
import mozilla.components.browser.state.selector.findTabOrCustomTab
|
|
|
|
import mozilla.components.browser.state.selector.findTabOrCustomTabOrSelectedTab
|
|
|
|
import mozilla.components.browser.state.selector.findTabOrCustomTabOrSelectedTab
|
|
|
|
import mozilla.components.browser.state.selector.getNormalOrPrivateTabs
|
|
|
|
import mozilla.components.browser.state.selector.getNormalOrPrivateTabs
|
|
|
|
import mozilla.components.browser.state.selector.selectedTab
|
|
|
|
import mozilla.components.browser.state.selector.selectedTab
|
|
|
|
|
|
|
|
import mozilla.components.browser.state.state.CustomTabSessionState
|
|
|
|
import mozilla.components.browser.state.state.SessionState
|
|
|
|
import mozilla.components.browser.state.state.SessionState
|
|
|
|
import mozilla.components.browser.state.state.TabSessionState
|
|
|
|
import mozilla.components.browser.state.state.TabSessionState
|
|
|
|
import mozilla.components.browser.state.state.content.DownloadState
|
|
|
|
import mozilla.components.browser.state.state.content.DownloadState
|
|
|
@ -57,6 +60,7 @@ import mozilla.components.feature.contextmenu.ContextMenuCandidate
|
|
|
|
import mozilla.components.feature.contextmenu.ContextMenuFeature
|
|
|
|
import mozilla.components.feature.contextmenu.ContextMenuFeature
|
|
|
|
import mozilla.components.feature.downloads.DownloadsFeature
|
|
|
|
import mozilla.components.feature.downloads.DownloadsFeature
|
|
|
|
import mozilla.components.feature.downloads.manager.FetchDownloadManager
|
|
|
|
import mozilla.components.feature.downloads.manager.FetchDownloadManager
|
|
|
|
|
|
|
|
import mozilla.components.feature.downloads.share.ShareDownloadFeature
|
|
|
|
import mozilla.components.feature.intent.ext.EXTRA_SESSION_ID
|
|
|
|
import mozilla.components.feature.intent.ext.EXTRA_SESSION_ID
|
|
|
|
import mozilla.components.feature.media.fullscreen.MediaSessionFullscreenFeature
|
|
|
|
import mozilla.components.feature.media.fullscreen.MediaSessionFullscreenFeature
|
|
|
|
import mozilla.components.feature.privatemode.feature.SecureWindowFeature
|
|
|
|
import mozilla.components.feature.privatemode.feature.SecureWindowFeature
|
|
|
@ -68,7 +72,6 @@ import mozilla.components.feature.session.FullScreenFeature
|
|
|
|
import mozilla.components.feature.session.PictureInPictureFeature
|
|
|
|
import mozilla.components.feature.session.PictureInPictureFeature
|
|
|
|
import mozilla.components.feature.session.SessionFeature
|
|
|
|
import mozilla.components.feature.session.SessionFeature
|
|
|
|
import mozilla.components.feature.session.SwipeRefreshFeature
|
|
|
|
import mozilla.components.feature.session.SwipeRefreshFeature
|
|
|
|
import mozilla.components.feature.session.behavior.EngineViewBottomBehavior
|
|
|
|
|
|
|
|
import mozilla.components.feature.sitepermissions.SitePermissions
|
|
|
|
import mozilla.components.feature.sitepermissions.SitePermissions
|
|
|
|
import mozilla.components.feature.sitepermissions.SitePermissionsFeature
|
|
|
|
import mozilla.components.feature.sitepermissions.SitePermissionsFeature
|
|
|
|
import mozilla.components.lib.state.ext.consumeFlow
|
|
|
|
import mozilla.components.lib.state.ext.consumeFlow
|
|
|
@ -81,6 +84,7 @@ import mozilla.components.support.ktx.android.view.exitImmersiveModeIfNeeded
|
|
|
|
import mozilla.components.support.ktx.android.view.hideKeyboard
|
|
|
|
import mozilla.components.support.ktx.android.view.hideKeyboard
|
|
|
|
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifAnyChanged
|
|
|
|
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifAnyChanged
|
|
|
|
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
|
|
|
|
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
|
|
|
|
|
|
|
|
import org.mozilla.fenix.BuildConfig
|
|
|
|
import org.mozilla.fenix.FeatureFlags
|
|
|
|
import org.mozilla.fenix.FeatureFlags
|
|
|
|
import org.mozilla.fenix.HomeActivity
|
|
|
|
import org.mozilla.fenix.HomeActivity
|
|
|
|
import org.mozilla.fenix.IntentReceiverActivity
|
|
|
|
import org.mozilla.fenix.IntentReceiverActivity
|
|
|
@ -100,9 +104,7 @@ import org.mozilla.fenix.components.toolbar.BrowserToolbarView
|
|
|
|
import org.mozilla.fenix.components.toolbar.BrowserToolbarViewInteractor
|
|
|
|
import org.mozilla.fenix.components.toolbar.BrowserToolbarViewInteractor
|
|
|
|
import org.mozilla.fenix.components.toolbar.DefaultBrowserToolbarController
|
|
|
|
import org.mozilla.fenix.components.toolbar.DefaultBrowserToolbarController
|
|
|
|
import org.mozilla.fenix.components.toolbar.DefaultBrowserToolbarMenuController
|
|
|
|
import org.mozilla.fenix.components.toolbar.DefaultBrowserToolbarMenuController
|
|
|
|
import org.mozilla.fenix.components.toolbar.SwipeRefreshScrollingViewBehavior
|
|
|
|
|
|
|
|
import org.mozilla.fenix.components.toolbar.ToolbarIntegration
|
|
|
|
import org.mozilla.fenix.components.toolbar.ToolbarIntegration
|
|
|
|
import org.mozilla.fenix.components.toolbar.ToolbarPosition
|
|
|
|
|
|
|
|
import org.mozilla.fenix.downloads.DownloadService
|
|
|
|
import org.mozilla.fenix.downloads.DownloadService
|
|
|
|
import org.mozilla.fenix.downloads.DynamicDownloadDialog
|
|
|
|
import org.mozilla.fenix.downloads.DynamicDownloadDialog
|
|
|
|
import org.mozilla.fenix.ext.accessibilityManager
|
|
|
|
import org.mozilla.fenix.ext.accessibilityManager
|
|
|
@ -124,8 +126,10 @@ import org.mozilla.fenix.theme.ThemeManager
|
|
|
|
import org.mozilla.fenix.utils.allowUndo
|
|
|
|
import org.mozilla.fenix.utils.allowUndo
|
|
|
|
import org.mozilla.fenix.wifi.SitePermissionsWifiIntegration
|
|
|
|
import org.mozilla.fenix.wifi.SitePermissionsWifiIntegration
|
|
|
|
import java.lang.ref.WeakReference
|
|
|
|
import java.lang.ref.WeakReference
|
|
|
|
import mozilla.components.feature.media.fullscreen.MediaFullscreenOrientationFeature
|
|
|
|
import mozilla.components.feature.session.behavior.EngineViewBrowserToolbarBehavior
|
|
|
|
import org.mozilla.fenix.FeatureFlags.newMediaSessionApi
|
|
|
|
import mozilla.components.feature.webauthn.WebAuthnFeature
|
|
|
|
|
|
|
|
import mozilla.components.support.base.feature.ActivityResultHandler
|
|
|
|
|
|
|
|
import mozilla.components.feature.session.behavior.ToolbarPosition as MozacToolbarPosition
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Base fragment extended by [BrowserFragment].
|
|
|
|
* Base fragment extended by [BrowserFragment].
|
|
|
@ -134,7 +138,7 @@ import org.mozilla.fenix.FeatureFlags.newMediaSessionApi
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
@ExperimentalCoroutinesApi
|
|
|
|
@ExperimentalCoroutinesApi
|
|
|
|
@Suppress("TooManyFunctions", "LargeClass")
|
|
|
|
@Suppress("TooManyFunctions", "LargeClass")
|
|
|
|
abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, ActivityResultHandler,
|
|
|
|
OnBackLongPressedListener, AccessibilityManager.AccessibilityStateChangeListener {
|
|
|
|
OnBackLongPressedListener, AccessibilityManager.AccessibilityStateChangeListener {
|
|
|
|
|
|
|
|
|
|
|
|
private lateinit var browserFragmentStore: BrowserFragmentStore
|
|
|
|
private lateinit var browserFragmentStore: BrowserFragmentStore
|
|
|
@ -157,6 +161,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
private val sessionFeature = ViewBoundFeatureWrapper<SessionFeature>()
|
|
|
|
private val sessionFeature = ViewBoundFeatureWrapper<SessionFeature>()
|
|
|
|
private val contextMenuFeature = ViewBoundFeatureWrapper<ContextMenuFeature>()
|
|
|
|
private val contextMenuFeature = ViewBoundFeatureWrapper<ContextMenuFeature>()
|
|
|
|
private val downloadsFeature = ViewBoundFeatureWrapper<DownloadsFeature>()
|
|
|
|
private val downloadsFeature = ViewBoundFeatureWrapper<DownloadsFeature>()
|
|
|
|
|
|
|
|
private val shareDownloadsFeature = ViewBoundFeatureWrapper<ShareDownloadFeature>()
|
|
|
|
private val appLinksFeature = ViewBoundFeatureWrapper<AppLinksFeature>()
|
|
|
|
private val appLinksFeature = ViewBoundFeatureWrapper<AppLinksFeature>()
|
|
|
|
private val promptsFeature = ViewBoundFeatureWrapper<PromptFeature>()
|
|
|
|
private val promptsFeature = ViewBoundFeatureWrapper<PromptFeature>()
|
|
|
|
private val findInPageIntegration = ViewBoundFeatureWrapper<FindInPageIntegration>()
|
|
|
|
private val findInPageIntegration = ViewBoundFeatureWrapper<FindInPageIntegration>()
|
|
|
@ -168,8 +173,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
private val sitePermissionWifiIntegration =
|
|
|
|
private val sitePermissionWifiIntegration =
|
|
|
|
ViewBoundFeatureWrapper<SitePermissionsWifiIntegration>()
|
|
|
|
ViewBoundFeatureWrapper<SitePermissionsWifiIntegration>()
|
|
|
|
private val secureWindowFeature = ViewBoundFeatureWrapper<SecureWindowFeature>()
|
|
|
|
private val secureWindowFeature = ViewBoundFeatureWrapper<SecureWindowFeature>()
|
|
|
|
private var fullScreenMediaFeature =
|
|
|
|
|
|
|
|
ViewBoundFeatureWrapper<MediaFullscreenOrientationFeature>()
|
|
|
|
|
|
|
|
private var fullScreenMediaSessionFeature =
|
|
|
|
private var fullScreenMediaSessionFeature =
|
|
|
|
ViewBoundFeatureWrapper<MediaSessionFullscreenFeature>()
|
|
|
|
ViewBoundFeatureWrapper<MediaSessionFullscreenFeature>()
|
|
|
|
private val searchFeature = ViewBoundFeatureWrapper<SearchFeature>()
|
|
|
|
private val searchFeature = ViewBoundFeatureWrapper<SearchFeature>()
|
|
|
@ -253,7 +256,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
@CallSuper
|
|
|
|
@CallSuper
|
|
|
|
internal open fun initializeUI(view: View, tab: SessionState) {
|
|
|
|
internal open fun initializeUI(view: View, tab: SessionState) {
|
|
|
|
val context = requireContext()
|
|
|
|
val context = requireContext()
|
|
|
|
val sessionManager = context.components.core.sessionManager
|
|
|
|
|
|
|
|
val store = context.components.core.store
|
|
|
|
val store = context.components.core.store
|
|
|
|
val activity = requireActivity() as HomeActivity
|
|
|
|
val activity = requireActivity() as HomeActivity
|
|
|
|
|
|
|
|
|
|
|
@ -280,14 +282,14 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
val browserToolbarController = DefaultBrowserToolbarController(
|
|
|
|
val browserToolbarController = DefaultBrowserToolbarController(
|
|
|
|
store = store,
|
|
|
|
store = store,
|
|
|
|
|
|
|
|
tabsUseCases = requireComponents.useCases.tabsUseCases,
|
|
|
|
activity = activity,
|
|
|
|
activity = activity,
|
|
|
|
navController = findNavController(),
|
|
|
|
navController = findNavController(),
|
|
|
|
metrics = requireComponents.analytics.metrics,
|
|
|
|
metrics = requireComponents.analytics.metrics,
|
|
|
|
readerModeController = readerMenuController,
|
|
|
|
readerModeController = readerMenuController,
|
|
|
|
sessionManager = requireComponents.core.sessionManager,
|
|
|
|
|
|
|
|
engineView = engineView,
|
|
|
|
engineView = engineView,
|
|
|
|
homeViewModel = homeViewModel,
|
|
|
|
homeViewModel = homeViewModel,
|
|
|
|
customTabSession = customTabSessionId?.let { sessionManager.findSessionById(it) },
|
|
|
|
customTabSessionId = customTabSessionId,
|
|
|
|
onTabCounterClicked = {
|
|
|
|
onTabCounterClicked = {
|
|
|
|
thumbnailsFeature.get()?.requestScreenshot()
|
|
|
|
thumbnailsFeature.get()?.requestScreenshot()
|
|
|
|
findNavController().nav(
|
|
|
|
findNavController().nav(
|
|
|
@ -317,17 +319,17 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
|
|
|
val browserToolbarMenuController = DefaultBrowserToolbarMenuController(
|
|
|
|
val browserToolbarMenuController = DefaultBrowserToolbarMenuController(
|
|
|
|
|
|
|
|
store = store,
|
|
|
|
activity = activity,
|
|
|
|
activity = activity,
|
|
|
|
navController = findNavController(),
|
|
|
|
navController = findNavController(),
|
|
|
|
metrics = requireComponents.analytics.metrics,
|
|
|
|
metrics = requireComponents.analytics.metrics,
|
|
|
|
settings = context.settings(),
|
|
|
|
settings = context.settings(),
|
|
|
|
readerModeController = readerMenuController,
|
|
|
|
readerModeController = readerMenuController,
|
|
|
|
sessionManager = requireComponents.core.sessionManager,
|
|
|
|
|
|
|
|
sessionFeature = sessionFeature,
|
|
|
|
sessionFeature = sessionFeature,
|
|
|
|
findInPageLauncher = { findInPageIntegration.withFeature { it.launch() } },
|
|
|
|
findInPageLauncher = { findInPageIntegration.withFeature { it.launch() } },
|
|
|
|
swipeRefresh = swipeRefresh,
|
|
|
|
swipeRefresh = swipeRefresh,
|
|
|
|
browserAnimator = browserAnimator,
|
|
|
|
browserAnimator = browserAnimator,
|
|
|
|
customTabSession = customTabSessionId?.let { sessionManager.findSessionById(it) },
|
|
|
|
customTabSessionId = customTabSessionId,
|
|
|
|
openInFenixIntent = openInFenixIntent,
|
|
|
|
openInFenixIntent = openInFenixIntent,
|
|
|
|
bookmarkTapped = { url: String, title: String ->
|
|
|
|
bookmarkTapped = { url: String, title: String ->
|
|
|
|
viewLifecycleOwner.lifecycleScope.launch {
|
|
|
|
viewLifecycleOwner.lifecycleScope.launch {
|
|
|
@ -349,7 +351,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
container = view.browserLayout,
|
|
|
|
container = view.browserLayout,
|
|
|
|
toolbarPosition = context.settings().toolbarPosition,
|
|
|
|
toolbarPosition = context.settings().toolbarPosition,
|
|
|
|
interactor = browserInteractor,
|
|
|
|
interactor = browserInteractor,
|
|
|
|
customTabSession = customTabSessionId?.let { sessionManager.findSessionById(it) },
|
|
|
|
customTabSession = customTabSessionId?.let { store.state.findCustomTab(it) },
|
|
|
|
lifecycleOwner = viewLifecycleOwner
|
|
|
|
lifecycleOwner = viewLifecycleOwner
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
@ -405,7 +407,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
view = view
|
|
|
|
view = view
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if (newMediaSessionApi) {
|
|
|
|
|
|
|
|
fullScreenMediaSessionFeature.set(
|
|
|
|
fullScreenMediaSessionFeature.set(
|
|
|
|
feature = MediaSessionFullscreenFeature(
|
|
|
|
feature = MediaSessionFullscreenFeature(
|
|
|
|
requireActivity(),
|
|
|
|
requireActivity(),
|
|
|
@ -414,16 +415,13 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
owner = this,
|
|
|
|
owner = this,
|
|
|
|
view = view
|
|
|
|
view = view
|
|
|
|
)
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
|
|
|
|
fullScreenMediaFeature.set(
|
|
|
|
val shareDownloadFeature = ShareDownloadFeature(
|
|
|
|
feature = MediaFullscreenOrientationFeature(
|
|
|
|
context = context.applicationContext,
|
|
|
|
requireActivity(),
|
|
|
|
httpClient = context.components.core.client,
|
|
|
|
context.components.core.store
|
|
|
|
store = store,
|
|
|
|
),
|
|
|
|
tabId = customTabSessionId
|
|
|
|
owner = this,
|
|
|
|
|
|
|
|
view = view
|
|
|
|
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val downloadFeature = DownloadsFeature(
|
|
|
|
val downloadFeature = DownloadsFeature(
|
|
|
|
context.applicationContext,
|
|
|
|
context.applicationContext,
|
|
|
@ -461,9 +459,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
|
|
|
|
|
|
|
|
downloadFeature.onDownloadStopped = { downloadState, _, downloadJobStatus ->
|
|
|
|
downloadFeature.onDownloadStopped = { downloadState, _, downloadJobStatus ->
|
|
|
|
// If the download is just paused, don't show any in-app notification
|
|
|
|
// If the download is just paused, don't show any in-app notification
|
|
|
|
if (downloadJobStatus == DownloadState.Status.COMPLETED ||
|
|
|
|
if (shouldShowCompletedDownloadDialog(downloadState, downloadJobStatus)) {
|
|
|
|
downloadJobStatus == DownloadState.Status.FAILED
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
saveDownloadDialogState(
|
|
|
|
saveDownloadDialogState(
|
|
|
|
downloadState.sessionId,
|
|
|
|
downloadState.sessionId,
|
|
|
@ -491,19 +487,22 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
onDismiss = { sharedViewModel.downloadDialogState.remove(downloadState.sessionId) }
|
|
|
|
onDismiss = { sharedViewModel.downloadDialogState.remove(downloadState.sessionId) }
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// Don't show the dialog if we aren't in the tab that started the download
|
|
|
|
|
|
|
|
if (downloadState.sessionId == sessionManager.selectedSession?.id) {
|
|
|
|
|
|
|
|
dynamicDownloadDialog.show()
|
|
|
|
dynamicDownloadDialog.show()
|
|
|
|
browserToolbarView.expand()
|
|
|
|
browserToolbarView.expand()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resumeDownloadDialogState(
|
|
|
|
resumeDownloadDialogState(
|
|
|
|
sessionManager.selectedSession?.id,
|
|
|
|
getCurrentTab()?.id,
|
|
|
|
store, view, context, toolbarHeight
|
|
|
|
store, view, context, toolbarHeight
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
shareDownloadsFeature.set(
|
|
|
|
|
|
|
|
shareDownloadFeature,
|
|
|
|
|
|
|
|
owner = this,
|
|
|
|
|
|
|
|
view = view
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
downloadsFeature.set(
|
|
|
|
downloadsFeature.set(
|
|
|
|
downloadFeature,
|
|
|
|
downloadFeature,
|
|
|
|
owner = this,
|
|
|
|
owner = this,
|
|
|
@ -532,7 +531,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
|
|
|
|
|
|
|
|
promptsFeature.set(
|
|
|
|
promptsFeature.set(
|
|
|
|
feature = PromptFeature(
|
|
|
|
feature = PromptFeature(
|
|
|
|
fragment = this,
|
|
|
|
activity = activity,
|
|
|
|
store = store,
|
|
|
|
store = store,
|
|
|
|
customTabId = customTabSessionId,
|
|
|
|
customTabId = customTabSessionId,
|
|
|
|
fragmentManager = parentFragmentManager,
|
|
|
|
fragmentManager = parentFragmentManager,
|
|
|
@ -553,7 +552,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
val directions = NavGraphDirections.actionGlobalShareFragment(
|
|
|
|
val directions = NavGraphDirections.actionGlobalShareFragment(
|
|
|
|
data = arrayOf(shareData),
|
|
|
|
data = arrayOf(shareData),
|
|
|
|
showPage = true,
|
|
|
|
showPage = true,
|
|
|
|
sessionId = getSessionById()?.id
|
|
|
|
sessionId = getCurrentTab()?.id
|
|
|
|
)
|
|
|
|
)
|
|
|
|
findNavController().navigate(directions)
|
|
|
|
findNavController().navigate(directions)
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -587,14 +586,14 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
|
|
|
|
|
|
|
|
searchFeature.set(
|
|
|
|
searchFeature.set(
|
|
|
|
feature = SearchFeature(store, customTabSessionId) { request, tabId ->
|
|
|
|
feature = SearchFeature(store, customTabSessionId) { request, tabId ->
|
|
|
|
val parentSession = sessionManager.findSessionById(tabId)
|
|
|
|
val parentSession = store.state.findTabOrCustomTab(tabId)
|
|
|
|
val useCase = if (request.isPrivate) {
|
|
|
|
val useCase = if (request.isPrivate) {
|
|
|
|
requireComponents.useCases.searchUseCases.newPrivateTabSearch
|
|
|
|
requireComponents.useCases.searchUseCases.newPrivateTabSearch
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
requireComponents.useCases.searchUseCases.newTabSearch
|
|
|
|
requireComponents.useCases.searchUseCases.newTabSearch
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (parentSession?.isCustomTabSession() == true) {
|
|
|
|
if (parentSession is CustomTabSessionState) {
|
|
|
|
useCase.invoke(request.query)
|
|
|
|
useCase.invoke(request.query)
|
|
|
|
requireActivity().startActivity(openInFenixIntent)
|
|
|
|
requireActivity().startActivity(openInFenixIntent)
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@ -643,7 +642,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
view = view
|
|
|
|
view = view
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if (FeatureFlags.webAuthFeature) {
|
|
|
|
// This component feature only works on Fenix when built on Mozilla infrastructure.
|
|
|
|
|
|
|
|
if (FeatureFlags.webAuthFeature && BuildConfig.MOZILLA_OFFICIAL) {
|
|
|
|
webAuthnFeature.set(
|
|
|
|
webAuthnFeature.set(
|
|
|
|
feature = WebAuthnFeature(
|
|
|
|
feature = WebAuthnFeature(
|
|
|
|
engine = requireComponents.core.engine,
|
|
|
|
engine = requireComponents.core.engine,
|
|
|
@ -818,34 +818,37 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
!inFullScreen
|
|
|
|
!inFullScreen
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun initializeEngineView(toolbarHeight: Int) {
|
|
|
|
@VisibleForTesting
|
|
|
|
|
|
|
|
internal fun initializeEngineView(toolbarHeight: Int) {
|
|
|
|
val context = requireContext()
|
|
|
|
val context = requireContext()
|
|
|
|
|
|
|
|
|
|
|
|
if (context.settings().isDynamicToolbarEnabled) {
|
|
|
|
if (!context.settings().shouldUseFixedTopToolbar && context.settings().isDynamicToolbarEnabled) {
|
|
|
|
engineView.setDynamicToolbarMaxHeight(toolbarHeight)
|
|
|
|
getEngineView().setDynamicToolbarMaxHeight(toolbarHeight)
|
|
|
|
|
|
|
|
|
|
|
|
val behavior = when (context.settings().toolbarPosition) {
|
|
|
|
val toolbarPosition = if (context.settings().shouldUseBottomToolbar) {
|
|
|
|
// Set engineView dynamic vertical clipping depending on the toolbar position.
|
|
|
|
MozacToolbarPosition.BOTTOM
|
|
|
|
ToolbarPosition.BOTTOM -> EngineViewBottomBehavior(context, null)
|
|
|
|
} else {
|
|
|
|
// Set scroll flags depending on if if the browser or the website is doing the scroll.
|
|
|
|
MozacToolbarPosition.TOP
|
|
|
|
ToolbarPosition.TOP -> SwipeRefreshScrollingViewBehavior(
|
|
|
|
}
|
|
|
|
|
|
|
|
(getSwipeRefreshLayout().layoutParams as CoordinatorLayout.LayoutParams).behavior =
|
|
|
|
|
|
|
|
EngineViewBrowserToolbarBehavior(
|
|
|
|
context,
|
|
|
|
context,
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
engineView,
|
|
|
|
getSwipeRefreshLayout(),
|
|
|
|
browserToolbarView
|
|
|
|
toolbarHeight,
|
|
|
|
|
|
|
|
toolbarPosition
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(swipeRefresh.layoutParams as CoordinatorLayout.LayoutParams).behavior = behavior
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// Ensure webpage's bottom elements are aligned to the very bottom of the engineView.
|
|
|
|
// Ensure webpage's bottom elements are aligned to the very bottom of the engineView.
|
|
|
|
engineView.setDynamicToolbarMaxHeight(0)
|
|
|
|
getEngineView().setDynamicToolbarMaxHeight(0)
|
|
|
|
|
|
|
|
|
|
|
|
// Effectively place the engineView on top of the toolbar if that is not dynamic.
|
|
|
|
// Effectively place the engineView on top/below of the toolbar if that is not dynamic.
|
|
|
|
|
|
|
|
val swipeRefreshParams =
|
|
|
|
|
|
|
|
getSwipeRefreshLayout().layoutParams as CoordinatorLayout.LayoutParams
|
|
|
|
if (context.settings().shouldUseBottomToolbar) {
|
|
|
|
if (context.settings().shouldUseBottomToolbar) {
|
|
|
|
val browserEngine = swipeRefresh.layoutParams as CoordinatorLayout.LayoutParams
|
|
|
|
swipeRefreshParams.bottomMargin = toolbarHeight
|
|
|
|
browserEngine.bottomMargin =
|
|
|
|
} else {
|
|
|
|
requireContext().resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
|
|
|
|
swipeRefreshParams.topMargin = toolbarHeight
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -1009,7 +1012,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
final override fun onViewStateRestored(savedInstanceState: Bundle?) {
|
|
|
|
final override fun onViewStateRestored(savedInstanceState: Bundle?) {
|
|
|
|
super.onViewStateRestored(savedInstanceState)
|
|
|
|
super.onViewStateRestored(savedInstanceState)
|
|
|
|
savedInstanceState?.getString(KEY_CUSTOM_TAB_SESSION_ID)?.let {
|
|
|
|
savedInstanceState?.getString(KEY_CUSTOM_TAB_SESSION_ID)?.let {
|
|
|
|
if (requireComponents.core.sessionManager.findSessionById(it)?.customTabConfig != null) {
|
|
|
|
if (requireComponents.core.store.state.findCustomTab(it) != null) {
|
|
|
|
customTabSessionId = it
|
|
|
|
customTabSessionId = it
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -1033,10 +1036,10 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Forwards activity results to the prompt feature.
|
|
|
|
* Forwards activity results to the [ActivityResultHandler] features.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
final override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
|
|
override fun onActivityResult(requestCode: Int, data: Intent?, resultCode: Int): Boolean {
|
|
|
|
listOf(
|
|
|
|
return listOf(
|
|
|
|
promptsFeature,
|
|
|
|
promptsFeature,
|
|
|
|
webAuthnFeature
|
|
|
|
webAuthnFeature
|
|
|
|
).any { it.onActivityResult(requestCode, data, resultCode) }
|
|
|
|
).any { it.onActivityResult(requestCode, data, resultCode) }
|
|
|
@ -1047,22 +1050,18 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
* or if it has a parent session and no more history
|
|
|
|
* or if it has a parent session and no more history
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
protected open fun removeSessionIfNeeded(): Boolean {
|
|
|
|
protected open fun removeSessionIfNeeded(): Boolean {
|
|
|
|
getSessionById()?.let { session ->
|
|
|
|
getCurrentTab()?.let { session ->
|
|
|
|
return if (session.source == SessionState.Source.ACTION_VIEW) {
|
|
|
|
return if (session.source == SessionState.Source.ACTION_VIEW) {
|
|
|
|
activity?.finish()
|
|
|
|
activity?.finish()
|
|
|
|
requireComponents.useCases.tabsUseCases.removeTab(session)
|
|
|
|
requireComponents.useCases.tabsUseCases.removeTab(session.id)
|
|
|
|
true
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
if (session.hasParentSession) {
|
|
|
|
val hasParentSession = session is TabSessionState && session.parentId != null
|
|
|
|
// The removeTab use case does not currently select a parent session, so
|
|
|
|
if (hasParentSession) {
|
|
|
|
// we are using sessionManager.remove
|
|
|
|
requireComponents.useCases.tabsUseCases.removeTab(session.id, selectParentIfExists = true)
|
|
|
|
requireComponents.core.sessionManager.remove(
|
|
|
|
|
|
|
|
session,
|
|
|
|
|
|
|
|
selectParentIfExists = true
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// We want to return to home if this session didn't have a parent session to select.
|
|
|
|
// We want to return to home if this session didn't have a parent session to select.
|
|
|
|
val goToOverview = !session.hasParentSession
|
|
|
|
val goToOverview = !hasParentSession
|
|
|
|
!goToOverview
|
|
|
|
!goToOverview
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -1127,20 +1126,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
(activity as HomeActivity).browsingModeManager.mode = sessionMode
|
|
|
|
(activity as HomeActivity).browsingModeManager.mode = sessionMode
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@VisibleForTesting
|
|
|
|
* Returns the current session.
|
|
|
|
internal fun getCurrentTab(): SessionState? {
|
|
|
|
*/
|
|
|
|
|
|
|
|
protected fun getSessionById(): Session? {
|
|
|
|
|
|
|
|
val sessionManager = requireComponents.core.sessionManager
|
|
|
|
|
|
|
|
val localCustomTabId = customTabSessionId
|
|
|
|
|
|
|
|
return if (localCustomTabId != null) {
|
|
|
|
|
|
|
|
sessionManager.findSessionById(localCustomTabId)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
sessionManager.selectedSession
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun getCurrentTab(): SessionState? {
|
|
|
|
|
|
|
|
return requireComponents.core.store.state.findCustomTabOrSelectedTab(customTabSessionId)
|
|
|
|
return requireComponents.core.store.state.findCustomTabOrSelectedTab(customTabSessionId)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -1228,12 +1215,14 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
.setText(getString(R.string.full_screen_notification))
|
|
|
|
.setText(getString(R.string.full_screen_notification))
|
|
|
|
.show()
|
|
|
|
.show()
|
|
|
|
activity?.enterToImmersiveMode()
|
|
|
|
activity?.enterToImmersiveMode()
|
|
|
|
|
|
|
|
browserToolbarView.collapse()
|
|
|
|
browserToolbarView.view.isVisible = false
|
|
|
|
browserToolbarView.view.isVisible = false
|
|
|
|
val browserEngine = swipeRefresh.layoutParams as CoordinatorLayout.LayoutParams
|
|
|
|
val browserEngine = swipeRefresh.layoutParams as CoordinatorLayout.LayoutParams
|
|
|
|
browserEngine.bottomMargin = 0
|
|
|
|
browserEngine.bottomMargin = 0
|
|
|
|
|
|
|
|
browserEngine.topMargin = 0
|
|
|
|
|
|
|
|
swipeRefresh.translationY = 0f
|
|
|
|
|
|
|
|
|
|
|
|
engineView.setDynamicToolbarMaxHeight(0)
|
|
|
|
engineView.setDynamicToolbarMaxHeight(0)
|
|
|
|
browserToolbarView.expand()
|
|
|
|
|
|
|
|
// Without this, fullscreen has a margin at the top.
|
|
|
|
// Without this, fullscreen has a margin at the top.
|
|
|
|
engineView.setVerticalClipping(0)
|
|
|
|
engineView.setVerticalClipping(0)
|
|
|
|
|
|
|
|
|
|
|
@ -1247,6 +1236,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
browserToolbarView.view.isVisible = true
|
|
|
|
browserToolbarView.view.isVisible = true
|
|
|
|
val toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
|
|
|
|
val toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
|
|
|
|
initializeEngineView(toolbarHeight)
|
|
|
|
initializeEngineView(toolbarHeight)
|
|
|
|
|
|
|
|
browserToolbarView.expand()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -1310,10 +1300,16 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
|
|
|
|
|
|
|
|
override fun onAccessibilityStateChanged(enabled: Boolean) {
|
|
|
|
override fun onAccessibilityStateChanged(enabled: Boolean) {
|
|
|
|
if (_browserToolbarView != null) {
|
|
|
|
if (_browserToolbarView != null) {
|
|
|
|
browserToolbarView.setScrollFlags(enabled)
|
|
|
|
browserToolbarView.setToolbarBehavior(enabled)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
|
|
|
|
|
|
|
super.onConfigurationChanged(newConfig)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_browserToolbarView?.dismissMenu()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This method is called in response to native web extension messages from
|
|
|
|
// This method is called in response to native web extension messages from
|
|
|
|
// content scripts (e.g the reader view extension). By the time these
|
|
|
|
// content scripts (e.g the reader view extension). By the time these
|
|
|
|
// messages are processed the fragment/view may no longer be attached.
|
|
|
|
// messages are processed the fragment/view may no longer be attached.
|
|
|
@ -1326,4 +1322,28 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Convenience method for replacing EngineView (id/engineView) in unit tests.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@VisibleForTesting
|
|
|
|
|
|
|
|
internal fun getEngineView() = engineView
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Convenience method for replacing SwipeRefreshLayout (id/swipeRefresh) in unit tests.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@VisibleForTesting
|
|
|
|
|
|
|
|
internal fun getSwipeRefreshLayout() = swipeRefresh
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@VisibleForTesting
|
|
|
|
|
|
|
|
internal fun shouldShowCompletedDownloadDialog(
|
|
|
|
|
|
|
|
downloadState: DownloadState,
|
|
|
|
|
|
|
|
status: DownloadState.Status
|
|
|
|
|
|
|
|
): Boolean {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val isValidStatus = status in listOf(DownloadState.Status.COMPLETED, DownloadState.Status.FAILED)
|
|
|
|
|
|
|
|
val isSameTab = downloadState.sessionId == getCurrentTab()?.id ?: false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return isValidStatus && isSameTab
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|