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

@ -20,6 +20,7 @@ import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.session.engine.EngineMiddleware import mozilla.components.browser.session.engine.EngineMiddleware
import mozilla.components.browser.session.storage.SessionStorage 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.action.RecentlyClosedAction
import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.store.BrowserStore 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.SupportUtils
import org.mozilla.fenix.settings.advanced.getSelectedLocale import org.mozilla.fenix.settings.advanced.getSelectedLocale
import org.mozilla.fenix.utils.Mockable import org.mozilla.fenix.utils.Mockable
import org.mozilla.fenix.utils.getUndoDelay
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
/** /**
@ -146,13 +148,18 @@ class Core(private val context: Context, private val crashReporter: CrashReporti
MediaMiddleware(context, MediaService::class.java), MediaMiddleware(context, MediaService::class.java),
DownloadMiddleware(context, DownloadService::class.java), DownloadMiddleware(context, DownloadService::class.java),
ReaderViewMiddleware(), ReaderViewMiddleware(),
ThumbnailsMiddleware(thumbnailStorage) ThumbnailsMiddleware(thumbnailStorage),
UndoMiddleware(::lookupSessionManager, context.getUndoDelay())
) + EngineMiddleware.create(engine, ::findSessionById) ) + EngineMiddleware.create(engine, ::findSessionById)
).also { ).also {
it.dispatch(RecentlyClosedAction.InitializeRecentlyClosedState) it.dispatch(RecentlyClosedAction.InitializeRecentlyClosedState)
} }
} }
private fun lookupSessionManager(): SessionManager {
return sessionManager
}
private fun findSessionById(tabId: String): Session? { private fun findSessionById(tabId: String): Session? {
return sessionManager.findSessionById(tabId) 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.nav
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.resetPoliciesAfter import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.sessionsOfType
import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
@ -469,17 +468,10 @@ class HomeFragment : Fragment() {
} }
private fun removeAllTabsAndShowSnackbar(sessionCode: String) { private fun removeAllTabsAndShowSnackbar(sessionCode: String) {
val tabs = sessionManager.sessionsOfType(private = sessionCode == ALL_PRIVATE_TABS).toList() if (sessionCode == ALL_PRIVATE_TABS) {
val selectedIndex = sessionManager sessionManager.removePrivateSessions()
.selectedSession?.let { sessionManager.sessions.indexOf(it) } } else {
?: SessionManager.NO_SELECTION sessionManager.removeNormalSessions()
val snapshot = tabs
.map(sessionManager::createSessionSnapshot)
.let { SessionManager.Snapshot(it, selectedIndex) }
tabs.forEach {
requireComponents.useCases.tabsUseCases.removeTab(it)
} }
val snackbarMessage = if (sessionCode == ALL_PRIVATE_TABS) { val snackbarMessage = if (sessionCode == ALL_PRIVATE_TABS) {
@ -493,7 +485,7 @@ class HomeFragment : Fragment() {
snackbarMessage, snackbarMessage,
requireContext().getString(R.string.snackbar_deleted_undo), requireContext().getString(R.string.snackbar_deleted_undo),
{ {
sessionManager.restore(snapshot) requireComponents.useCases.tabsUseCases.undo.invoke()
}, },
operation = { }, operation = { },
anchorView = snackbarAnchorView anchorView = snackbarAnchorView
@ -501,15 +493,11 @@ class HomeFragment : Fragment() {
} }
private fun removeTabAndShowSnackbar(sessionId: String) { private fun removeTabAndShowSnackbar(sessionId: String) {
sessionManager.findSessionById(sessionId)?.let { session -> val tab = store.state.findTab(sessionId) ?: return
val snapshot = sessionManager.createSessionSnapshot(session)
val state = store.state.findTab(sessionId)?.engineState?.engineSessionState
val isSelected =
session.id == requireComponents.core.store.state.selectedTabId ?: false
requireComponents.useCases.tabsUseCases.removeTab(sessionId) requireComponents.useCases.tabsUseCases.removeTab(sessionId)
val snackbarMessage = if (snapshot.session.private) { val snackbarMessage = if (tab.content.private) {
requireContext().getString(R.string.snackbar_private_tab_closed) requireContext().getString(R.string.snackbar_private_tab_closed)
} else { } else {
requireContext().getString(R.string.snackbar_tab_closed) requireContext().getString(R.string.snackbar_tab_closed)
@ -520,11 +508,7 @@ class HomeFragment : Fragment() {
snackbarMessage, snackbarMessage,
requireContext().getString(R.string.snackbar_deleted_undo), requireContext().getString(R.string.snackbar_deleted_undo),
{ {
sessionManager.add( requireComponents.useCases.tabsUseCases.undo.invoke()
snapshot.session,
isSelected,
engineSessionState = state
)
findNavController().navigate( findNavController().navigate(
HomeFragmentDirections.actionGlobalBrowser(null) HomeFragmentDirections.actionGlobalBrowser(null)
) )
@ -533,7 +517,6 @@ class HomeFragment : Fragment() {
anchorView = snackbarAnchorView anchorView = snackbarAnchorView
) )
} }
}
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()

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

@ -4,6 +4,7 @@
package org.mozilla.fenix.utils package org.mozilla.fenix.utils
import android.content.Context
import android.view.View import android.view.View
import androidx.appcompat.widget.ContentFrameLayout import androidx.appcompat.widget.ContentFrameLayout
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
@ -19,6 +20,18 @@ import java.util.concurrent.atomic.AtomicBoolean
internal const val UNDO_DELAY = 3000L internal const val UNDO_DELAY = 3000L
internal const val ACCESSIBLE_UNDO_DELAY = 15000L 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. * Runs [operation] after giving user time (see [UNDO_DELAY]) to cancel it.
* In case of cancellation, [onCancel] is executed. * 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 // Wait a bit, and if user didn't request cancellation, proceed with
// requested operation and hide the snackbar. // requested operation and hide the snackbar.
launch { launch {
val lengthToDelay = if (view.context.settings().accessibilityServicesEnabled) { delay(view.context.getUndoDelay())
ACCESSIBLE_UNDO_DELAY
} else {
UNDO_DELAY
}
delay(lengthToDelay)
if (!requestedUndo.get()) { if (!requestedUndo.get()) {
snackbar.dismiss() snackbar.dismiss()

Loading…
Cancel
Save