Skip to content

Commit

Permalink
Use "undo" implementation from Android Components.
Browse files Browse the repository at this point in the history
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:
mozilla-mobile/android-components#8449
  • Loading branch information
pocmo authored and ekager committed Sep 28, 2020
1 parent d287e6e commit 3983c50
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 = { }
)
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/java/org/mozilla/fenix/components/Core.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

/**
Expand Down Expand Up @@ -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)
}
Expand Down
67 changes: 25 additions & 42 deletions app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -493,46 +485,37 @@ class HomeFragment : Fragment() {
snackbarMessage,
requireContext().getString(R.string.snackbar_deleted_undo),
{
sessionManager.restore(snapshot)
requireComponents.useCases.tabsUseCases.undo.invoke()
},
operation = { },
anchorView = snackbarAnchorView
)
}

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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -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 {
Expand All @@ -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,
Expand Down
21 changes: 14 additions & 7 deletions app/src/main/java/org/mozilla/fenix/utils/Undo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 3983c50

Please sign in to comment.