1
0
Fork 0

Add custom tab fragment

master
Tiger Oakes 2019-08-12 12:31:59 -04:00 committed by Emily Kager
parent ffbbe119f3
commit 66e14959ba
7 changed files with 234 additions and 131 deletions

View File

@ -200,50 +200,39 @@ open class HomeActivity : AppCompatActivity(), ShareFragment.TabsSharedCallback
load(searchTermOrURL, newTab, engine, forceSearch)
}
@Suppress("ComplexMethod")
fun openToBrowser(from: BrowserDirection, customTabSessionId: String? = null) {
if (sessionObserver == null)
sessionObserver = subscribeToSessions()
if (navHost.navController.alreadyOnDestination(R.id.browserFragment)) return
@IdRes val fragmentId = if (from.fragmentId != 0) from.fragmentId else null
val directions = when (from) {
BrowserDirection.FromGlobal -> {
NavGraphDirections.actionGlobalBrowser(customTabSessionId)
}
BrowserDirection.FromHome -> {
HomeFragmentDirections.actionHomeFragmentToBrowserFragment(customTabSessionId)
}
BrowserDirection.FromSearch -> {
SearchFragmentDirections.actionSearchFragmentToBrowserFragment(
customTabSessionId
)
}
BrowserDirection.FromSettings -> {
SettingsFragmentDirections.actionSettingsFragmentToBrowserFragment(
customTabSessionId
)
}
BrowserDirection.FromBookmarks -> {
BookmarkFragmentDirections.actionBookmarkFragmentToBrowserFragment(
customTabSessionId
)
}
BrowserDirection.FromHistory -> {
HistoryFragmentDirections.actionHistoryFragmentToBrowserFragment(
customTabSessionId
)
}
BrowserDirection.FromExceptions -> {
ExceptionsFragmentDirections.actionExceptionsFragmentToBrowserFragment(
customTabSessionId
)
}
}
val directions = getNavDirections(from, customTabSessionId)
navHost.navController.nav(fragmentId, directions)
}
protected open fun getNavDirections(
from: BrowserDirection,
customTabSessionId: String?
) = when (from) {
BrowserDirection.FromGlobal ->
NavGraphDirections.actionGlobalBrowser(customTabSessionId)
BrowserDirection.FromHome ->
HomeFragmentDirections.actionHomeFragmentToBrowserFragment(customTabSessionId)
BrowserDirection.FromSearch ->
SearchFragmentDirections.actionSearchFragmentToBrowserFragment(customTabSessionId)
BrowserDirection.FromSettings ->
SettingsFragmentDirections.actionSettingsFragmentToBrowserFragment(customTabSessionId)
BrowserDirection.FromBookmarks ->
BookmarkFragmentDirections.actionBookmarkFragmentToBrowserFragment(customTabSessionId)
BrowserDirection.FromHistory ->
HistoryFragmentDirections.actionHistoryFragmentToBrowserFragment(customTabSessionId)
BrowserDirection.FromExceptions ->
ExceptionsFragmentDirections.actionExceptionsFragmentToBrowserFragment(
customTabSessionId
)
}
private fun load(
searchTermOrURL: String,
newTab: Boolean,

View File

@ -57,18 +57,17 @@ import org.mozilla.fenix.collections.CreateCollectionViewModel
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.FindInPageIntegration
import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.components.toolbar.BrowserInteractor
import org.mozilla.fenix.components.toolbar.BrowserState
import org.mozilla.fenix.components.toolbar.BrowserStore
import org.mozilla.fenix.components.toolbar.BrowserToolbarController
import org.mozilla.fenix.components.toolbar.BrowserToolbarView
import org.mozilla.fenix.components.toolbar.BrowserToolbarViewInteractor
import org.mozilla.fenix.components.toolbar.DefaultBrowserToolbarController
import org.mozilla.fenix.components.toolbar.QuickActionSheetState
import org.mozilla.fenix.components.toolbar.ToolbarIntegration
import org.mozilla.fenix.downloads.DownloadService
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.enterToImmersiveMode
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.quickactionsheet.QuickActionSheetBehavior
import org.mozilla.fenix.settings.SupportUtils
@ -82,7 +81,7 @@ import org.mozilla.fenix.utils.Settings
@Suppress("TooManyFunctions", "LargeClass")
abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Observer {
protected lateinit var browserStore: BrowserStore
protected lateinit var browserInteractor: BrowserInteractor
protected lateinit var browserInteractor: BrowserToolbarViewInteractor
protected lateinit var browserToolbarView: BrowserToolbarView
private val sessionFeature = ViewBoundFeatureWrapper<SessionFeature>()
@ -139,8 +138,7 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
return view
}
@CallSuper
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
final override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
browserInitialized = initializeUI(view) != null
}
@ -476,7 +474,9 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
protected abstract fun createBrowserToolbarViewInteractor(
browserToolbarController: BrowserToolbarController,
session: Session?
): BrowserInteractor
): BrowserToolbarViewInteractor
protected abstract fun navToQuickSettingsSheet(session: Session, sitePermissions: SitePermissions?)
/**
* Returns the top and bottom margins.
@ -516,16 +516,7 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
}
view?.let {
val directions =
BrowserFragmentDirections.actionBrowserFragmentToQuickSettingsSheetDialogFragment(
sessionId = session.id,
url = session.url,
isSecured = session.securityInfo.secure,
isTrackingProtectionOn = session.trackerBlockingEnabled,
sitePermissions = sitePermissions,
gravity = getAppropriateLayoutGravity()
)
nav(R.id.browserFragment, directions)
navToQuickSettingsSheet(session, sitePermissions)
}
}
}

View File

@ -17,7 +17,6 @@ import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.transition.TransitionInflater
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.component_search.*
import kotlinx.android.synthetic.main.fragment_browser.view.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
@ -29,6 +28,7 @@ import mozilla.appservices.places.BookmarkRoot
import mozilla.components.browser.session.Session
import mozilla.components.feature.readerview.ReaderViewFeature
import mozilla.components.feature.session.ThumbnailsFeature
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.support.base.feature.BackHandler
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
@ -40,8 +40,8 @@ import org.mozilla.fenix.components.TabCollectionStorage
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.toolbar.BrowserInteractor
import org.mozilla.fenix.components.toolbar.BrowserToolbarController
import org.mozilla.fenix.components.toolbar.BrowserToolbarViewInteractor
import org.mozilla.fenix.components.toolbar.QuickActionSheetAction
import org.mozilla.fenix.customtabs.CustomTabsIntegration
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.home.sessioncontrol.SessionControlChange
@ -52,7 +52,7 @@ import org.mozilla.fenix.quickactionsheet.QuickActionSheetSessionObserver
import org.mozilla.fenix.quickactionsheet.QuickActionSheetView
/**
* Fragment used for browsing the web within the main app and external apps.
* Fragment used for browsing the web within the main app.
*/
@ObsoleteCoroutinesApi
@ExperimentalCoroutinesApi
@ -63,7 +63,6 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
private val readerViewFeature = ViewBoundFeatureWrapper<ReaderViewFeature>()
private val thumbnailsFeature = ViewBoundFeatureWrapper<ThumbnailsFeature>()
private val customTabsIntegration = ViewBoundFeatureWrapper<CustomTabsIntegration>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -93,25 +92,6 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
return super.initializeUI(view)?.also {
quickActionSheetView =
QuickActionSheetView(view.nestedScrollQuickAction, browserInteractor)
customTabSessionId?.let { customTabSessionId ->
customTabsIntegration.set(
feature = CustomTabsIntegration(
requireContext(),
requireComponents.core.sessionManager,
toolbar,
customTabSessionId,
activity,
view.nestedScrollQuickAction,
view.swipeRefresh,
onItemTapped = { browserInteractor.onBrowserToolbarMenuItemTapped(it) }
),
owner = this,
view = view)
}
thumbnailsFeature.set(
feature = ThumbnailsFeature(
requireContext(),
@ -181,46 +161,51 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
return readerViewFeature.onBackPressed() || super.onBackPressed()
}
override fun removeSessionIfNeeded(): Boolean {
if (customTabsIntegration.onBackPressed()) return true
getSessionById()?.let { session ->
if (session.source == Session.Source.ACTION_VIEW) requireComponents.core.sessionManager.remove(
session
)
}
return false
}
override fun createBrowserToolbarViewInteractor(
browserToolbarController: BrowserToolbarController,
session: Session?
) = BrowserInteractor(
context = context!!,
store = browserStore,
browserToolbarController = browserToolbarController,
quickActionSheetController = DefaultQuickActionSheetController(
): BrowserToolbarViewInteractor {
val interactor = BrowserInteractor(
context = context!!,
navController = findNavController(),
currentSession = getSessionById()
?: requireComponents.core.sessionManager.selectedSessionOrThrow,
appLinksUseCases = requireComponents.useCases.appLinksUseCases,
bookmarkTapped = {
lifecycleScope.launch { bookmarkTapped(it) }
}
),
readerModeController = DefaultReaderModeController(readerViewFeature),
customTabSession = customTabSessionId?.let { requireComponents.core.sessionManager.findSessionById(it) }
)
store = browserStore,
browserToolbarController = browserToolbarController,
quickActionSheetController = DefaultQuickActionSheetController(
context = context!!,
navController = findNavController(),
currentSession = getSessionById()
?: requireComponents.core.sessionManager.selectedSessionOrThrow,
appLinksUseCases = requireComponents.useCases.appLinksUseCases,
bookmarkTapped = {
lifecycleScope.launch { bookmarkTapped(it) }
}
),
readerModeController = DefaultReaderModeController(readerViewFeature),
currentSession = session
)
quickActionSheetView = QuickActionSheetView(view!!.nestedScrollQuickAction, interactor)
return interactor
}
override fun navToQuickSettingsSheet(session: Session, sitePermissions: SitePermissions?) {
val directions = BrowserFragmentDirections.actionBrowserFragmentToQuickSettingsSheetDialogFragment(
sessionId = session.id,
url = session.url,
isSecured = session.securityInfo.secure,
isTrackingProtectionOn = session.trackerBlockingEnabled,
sitePermissions = sitePermissions,
gravity = getAppropriateLayoutGravity()
)
nav(R.id.browserFragment, directions)
}
override fun getEngineMargins(): Pair<Int, Int> {
val toolbarAndQASSize = resources.getDimensionPixelSize(R.dimen.toolbar_and_qab_height)
val toolbarSize = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
return if (customTabSessionId != null) Pair(toolbarSize, 0) else Pair(0, toolbarAndQASSize)
return 0 to toolbarAndQASSize
}
override fun getAppropriateLayoutGravity() =
if (customTabSessionId != null) Gravity.TOP else Gravity.BOTTOM
override fun getAppropriateLayoutGravity() = Gravity.BOTTOM
private fun themeReaderViewControlsForPrivateMode(view: View) = with(view) {
listOf(

View File

@ -13,14 +13,9 @@ import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.quickactionsheet.QuickActionSheetController
import org.mozilla.fenix.quickactionsheet.QuickActionSheetViewInteractor
class BrowserInteractor(
private val context: Context,
private val store: BrowserStore,
private val browserToolbarController: BrowserToolbarController,
private val quickActionSheetController: QuickActionSheetController,
private val readerModeController: ReaderModeController,
private val customTabSession: Session?
) : BrowserToolbarViewInteractor, QuickActionSheetViewInteractor {
open class BrowserToolbarInteractor(
private val browserToolbarController: BrowserToolbarController
) : BrowserToolbarViewInteractor {
override fun onBrowserToolbarClicked() {
browserToolbarController.handleToolbarClick()
@ -29,6 +24,16 @@ class BrowserInteractor(
override fun onBrowserToolbarMenuItemTapped(item: ToolbarMenu.Item) {
browserToolbarController.handleToolbarItemInteraction(item)
}
}
class BrowserInteractor(
private val context: Context,
private val store: BrowserStore,
browserToolbarController: BrowserToolbarController,
private val quickActionSheetController: QuickActionSheetController,
private val readerModeController: ReaderModeController,
private val currentSession: Session?
) : BrowserToolbarInteractor(browserToolbarController), QuickActionSheetViewInteractor {
override fun onQuickActionSheetOpened() {
context.metrics.track(Event.QuickActionSheetOpened)
@ -52,7 +57,7 @@ class BrowserInteractor(
override fun onQuickActionSheetReadPressed() {
val enabled =
customTabSession?.readerMode ?: context.components.core.sessionManager.selectedSession?.readerMode ?: false
currentSession?.readerMode ?: context.components.core.sessionManager.selectedSession?.readerMode ?: false
if (enabled) {
context.metrics.track(Event.QuickActionSheetClosed)

View File

@ -7,10 +7,13 @@ package org.mozilla.fenix.customtabs
import androidx.navigation.NavDestination
import mozilla.components.browser.session.intent.getSessionId
import mozilla.components.support.utils.SafeIntent
import org.mozilla.fenix.browser.browsingmode.CustomTabBrowsingModeManager
import org.mozilla.fenix.theme.CustomTabThemeManager
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.NavGraphDirections
import org.mozilla.fenix.browser.browsingmode.CustomTabBrowsingModeManager
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.theme.CustomTabThemeManager
import java.security.InvalidParameterException
open class CustomTabActivity : HomeActivity() {
final override fun getSentryBreadcrumbMessage(destination: NavDestination): String {
@ -22,6 +25,17 @@ open class CustomTabActivity : HomeActivity() {
final override fun getIntentSessionId(intent: SafeIntent) = intent.getSessionId()
override fun getNavDirections(
from: BrowserDirection,
customTabSessionId: String?
) = when (from) {
BrowserDirection.FromGlobal ->
NavGraphDirections.actionGlobalExternalAppBrowser(customTabSessionId)
else -> throw InvalidParameterException(
"Tried to navigate to ExternalAppBrowserFragment from $from"
)
}
final override fun createBrowsingModeManager() =
CustomTabBrowsingModeManager()

View File

@ -0,0 +1,92 @@
/* 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 org.mozilla.fenix.customtabs
import android.view.Gravity
import android.view.View
import kotlinx.android.synthetic.main.component_search.*
import kotlinx.android.synthetic.main.fragment_browser.view.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ObsoleteCoroutinesApi
import mozilla.components.browser.session.Session
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.support.base.feature.BackHandler
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.BaseBrowserFragment
import org.mozilla.fenix.components.toolbar.BrowserToolbarController
import org.mozilla.fenix.components.toolbar.BrowserToolbarInteractor
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
/**
* Fragment used for browsing the web within external apps.
*/
@ObsoleteCoroutinesApi
@ExperimentalCoroutinesApi
class ExternalAppBrowserFragment : BaseBrowserFragment(), BackHandler {
private val customTabsIntegration = ViewBoundFeatureWrapper<CustomTabsIntegration>()
override fun initializeUI(view: View): Session? {
return super.initializeUI(view)?.also {
customTabSessionId?.let { customTabSessionId ->
customTabsIntegration.set(
feature = CustomTabsIntegration(
requireContext(),
requireComponents.core.sessionManager,
toolbar,
customTabSessionId,
activity,
view.nestedScrollQuickAction,
view.swipeRefresh,
onItemTapped = { browserInteractor.onBrowserToolbarMenuItemTapped(it) }
),
owner = this,
view = view)
}
consumeFrom(browserStore) {
browserToolbarView.update(it)
}
}
}
override fun removeSessionIfNeeded(): Boolean {
return customTabsIntegration.onBackPressed() || super.removeSessionIfNeeded()
}
override fun createBrowserToolbarViewInteractor(
browserToolbarController: BrowserToolbarController,
session: Session?
) = BrowserToolbarInteractor(browserToolbarController)
override fun navToQuickSettingsSheet(session: Session, sitePermissions: SitePermissions?) {
val directions = ExternalAppBrowserFragmentDirections
.actionExternalAppBrowserFragmentToQuickSettingsSheetDialogFragment(
sessionId = session.id,
url = session.url,
isSecured = session.securityInfo.secure,
isTrackingProtectionOn = session.trackerBlockingEnabled,
sitePermissions = sitePermissions,
gravity = getAppropriateLayoutGravity()
)
nav(R.id.externalAppBrowserFragment, directions)
}
override fun getEngineMargins(): Pair<Int, Int> {
val toolbarSize = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
return toolbarSize to 0
}
override fun getAppropriateLayoutGravity() = Gravity.TOP
companion object {
private const val SHARED_TRANSITION_MS = 200L
private const val TAB_ITEM_TRANSITION_NAME = "tab_item"
}
}

View File

@ -11,6 +11,12 @@
app:popUpTo="@id/nav_graph"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_global_external_app_browser"
app:destination="@id/externalAppBrowserFragment"
app:popUpTo="@id/nav_graph"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_global_search"
app:destination="@id/searchFragment"
@ -25,6 +31,26 @@
android:id="@+id/action_global_turn_on_sync"
app:destination="@id/turnOnSyncFragment" />
<action
android:id="@+id/action_global_settingsFragment"
app:destination="@id/settingsFragment" />
<action
android:id="@+id/action_global_searchEngineFragment"
app:destination="@id/searchEngineFragment" />
<action
android:id="@+id/action_global_accessibilityFragment"
app:destination="@id/accessibilityFragment" />
<action
android:id="@+id/action_global_deleteBrowsingDataFragment"
app:destination="@id/deleteBrowsingDataFragment" />
<action
android:id="@+id/action_global_homeFragment"
app:destination="@id/homeFragment" />
<fragment
android:id="@+id/homeFragment"
android:name="org.mozilla.fenix.home.HomeFragment"
@ -161,6 +187,22 @@
app:destination="@id/quickSettingsSheetDialogFragment" />
</fragment>
<fragment
android:id="@+id/externalAppBrowserFragment"
android:name="org.mozilla.fenix.customtabs.ExternalAppBrowserFragment"
tools:layout="@layout/fragment_browser">
<argument
android:name="activeSessionId"
app:argType="string"
app:nullable="true" />
<action
android:id="@+id/action_externalAppBrowserFragment_to_shareFragment"
app:destination="@id/shareFragment" />
<action
android:id="@+id/action_externalAppBrowserFragment_to_quickSettingsSheetDialogFragment"
app:destination="@id/quickSettingsSheetDialogFragment" />
</fragment>
<fragment
android:id="@+id/libraryFragment"
android:name="org.mozilla.fenix.library.LibraryFragment"
@ -471,19 +513,4 @@
<dialog
android:id="@+id/signOutFragment"
android:name="org.mozilla.fenix.settings.SignOutFragment" />
<action
android:id="@+id/action_global_settingsFragment"
app:destination="@id/settingsFragment" />
<action
android:id="@+id/action_global_searchEngineFragment"
app:destination="@id/searchEngineFragment" />
<action
android:id="@+id/action_global_accessibilityFragment"
app:destination="@id/accessibilityFragment" />
<action
android:id="@+id/action_global_deleteBrowsingDataFragment"
app:destination="@id/deleteBrowsingDataFragment" />
<action
android:id="@+id/action_global_homeFragment"
app:destination="@id/homeFragment" />
</navigation>
</navigation>