Use "undo" implementation from Android Components.

This is not the super fancy version yet - since we still need to restore into SessionManager and
haven't fully switched to BrowserStore yet. However AC having knowledge about "undo" and whether
it was performed or not, will help us with features like "recently closed tabs". And once we
can improve "undo", Fenix will get all the nice things automatically.

Requires:
https://github.com/mozilla-mobile/android-components/pull/8449
pull/184/head
Sebastian Kaspari 4 years ago committed by ekager
parent d287e6e9e0
commit 3983c509dc

@ -260,9 +260,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
)
},
onCloseTab = { closedSession ->
val tab = store.state.findTab(closedSession.id)
?: return@DefaultBrowserToolbarController
val isSelected = tab.id == context.components.core.store.state.selectedTabId
val tab = store.state.findTab(closedSession.id) ?: return@DefaultBrowserToolbarController
val snackbarMessage = if (tab.content.private) {
requireContext().getString(R.string.snackbar_private_tab_closed)
@ -275,11 +273,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
snackbarMessage,
requireContext().getString(R.string.snackbar_deleted_undo),
{
sessionManager.add(
closedSession,
isSelected,
engineSessionState = tab.engineState.engineSessionState
)
requireComponents.useCases.tabsUseCases.undo.invoke()
},
operation = { }
)

@ -20,6 +20,7 @@ import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.session.engine.EngineMiddleware
import mozilla.components.browser.session.storage.SessionStorage
import mozilla.components.browser.session.undo.UndoMiddleware
import mozilla.components.browser.state.action.RecentlyClosedAction
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.store.BrowserStore
@ -69,6 +70,7 @@ import org.mozilla.fenix.search.telemetry.incontent.InContentTelemetry
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.settings.advanced.getSelectedLocale
import org.mozilla.fenix.utils.Mockable
import org.mozilla.fenix.utils.getUndoDelay
import java.util.concurrent.TimeUnit
/**
@ -146,13 +148,18 @@ class Core(private val context: Context, private val crashReporter: CrashReporti
MediaMiddleware(context, MediaService::class.java),
DownloadMiddleware(context, DownloadService::class.java),
ReaderViewMiddleware(),
ThumbnailsMiddleware(thumbnailStorage)
ThumbnailsMiddleware(thumbnailStorage),
UndoMiddleware(::lookupSessionManager, context.getUndoDelay())
) + EngineMiddleware.create(engine, ::findSessionById)
).also {
it.dispatch(RecentlyClosedAction.InitializeRecentlyClosedState)
}
}
private fun lookupSessionManager(): SessionManager {
return sessionManager
}
private fun findSessionById(tabId: String): Session? {
return sessionManager.findSessionById(tabId)
}

@ -92,7 +92,6 @@ import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.sessionsOfType
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
@ -469,17 +468,10 @@ class HomeFragment : Fragment() {
}
private fun removeAllTabsAndShowSnackbar(sessionCode: String) {
val tabs = sessionManager.sessionsOfType(private = sessionCode == ALL_PRIVATE_TABS).toList()
val selectedIndex = sessionManager
.selectedSession?.let { sessionManager.sessions.indexOf(it) }
?: SessionManager.NO_SELECTION
val snapshot = tabs
.map(sessionManager::createSessionSnapshot)
.let { SessionManager.Snapshot(it, selectedIndex) }
tabs.forEach {
requireComponents.useCases.tabsUseCases.removeTab(it)
if (sessionCode == ALL_PRIVATE_TABS) {
sessionManager.removePrivateSessions()
} else {
sessionManager.removeNormalSessions()
}
val snackbarMessage = if (sessionCode == ALL_PRIVATE_TABS) {
@ -493,7 +485,7 @@ class HomeFragment : Fragment() {
snackbarMessage,
requireContext().getString(R.string.snackbar_deleted_undo),
{
sessionManager.restore(snapshot)
requireComponents.useCases.tabsUseCases.undo.invoke()
},
operation = { },
anchorView = snackbarAnchorView
@ -501,38 +493,29 @@ class HomeFragment : Fragment() {
}
private fun removeTabAndShowSnackbar(sessionId: String) {
sessionManager.findSessionById(sessionId)?.let { session ->
val snapshot = sessionManager.createSessionSnapshot(session)
val state = store.state.findTab(sessionId)?.engineState?.engineSessionState
val isSelected =
session.id == requireComponents.core.store.state.selectedTabId ?: false
val tab = store.state.findTab(sessionId) ?: return
requireComponents.useCases.tabsUseCases.removeTab(sessionId)
val snackbarMessage = if (snapshot.session.private) {
requireContext().getString(R.string.snackbar_private_tab_closed)
} else {
requireContext().getString(R.string.snackbar_tab_closed)
}
requireComponents.useCases.tabsUseCases.removeTab(sessionId)
viewLifecycleOwner.lifecycleScope.allowUndo(
requireView(),
snackbarMessage,
requireContext().getString(R.string.snackbar_deleted_undo),
{
sessionManager.add(
snapshot.session,
isSelected,
engineSessionState = state
)
findNavController().navigate(
HomeFragmentDirections.actionGlobalBrowser(null)
)
},
operation = { },
anchorView = snackbarAnchorView
)
val snackbarMessage = if (tab.content.private) {
requireContext().getString(R.string.snackbar_private_tab_closed)
} else {
requireContext().getString(R.string.snackbar_tab_closed)
}
viewLifecycleOwner.lifecycleScope.allowUndo(
requireView(),
snackbarMessage,
requireContext().getString(R.string.snackbar_deleted_undo),
{
requireComponents.useCases.tabsUseCases.undo.invoke()
findNavController().navigate(
HomeFragmentDirections.actionGlobalBrowser(null)
)
},
operation = { },
anchorView = snackbarAnchorView
)
}
override fun onDestroyView() {

@ -260,10 +260,7 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
private fun showUndoSnackbarForTab(sessionId: String) {
val store = requireComponents.core.store
val sessionManager = requireComponents.core.sessionManager
val tab = requireComponents.core.store.state.findTab(sessionId) ?: return
val session = sessionManager.findSessionById(sessionId) ?: return
// Check if this is the last tab of this session type
val isLastOpenTab =
@ -273,8 +270,6 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
return
}
val isSelected = sessionId == requireComponents.core.store.state.selectedTabId ?: false
val snackbarMessage = if (tab.content.private) {
getString(R.string.snackbar_private_tab_closed)
} else {
@ -286,12 +281,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler
snackbarMessage,
getString(R.string.snackbar_deleted_undo),
{
sessionManager.add(
session,
isSelected,
engineSessionState = tab.engineState.engineSessionState
)
_tabTrayView?.scrollToTab(session.id)
requireComponents.useCases.tabsUseCases.undo.invoke()
_tabTrayView?.scrollToTab(tab.id)
},
operation = { },
elevation = ELEVATION,

@ -4,6 +4,7 @@
package org.mozilla.fenix.utils
import android.content.Context
import android.view.View
import androidx.appcompat.widget.ContentFrameLayout
import androidx.core.view.updatePadding
@ -19,6 +20,18 @@ import java.util.concurrent.atomic.AtomicBoolean
internal const val UNDO_DELAY = 3000L
internal const val ACCESSIBLE_UNDO_DELAY = 15000L
/**
* Get the recommended time an "undo" action should be available until it can automatically be
* dismissed. The delay may be different based on the accessibility settings of the device.
*/
fun Context.getUndoDelay(): Long {
return if (settings().accessibilityServicesEnabled) {
ACCESSIBLE_UNDO_DELAY
} else {
UNDO_DELAY
}
}
/**
* Runs [operation] after giving user time (see [UNDO_DELAY]) to cancel it.
* In case of cancellation, [onCancel] is executed.
@ -92,13 +105,7 @@ fun CoroutineScope.allowUndo(
// Wait a bit, and if user didn't request cancellation, proceed with
// requested operation and hide the snackbar.
launch {
val lengthToDelay = if (view.context.settings().accessibilityServicesEnabled) {
ACCESSIBLE_UNDO_DELAY
} else {
UNDO_DELAY
}
delay(lengthToDelay)
delay(view.context.getUndoDelay())
if (!requestedUndo.get()) {
snackbar.dismiss()

Loading…
Cancel
Save