2020-05-21 04:57:47 +02:00
|
|
|
/* 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.tabtray
|
|
|
|
|
2020-05-31 02:27:59 +02:00
|
|
|
import android.content.res.Configuration
|
2020-05-21 05:20:29 +02:00
|
|
|
import android.os.Bundle
|
|
|
|
import android.view.LayoutInflater
|
|
|
|
import android.view.View
|
|
|
|
import android.view.ViewGroup
|
2020-05-21 04:57:47 +02:00
|
|
|
import androidx.appcompat.app.AppCompatDialogFragment
|
2020-05-21 05:20:29 +02:00
|
|
|
import androidx.core.view.updatePadding
|
2020-06-02 22:03:58 +02:00
|
|
|
import androidx.fragment.app.FragmentManager
|
2020-05-26 19:10:45 +02:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2020-05-30 00:17:56 +02:00
|
|
|
import androidx.navigation.fragment.findNavController
|
2020-05-21 06:18:53 +02:00
|
|
|
import kotlinx.android.synthetic.main.component_tabstray.view.*
|
2020-05-21 05:47:07 +02:00
|
|
|
import kotlinx.android.synthetic.main.fragment_tab_tray_dialog.*
|
2020-05-21 05:20:29 +02:00
|
|
|
import kotlinx.android.synthetic.main.fragment_tab_tray_dialog.view.*
|
2020-05-30 00:17:56 +02:00
|
|
|
import mozilla.components.browser.session.Session
|
|
|
|
import mozilla.components.browser.session.SessionManager
|
2020-06-02 22:03:58 +02:00
|
|
|
import mozilla.components.browser.state.selector.normalTabs
|
|
|
|
import mozilla.components.browser.state.selector.privateTabs
|
|
|
|
import mozilla.components.browser.state.state.BrowserState
|
2020-06-08 02:03:06 +02:00
|
|
|
import mozilla.components.feature.tab.collections.TabCollection
|
2020-06-09 23:14:45 +02:00
|
|
|
import mozilla.components.feature.tabs.TabsUseCases
|
2020-06-17 05:10:06 +02:00
|
|
|
import mozilla.components.feature.tabs.tabstray.TabsFeature
|
2020-05-27 20:07:03 +02:00
|
|
|
import mozilla.components.lib.state.ext.consumeFrom
|
2020-06-09 23:14:45 +02:00
|
|
|
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
|
2020-05-21 07:22:31 +02:00
|
|
|
import org.mozilla.fenix.HomeActivity
|
2020-05-21 05:20:29 +02:00
|
|
|
import org.mozilla.fenix.R
|
2020-06-08 02:03:06 +02:00
|
|
|
import org.mozilla.fenix.components.FenixSnackbar
|
2020-06-17 05:10:06 +02:00
|
|
|
import org.mozilla.fenix.components.TabCollectionStorage
|
2020-05-26 19:10:45 +02:00
|
|
|
import org.mozilla.fenix.ext.components
|
|
|
|
import org.mozilla.fenix.ext.requireComponents
|
|
|
|
import org.mozilla.fenix.utils.allowUndo
|
2020-05-21 04:57:47 +02:00
|
|
|
|
2020-06-09 18:08:21 +02:00
|
|
|
@SuppressWarnings("TooManyFunctions", "LargeClass")
|
2020-06-17 05:10:06 +02:00
|
|
|
class TabTrayDialogFragment : AppCompatDialogFragment() {
|
2020-06-09 23:14:45 +02:00
|
|
|
private val tabsFeature = ViewBoundFeatureWrapper<TabsFeature>()
|
2020-06-03 08:03:20 +02:00
|
|
|
private var _tabTrayView: TabTrayView? = null
|
|
|
|
private val tabTrayView: TabTrayView
|
|
|
|
get() = _tabTrayView!!
|
2020-05-21 05:47:07 +02:00
|
|
|
|
2020-06-08 02:03:06 +02:00
|
|
|
private val collectionStorageObserver = object : TabCollectionStorage.Observer {
|
|
|
|
override fun onCollectionCreated(title: String, sessions: List<Session>) {
|
|
|
|
showCollectionSnackbar()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onTabsAdded(tabCollection: TabCollection, sessions: List<Session>) {
|
|
|
|
showCollectionSnackbar()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-09 23:14:45 +02:00
|
|
|
private val selectTabUseCase = object : TabsUseCases.SelectTabUseCase {
|
|
|
|
override fun invoke(tabId: String) {
|
|
|
|
requireComponents.useCases.tabsUseCases.selectTab(tabId)
|
|
|
|
navigateToBrowser()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun invoke(session: Session) {
|
|
|
|
requireComponents.useCases.tabsUseCases.selectTab(session)
|
|
|
|
navigateToBrowser()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private val removeTabUseCase = object : TabsUseCases.RemoveTabUseCase {
|
|
|
|
override fun invoke(sessionId: String) {
|
|
|
|
showUndoSnackbarForTab(sessionId)
|
|
|
|
requireComponents.useCases.tabsUseCases.removeTab(sessionId)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun invoke(session: Session) {
|
|
|
|
showUndoSnackbarForTab(session.id)
|
|
|
|
requireComponents.useCases.tabsUseCases.removeTab(session)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-21 05:20:29 +02:00
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
setStyle(STYLE_NO_TITLE, R.style.TabTrayDialogStyle)
|
|
|
|
}
|
2020-05-21 04:57:47 +02:00
|
|
|
|
2020-05-21 05:20:29 +02:00
|
|
|
override fun onCreateView(
|
|
|
|
inflater: LayoutInflater,
|
|
|
|
container: ViewGroup?,
|
|
|
|
savedInstanceState: Bundle?
|
|
|
|
): View? = inflater.inflate(R.layout.fragment_tab_tray_dialog, container, false)
|
|
|
|
|
2020-05-31 02:27:59 +02:00
|
|
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
|
|
|
super.onConfigurationChanged(newConfig)
|
|
|
|
|
2020-06-08 22:21:56 +02:00
|
|
|
val isLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
|
|
|
|
tabTrayView.setTopOffset(isLandscape)
|
|
|
|
|
|
|
|
if (isLandscape) {
|
2020-06-23 17:50:20 +02:00
|
|
|
tabTrayView.dismissMenu()
|
2020-05-31 02:27:59 +02:00
|
|
|
tabTrayView.expand()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-21 05:20:29 +02:00
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
|
|
super.onViewCreated(view, savedInstanceState)
|
2020-06-03 08:03:20 +02:00
|
|
|
val isPrivate = (activity as HomeActivity).browsingModeManager.mode.isPrivate
|
|
|
|
|
|
|
|
_tabTrayView = TabTrayView(
|
2020-05-21 07:22:31 +02:00
|
|
|
view.tabLayout,
|
2020-06-17 05:10:06 +02:00
|
|
|
interactor = TabTrayFragmentInteractor(
|
|
|
|
DefaultTabTrayController(
|
|
|
|
activity = (activity as HomeActivity),
|
|
|
|
navController = findNavController(),
|
|
|
|
dismissTabTray = ::dismissAllowingStateLoss,
|
|
|
|
showUndoSnackbar = ::showUndoSnackbar,
|
|
|
|
registerCollectionStorageObserver = ::registerCollectionStorageObserver
|
|
|
|
)
|
|
|
|
),
|
|
|
|
isPrivate = isPrivate,
|
|
|
|
startingInLandscape = requireContext().resources.configuration.orientation ==
|
|
|
|
Configuration.ORIENTATION_LANDSCAPE,
|
|
|
|
lifecycleScope = viewLifecycleOwner.lifecycleScope
|
2020-06-09 23:14:45 +02:00
|
|
|
) { tabsFeature.get()?.filterTabs(it) }
|
2020-06-03 08:03:20 +02:00
|
|
|
|
2020-06-09 23:14:45 +02:00
|
|
|
tabsFeature.set(
|
2020-06-03 08:03:20 +02:00
|
|
|
TabsFeature(
|
|
|
|
tabTrayView.view.tabsTray,
|
|
|
|
view.context.components.core.store,
|
2020-06-09 23:14:45 +02:00
|
|
|
selectTabUseCase,
|
|
|
|
removeTabUseCase,
|
2020-06-03 08:03:20 +02:00
|
|
|
{ it.content.private == isPrivate },
|
|
|
|
{ }
|
2020-06-09 23:14:45 +02:00
|
|
|
),
|
|
|
|
owner = viewLifecycleOwner,
|
|
|
|
view = view
|
|
|
|
)
|
2020-05-21 05:47:07 +02:00
|
|
|
|
2020-05-27 20:07:03 +02:00
|
|
|
tabLayout.setOnClickListener {
|
|
|
|
dismissAllowingStateLoss()
|
|
|
|
}
|
2020-05-21 05:47:07 +02:00
|
|
|
|
2020-05-21 05:48:57 +02:00
|
|
|
view.tabLayout.setOnApplyWindowInsetsListener { v, insets ->
|
|
|
|
v.updatePadding(
|
|
|
|
left = insets.systemWindowInsetLeft,
|
2020-05-21 06:18:53 +02:00
|
|
|
right = insets.systemWindowInsetRight,
|
|
|
|
bottom = insets.systemWindowInsetBottom
|
2020-05-21 05:48:57 +02:00
|
|
|
)
|
2020-05-21 06:18:53 +02:00
|
|
|
|
|
|
|
tabTrayView.view.tab_wrapper.updatePadding(
|
|
|
|
bottom = insets.systemWindowInsetBottom
|
|
|
|
)
|
|
|
|
|
2020-05-21 05:48:57 +02:00
|
|
|
insets
|
|
|
|
}
|
2020-05-27 20:07:03 +02:00
|
|
|
|
2020-06-02 22:03:58 +02:00
|
|
|
consumeFrom(requireComponents.core.store) {
|
|
|
|
tabTrayView.updateState(it)
|
|
|
|
navigateHomeIfNeeded(it)
|
|
|
|
}
|
2020-05-21 05:47:07 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 23:14:45 +02:00
|
|
|
private fun showUndoSnackbarForTab(sessionId: String) {
|
2020-05-26 19:10:45 +02:00
|
|
|
val sessionManager = view?.context?.components?.core?.sessionManager
|
|
|
|
val snapshot = sessionManager
|
2020-06-09 23:14:45 +02:00
|
|
|
?.findSessionById(sessionId)?.let {
|
2020-05-26 19:10:45 +02:00
|
|
|
sessionManager.createSessionSnapshot(it)
|
|
|
|
} ?: return
|
|
|
|
|
|
|
|
val state = snapshot.engineSession?.saveState()
|
2020-06-09 23:14:45 +02:00
|
|
|
val isSelected = sessionId == requireComponents.core.store.state.selectedTabId ?: false
|
2020-05-26 19:10:45 +02:00
|
|
|
|
|
|
|
val snackbarMessage = if (snapshot.session.private) {
|
|
|
|
getString(R.string.snackbar_private_tab_closed)
|
|
|
|
} else {
|
|
|
|
getString(R.string.snackbar_tab_closed)
|
|
|
|
}
|
|
|
|
|
2020-05-26 21:06:29 +02:00
|
|
|
view?.tabLayout?.let {
|
|
|
|
viewLifecycleOwner.lifecycleScope.allowUndo(
|
|
|
|
it,
|
|
|
|
snackbarMessage,
|
|
|
|
getString(R.string.snackbar_deleted_undo),
|
|
|
|
{
|
|
|
|
sessionManager.add(snapshot.session, isSelected, engineSessionState = state)
|
|
|
|
},
|
|
|
|
operation = { },
|
|
|
|
elevation = ELEVATION
|
|
|
|
)
|
|
|
|
}
|
2020-05-23 04:41:49 +02:00
|
|
|
}
|
|
|
|
|
2020-06-09 23:14:45 +02:00
|
|
|
override fun onDestroyView() {
|
|
|
|
_tabTrayView = null
|
|
|
|
super.onDestroyView()
|
|
|
|
}
|
|
|
|
|
|
|
|
fun navigateToBrowser() {
|
2020-05-30 00:17:56 +02:00
|
|
|
dismissAllowingStateLoss()
|
|
|
|
if (findNavController().currentDestination?.id == R.id.browserFragment) return
|
|
|
|
if (!findNavController().popBackStack(R.id.browserFragment, false)) {
|
|
|
|
findNavController().navigate(R.id.browserFragment)
|
|
|
|
}
|
2020-05-21 05:47:07 +02:00
|
|
|
}
|
|
|
|
|
2020-06-02 22:03:58 +02:00
|
|
|
private fun navigateHomeIfNeeded(state: BrowserState) {
|
|
|
|
val shouldPop = if (tabTrayView.isPrivateModeSelected) {
|
|
|
|
state.privateTabs.isEmpty()
|
|
|
|
} else {
|
|
|
|
state.normalTabs.isEmpty()
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shouldPop) {
|
|
|
|
findNavController().popBackStack(R.id.homeFragment, false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-08 02:03:06 +02:00
|
|
|
private fun registerCollectionStorageObserver() {
|
|
|
|
requireComponents.core.tabCollectionStorage.register(collectionStorageObserver, this)
|
|
|
|
}
|
|
|
|
|
2020-06-17 05:10:06 +02:00
|
|
|
private fun showUndoSnackbar(snackbarMessage: String, snapshot: SessionManager.Snapshot) {
|
|
|
|
view?.let {
|
|
|
|
viewLifecycleOwner.lifecycleScope.allowUndo(
|
|
|
|
it,
|
|
|
|
snackbarMessage,
|
|
|
|
getString(R.string.snackbar_deleted_undo),
|
|
|
|
{
|
|
|
|
context?.components?.core?.sessionManager?.restore(snapshot)
|
|
|
|
},
|
|
|
|
operation = { },
|
|
|
|
elevation = ELEVATION
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-08 02:03:06 +02:00
|
|
|
private fun showCollectionSnackbar() {
|
|
|
|
view.let {
|
|
|
|
val snackbar = FenixSnackbar
|
|
|
|
.make(
|
|
|
|
duration = FenixSnackbar.LENGTH_LONG,
|
|
|
|
isDisplayedWithBrowserToolbar = true,
|
|
|
|
view = (view as View)
|
|
|
|
)
|
|
|
|
.setText(requireContext().getString(R.string.create_collection_tabs_saved))
|
|
|
|
.setAction(requireContext().getString(R.string.create_collection_view)) {
|
|
|
|
dismissAllowingStateLoss()
|
2020-06-13 00:55:07 +02:00
|
|
|
findNavController().navigate(
|
|
|
|
TabTrayDialogFragmentDirections.actionGlobalHome(focusOnAddressBar = false)
|
|
|
|
)
|
2020-06-08 02:03:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
snackbar.view.elevation = ELEVATION
|
|
|
|
snackbar.show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-26 21:06:29 +02:00
|
|
|
companion object {
|
|
|
|
private const val ELEVATION = 80f
|
2020-06-02 22:03:58 +02:00
|
|
|
private const val FRAGMENT_TAG = "tabTrayDialogFragment"
|
|
|
|
|
|
|
|
fun show(fragmentManager: FragmentManager) {
|
2020-06-08 23:41:19 +02:00
|
|
|
// If we've killed the fragmentManager. Let's not try to show the tabs tray.
|
2020-06-17 05:10:06 +02:00
|
|
|
if (fragmentManager.isDestroyed) {
|
|
|
|
return
|
|
|
|
}
|
2020-06-08 23:41:19 +02:00
|
|
|
|
2020-06-02 22:03:58 +02:00
|
|
|
// We want to make sure we don't accidentally show the dialog twice if
|
|
|
|
// a user somehow manages to trigger `show()` twice before we present the dialog.
|
|
|
|
if (fragmentManager.findFragmentByTag(FRAGMENT_TAG) == null) {
|
|
|
|
TabTrayDialogFragment().showNow(fragmentManager, FRAGMENT_TAG)
|
|
|
|
}
|
|
|
|
}
|
2020-05-26 21:06:29 +02:00
|
|
|
}
|
2020-05-21 21:58:26 +02:00
|
|
|
}
|