1
0
Fork 0
fenix/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarController.kt

274 lines
12 KiB
Kotlin
Raw Normal View History

/* 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.components.toolbar
import android.app.Activity
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.View
import android.view.ViewGroup
import androidx.annotation.VisibleForTesting
import androidx.core.widget.NestedScrollView
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.navigation.NavController
import androidx.navigation.NavDirections
import androidx.navigation.NavOptions
import androidx.navigation.fragment.FragmentNavigator
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.bottomsheet.BottomSheetBehavior
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import mozilla.components.browser.session.Session
import mozilla.components.concept.engine.EngineView
import mozilla.components.support.ktx.kotlin.isUrl
import org.mozilla.fenix.NavGraphDirections
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.BrowserFragment
import org.mozilla.fenix.browser.BrowserFragmentDirections
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
import org.mozilla.fenix.components.FenixSnackbar
#4596 migrate collections (#5911) * For #4596: move code from CollectionCreationComponent to CollectionCreationStore Other than adding comments, no changes were made. The code will be updated in a following commit. This is in order to make the commit diff more readable. * For 4596: update CollectionCreateStore to libstate * For 4596: copied CollectionCreationUIView into CollectionCreationView Otherwise, no code was changed. The next commit will update this code. This is in order to make the commit diff more readable. * For 4596: update CollectionCreationView to LibState Note that the minimal changes possible to enable migration were made. Refactoring will happen in a later commit. * For 4596: updated CollectionCreationTabListAdapter to work with the new View * For 4596: updated SaveCollectionListAdapter to work with the new View * For 4596: implemented CollectionCreationController For now, it has an identical interface to the interactor. In a later commit several of its responsibilities will be moved around, some to the interactor and some to the reducer * For 4596: copied over previous reducer code No other changes were made. The code will be updated in the following commit. This is done to make changes more readable for the reviewer * For 4596: update reducer code param names Otherwise, no changes at this time * For 4596: add arguments to CreateCollectionFragment in nav_graph These will be used to replace the current CreateCollectionViewModel, which shares data between fragments in a way that doesn't fit within our architecture. * For 4596: pass arguments to collection via transaction instead of VM The VM will be removed in a later commit * For 4596: update BrowserToolbarController to share state to collection via its Direction * For 4596: removed CreateCollectionViewModel * For 4596: test tab retrieval in CreateCollectionFragment * For 4596: fix crashing CreateCollectionFragmentTest * For 4596: removed classes create collection classes used by old architecture * For 4596: collection interactor rename + kdoc * For 4596: moved collection interactor interface * For 4596: renamed CreateCollectionFragment All related classes followed the pattern of CollectionCreationX * For 4596: kdoc CollectionCreationController There's no effective difference between these calls and their interactor equivalent, so I linked to them * For 4596: fix bug that caused rename to not work * For 4596: removed unused collection actions These were unused before the LibState refactor * For 4596: kdoc StepChanged * For 4596: removed todos about moving logic to the reducer saveTabsToCollection: this could be moved, but that would involve creating a new action. SaveCollectionStep should probably be refactored out, so adding this layer of indirection seemed counterproductive handleBackPress: needs to be able to call dismiss(). The reducer doesn't (and shouldn't) be able to do that, so this needs to live here stepBack: called by handleBackPress. See above * For 4596: wrote tests for CollectionCreationController#stepback * For 4596: fixed tests broken by changes to collections * For 4596: small readability refactor for CollectionController#stepBack No change to functionality (see tests) * For 4596: broke apart CollectionView#update There's probably a lot more that could be done here, but smaller changes were made to reduce scope * For 4596: remove unnecessary todos It looks like we don't follow the suggested pattern in this project * For 4596: test CollectionCreationController#normalSessionSize * For 4596: updated naming in CollectionCreationController per review
2019-10-23 02:33:54 +02:00
import org.mozilla.fenix.collections.SaveCollectionStep
import org.mozilla.fenix.components.TabCollectionStorage
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.lib.Do
import org.mozilla.fenix.quickactionsheet.QuickActionSheetBehavior
2019-09-14 06:29:11 +02:00
import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
/**
* An interface that handles the view manipulation of the BrowserToolbar, triggered by the Interactor
*/
interface BrowserToolbarController {
fun handleToolbarPaste(text: String)
fun handleToolbarPasteAndGo(text: String)
fun handleToolbarItemInteraction(item: ToolbarMenu.Item)
fun handleToolbarClick()
fun handleTabCounterClick()
}
@Suppress("LargeClass")
class DefaultBrowserToolbarController(
private val activity: Activity,
private val snackbar: FenixSnackbar?,
private val navController: NavController,
2019-08-07 22:02:08 +02:00
private val browsingModeManager: BrowsingModeManager,
private val findInPageLauncher: () -> Unit,
private val browserLayout: ViewGroup,
private val engineView: EngineView,
private val adjustBackgroundAndNavigate: (NavDirections) -> Unit,
private val swipeRefresh: SwipeRefreshLayout,
private val customTabSession: Session?,
private val getSupportUrl: () -> String,
private val openInFenixIntent: Intent,
private val bottomSheetBehavior: QuickActionSheetBehavior<NestedScrollView>,
private val scope: LifecycleCoroutineScope,
private val tabCollectionStorage: TabCollectionStorage
) : BrowserToolbarController {
private val currentSession
get() = customTabSession ?: activity.components.core.sessionManager.selectedSession
override fun handleToolbarPaste(text: String) {
adjustBackgroundAndNavigate.invoke(
BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
sessionId = currentSession?.id,
pastedText = text
)
)
}
override fun handleToolbarPasteAndGo(text: String) {
if (text.isUrl()) {
activity.components.core.sessionManager.selectedSession?.searchTerms = ""
activity.components.useCases.sessionUseCases.loadUrl.invoke(text)
return
}
activity.components.core.sessionManager.selectedSession?.searchTerms = text
activity.components.useCases.searchUseCases.defaultSearch.invoke(text)
}
override fun handleToolbarClick() {
activity.components.analytics.metrics.track(
Event.SearchBarTapped(Event.SearchBarTapped.Source.BROWSER)
)
adjustBackgroundAndNavigate.invoke(
BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(currentSession?.id)
)
}
override fun handleTabCounterClick() {
animateTabAndNavigateHome()
}
@ExperimentalCoroutinesApi
@SuppressWarnings("ComplexMethod", "LongMethod")
override fun handleToolbarItemInteraction(item: ToolbarMenu.Item) {
val sessionUseCases = activity.components.useCases.sessionUseCases
trackToolbarItemInteraction(item)
Do exhaustive when (item) {
ToolbarMenu.Item.Back -> sessionUseCases.goBack.invoke(currentSession)
ToolbarMenu.Item.Forward -> sessionUseCases.goForward.invoke(currentSession)
ToolbarMenu.Item.Reload -> sessionUseCases.reload.invoke(currentSession)
ToolbarMenu.Item.Stop -> sessionUseCases.stopLoading.invoke(currentSession)
ToolbarMenu.Item.Settings -> adjustBackgroundAndNavigate.invoke(
BrowserFragmentDirections.actionBrowserFragmentToSettingsFragment()
)
ToolbarMenu.Item.Library -> adjustBackgroundAndNavigate.invoke(
BrowserFragmentDirections.actionBrowserFragmentToLibraryFragment()
)
is ToolbarMenu.Item.RequestDesktop -> sessionUseCases.requestDesktopSite.invoke(
item.isChecked,
currentSession
)
ToolbarMenu.Item.AddToHomeScreen -> {
MainScope().launch {
with(activity.components.useCases.webAppUseCases) {
if (isInstallable()) {
addToHomescreen()
} else {
val directions =
BrowserFragmentDirections.actionBrowserFragmentToCreateShortcutFragment()
navController.navigate(directions)
}
}
}
}
ToolbarMenu.Item.Share -> {
val currentUrl = currentSession?.url
currentUrl?.apply {
val directions = NavGraphDirections.actionGlobalShareFragment(this)
navController.navigate(directions)
}
}
2019-08-07 22:02:08 +02:00
ToolbarMenu.Item.NewTab -> {
val directions = BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
sessionId = null
)
adjustBackgroundAndNavigate.invoke(directions)
2019-08-07 22:02:08 +02:00
browsingModeManager.mode = BrowsingMode.Normal
}
ToolbarMenu.Item.NewPrivateTab -> {
val directions = BrowserFragmentDirections.actionBrowserFragmentToSearchFragment(
sessionId = null
)
adjustBackgroundAndNavigate.invoke(directions)
2019-08-07 22:02:08 +02:00
browsingModeManager.mode = BrowsingMode.Private
}
ToolbarMenu.Item.FindInPage -> {
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
findInPageLauncher()
activity.components.analytics.metrics.track(Event.FindInPageOpened)
}
ToolbarMenu.Item.ReportIssue -> {
val currentUrl = currentSession?.url
currentUrl?.apply {
val reportUrl = String.format(BrowserFragment.REPORT_SITE_ISSUE_URL, this)
activity.components.useCases.tabsUseCases.addTab.invoke(reportUrl)
}
}
ToolbarMenu.Item.Help -> {
activity.components.useCases.tabsUseCases.addTab.invoke(getSupportUrl())
}
ToolbarMenu.Item.SaveToCollection -> {
activity.components.analytics.metrics
.track(Event.CollectionSaveButtonPressed(TELEMETRY_BROWSER_IDENTIFIER))
#4596 migrate collections (#5911) * For #4596: move code from CollectionCreationComponent to CollectionCreationStore Other than adding comments, no changes were made. The code will be updated in a following commit. This is in order to make the commit diff more readable. * For 4596: update CollectionCreateStore to libstate * For 4596: copied CollectionCreationUIView into CollectionCreationView Otherwise, no code was changed. The next commit will update this code. This is in order to make the commit diff more readable. * For 4596: update CollectionCreationView to LibState Note that the minimal changes possible to enable migration were made. Refactoring will happen in a later commit. * For 4596: updated CollectionCreationTabListAdapter to work with the new View * For 4596: updated SaveCollectionListAdapter to work with the new View * For 4596: implemented CollectionCreationController For now, it has an identical interface to the interactor. In a later commit several of its responsibilities will be moved around, some to the interactor and some to the reducer * For 4596: copied over previous reducer code No other changes were made. The code will be updated in the following commit. This is done to make changes more readable for the reviewer * For 4596: update reducer code param names Otherwise, no changes at this time * For 4596: add arguments to CreateCollectionFragment in nav_graph These will be used to replace the current CreateCollectionViewModel, which shares data between fragments in a way that doesn't fit within our architecture. * For 4596: pass arguments to collection via transaction instead of VM The VM will be removed in a later commit * For 4596: update BrowserToolbarController to share state to collection via its Direction * For 4596: removed CreateCollectionViewModel * For 4596: test tab retrieval in CreateCollectionFragment * For 4596: fix crashing CreateCollectionFragmentTest * For 4596: removed classes create collection classes used by old architecture * For 4596: collection interactor rename + kdoc * For 4596: moved collection interactor interface * For 4596: renamed CreateCollectionFragment All related classes followed the pattern of CollectionCreationX * For 4596: kdoc CollectionCreationController There's no effective difference between these calls and their interactor equivalent, so I linked to them * For 4596: fix bug that caused rename to not work * For 4596: removed unused collection actions These were unused before the LibState refactor * For 4596: kdoc StepChanged * For 4596: removed todos about moving logic to the reducer saveTabsToCollection: this could be moved, but that would involve creating a new action. SaveCollectionStep should probably be refactored out, so adding this layer of indirection seemed counterproductive handleBackPress: needs to be able to call dismiss(). The reducer doesn't (and shouldn't) be able to do that, so this needs to live here stepBack: called by handleBackPress. See above * For 4596: wrote tests for CollectionCreationController#stepback * For 4596: fixed tests broken by changes to collections * For 4596: small readability refactor for CollectionController#stepBack No change to functionality (see tests) * For 4596: broke apart CollectionView#update There's probably a lot more that could be done here, but smaller changes were made to reduce scope * For 4596: remove unnecessary todos It looks like we don't follow the suggested pattern in this project * For 4596: test CollectionCreationController#normalSessionSize * For 4596: updated naming in CollectionCreationController per review
2019-10-23 02:33:54 +02:00
currentSession?.let { currentSession ->
val directions = BrowserFragmentDirections.actionBrowserFragmentToCreateCollectionFragment(
previousFragmentId = R.id.browserFragment,
tabIds = arrayOf(currentSession.id),
selectedTabIds = arrayOf(currentSession.id),
saveCollectionStep = if (tabCollectionStorage.cachedTabCollections.isEmpty()) {
SaveCollectionStep.NameCollection
} else {
SaveCollectionStep.SelectCollection
}
)
navController.nav(R.id.browserFragment, directions)
}
}
ToolbarMenu.Item.OpenInFenix -> {
// Release the session from this view so that it can immediately be rendered by a different view
engineView.release()
// Strip the CustomTabConfig to turn this Session into a regular tab and then select it
customTabSession!!.customTabConfig = null
activity.components.core.sessionManager.select(customTabSession)
// Switch to the actual browser which should now display our new selected session
activity.startActivity(openInFenixIntent)
// Close this activity since it is no longer displaying any session
activity.finish()
}
ToolbarMenu.Item.Quit -> deleteAndQuit(activity, scope, snackbar)
}
}
private fun animateTabAndNavigateHome() {
// We need to dynamically add the options here because if you do it in XML it overwrites
val options = NavOptions.Builder().setPopUpTo(R.id.nav_graph, false)
.setEnterAnim(R.anim.fade_in).build()
val extras = FragmentNavigator.Extras.Builder().addSharedElement(
browserLayout,
"${TAB_ITEM_TRANSITION_NAME}${currentSession?.id}"
).build()
swipeRefresh.background = ColorDrawable(Color.TRANSPARENT)
engineView.asView().visibility = View.GONE
if (!navController.popBackStack(R.id.homeFragment, false)) {
navController.nav(
R.id.browserFragment,
R.id.action_browserFragment_to_homeFragment,
null,
options,
extras
)
}
}
@SuppressWarnings("ComplexMethod")
private fun trackToolbarItemInteraction(item: ToolbarMenu.Item) {
val eventItem = when (item) {
ToolbarMenu.Item.Back -> Event.BrowserMenuItemTapped.Item.BACK
ToolbarMenu.Item.Forward -> Event.BrowserMenuItemTapped.Item.FORWARD
ToolbarMenu.Item.Reload -> Event.BrowserMenuItemTapped.Item.RELOAD
ToolbarMenu.Item.Stop -> Event.BrowserMenuItemTapped.Item.STOP
ToolbarMenu.Item.Settings -> Event.BrowserMenuItemTapped.Item.SETTINGS
ToolbarMenu.Item.Library -> Event.BrowserMenuItemTapped.Item.LIBRARY
is ToolbarMenu.Item.RequestDesktop ->
if (item.isChecked) {
Event.BrowserMenuItemTapped.Item.DESKTOP_VIEW_ON
} else {
Event.BrowserMenuItemTapped.Item.DESKTOP_VIEW_OFF
}
ToolbarMenu.Item.NewPrivateTab -> Event.BrowserMenuItemTapped.Item.NEW_PRIVATE_TAB
ToolbarMenu.Item.FindInPage -> Event.BrowserMenuItemTapped.Item.FIND_IN_PAGE
ToolbarMenu.Item.ReportIssue -> Event.BrowserMenuItemTapped.Item.REPORT_SITE_ISSUE
ToolbarMenu.Item.Help -> Event.BrowserMenuItemTapped.Item.HELP
ToolbarMenu.Item.NewTab -> Event.BrowserMenuItemTapped.Item.NEW_TAB
ToolbarMenu.Item.OpenInFenix -> Event.BrowserMenuItemTapped.Item.OPEN_IN_FENIX
ToolbarMenu.Item.Share -> Event.BrowserMenuItemTapped.Item.SHARE
ToolbarMenu.Item.SaveToCollection -> Event.BrowserMenuItemTapped.Item.SAVE_TO_COLLECTION
ToolbarMenu.Item.AddToHomeScreen -> Event.BrowserMenuItemTapped.Item.ADD_TO_HOMESCREEN
ToolbarMenu.Item.Quit -> Event.BrowserMenuItemTapped.Item.QUIT
}
activity.components.analytics.metrics.track(Event.BrowserMenuItemTapped(eventItem))
}
companion object {
@VisibleForTesting
const val TAB_ITEM_TRANSITION_NAME = "tab_item"
internal const val TELEMETRY_BROWSER_IDENTIFIER = "browserMenu"
}
}