For #5843 - Ensure tabs pending deletion are removed
parent
13c9c39658
commit
7f34204188
|
@ -5,6 +5,7 @@
|
||||||
package org.mozilla.fenix.components
|
package org.mozilla.fenix.components
|
||||||
|
|
||||||
import GeckoProvider
|
import GeckoProvider
|
||||||
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import io.sentry.Sentry
|
import io.sentry.Sentry
|
||||||
|
@ -116,6 +117,11 @@ class Core(private val context: Context) {
|
||||||
*/
|
*/
|
||||||
val customTabsStore by lazy { CustomTabsServiceStore() }
|
val customTabsStore by lazy { CustomTabsServiceStore() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [PendingSessionDeletionManager] maintains a set of sessionIds that are marked for deletion
|
||||||
|
*/
|
||||||
|
val pendingSessionDeletionManager by lazy { PendingSessionDeletionManager(context as Application) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The session manager component provides access to a centralized registry of
|
* The session manager component provides access to a centralized registry of
|
||||||
* all browser sessions (i.e. tabs). It is initialized here to persist and restore
|
* all browser sessions (i.e. tabs). It is initialized here to persist and restore
|
||||||
|
@ -142,6 +148,12 @@ class Core(private val context: Context) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pendingSessionDeletionManager.getSessionsToDelete(context).forEach {
|
||||||
|
sessionManager.findSessionById(it)?.let { session ->
|
||||||
|
sessionManager.remove(session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Now that we have restored our previous state (if there's one) let's setup auto saving the state while
|
// Now that we have restored our previous state (if there's one) let's setup auto saving the state while
|
||||||
// the app is used.
|
// the app is used.
|
||||||
sessionStorage.autoSave(sessionManager)
|
sessionStorage.autoSave(sessionManager)
|
||||||
|
@ -277,13 +289,13 @@ class Core(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun geCustomCookiePolicy(): CookiePolicy {
|
private fun geCustomCookiePolicy(): CookiePolicy {
|
||||||
return when (context.settings().blockCookiesSelectionInCustomTrackingProtection) {
|
return when (context.settings().blockCookiesSelectionInCustomTrackingProtection) {
|
||||||
"all" -> CookiePolicy.ACCEPT_NONE
|
"all" -> CookiePolicy.ACCEPT_NONE
|
||||||
"social" -> CookiePolicy.ACCEPT_NON_TRACKERS
|
"social" -> CookiePolicy.ACCEPT_NON_TRACKERS
|
||||||
"unvisited" -> CookiePolicy.ACCEPT_VISITED
|
"unvisited" -> CookiePolicy.ACCEPT_VISITED
|
||||||
"third-party" -> CookiePolicy.ACCEPT_ONLY_FIRST_PARTY
|
"third-party" -> CookiePolicy.ACCEPT_ONLY_FIRST_PARTY
|
||||||
else -> CookiePolicy.ACCEPT_NONE
|
else -> CookiePolicy.ACCEPT_NONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCustomTrackingCategories(): Array<TrackingCategory> {
|
private fun getCustomTrackingCategories(): Array<TrackingCategory> {
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import org.mozilla.fenix.ext.settings
|
||||||
|
|
||||||
|
class PendingSessionDeletionManager(application: Application) :
|
||||||
|
Application.ActivityLifecycleCallbacks {
|
||||||
|
|
||||||
|
private val sessionIdsPendingDeletion = mutableSetOf<String>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
application.registerActivityLifecycleCallbacks(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addSession(sessionId: String) {
|
||||||
|
sessionIdsPendingDeletion.add(sessionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeSession(sessionId: String) {
|
||||||
|
sessionIdsPendingDeletion.remove(sessionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSessionsToDelete(context: Context): Set<String> {
|
||||||
|
return context.settings().preferences.getStringSet(
|
||||||
|
PREF_KEY,
|
||||||
|
setOf()
|
||||||
|
) ?: setOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityPaused(activity: Activity?) {
|
||||||
|
activity?.settings()?.preferences?.edit()?.putStringSet(
|
||||||
|
PREF_KEY,
|
||||||
|
sessionIdsPendingDeletion
|
||||||
|
)?.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResumed(p0: Activity?) {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityStarted(p0: Activity?) {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityDestroyed(p0: Activity?) {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivitySaveInstanceState(p0: Activity?, p1: Bundle?) {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityStopped(p0: Activity?) {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityCreated(p0: Activity?, p1: Bundle?) {
|
||||||
|
/* no-op */
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val PREF_KEY = "pref_key_session_id_set_to_delete"
|
||||||
|
}
|
||||||
|
}
|
|
@ -202,7 +202,7 @@ class HomeFragment : Fragment() {
|
||||||
registerCollectionStorageObserver = ::registerCollectionStorageObserver,
|
registerCollectionStorageObserver = ::registerCollectionStorageObserver,
|
||||||
scrollToTheTop = ::scrollToTheTop,
|
scrollToTheTop = ::scrollToTheTop,
|
||||||
showDeleteCollectionPrompt = ::showDeleteCollectionPrompt,
|
showDeleteCollectionPrompt = ::showDeleteCollectionPrompt,
|
||||||
openSettingsScreen = :: openSettingsScreen
|
openSettingsScreen = ::openSettingsScreen
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
updateLayout(view)
|
updateLayout(view)
|
||||||
|
@ -277,7 +277,8 @@ class HomeFragment : Fragment() {
|
||||||
// TODO remove when viewLifecycleOwner is fixed
|
// TODO remove when viewLifecycleOwner is fixed
|
||||||
val context = context ?: return@launch
|
val context = context ?: return@launch
|
||||||
|
|
||||||
val iconSize = context.resources.getDimensionPixelSize(R.dimen.preference_icon_drawable_size)
|
val iconSize =
|
||||||
|
context.resources.getDimensionPixelSize(R.dimen.preference_icon_drawable_size)
|
||||||
|
|
||||||
val searchEngine = context.components.search.provider.getDefaultEngine(context)
|
val searchEngine = context.components.search.provider.getDefaultEngine(context)
|
||||||
val searchIcon = BitmapDrawable(context.resources, searchEngine.icon)
|
val searchIcon = BitmapDrawable(context.resources, searchEngine.icon)
|
||||||
|
@ -336,7 +337,8 @@ class HomeFragment : Fragment() {
|
||||||
|
|
||||||
if (onboarding.userHasBeenOnboarded()) {
|
if (onboarding.userHasBeenOnboarded()) {
|
||||||
homeFragmentStore.dispatch(
|
homeFragmentStore.dispatch(
|
||||||
HomeFragmentAction.ModeChange(Mode.fromBrowsingMode(newMode)))
|
HomeFragmentAction.ModeChange(Mode.fromBrowsingMode(newMode))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,12 +357,14 @@ class HomeFragment : Fragment() {
|
||||||
val context = requireContext()
|
val context = requireContext()
|
||||||
val components = context.components
|
val components = context.components
|
||||||
|
|
||||||
homeFragmentStore.dispatch(HomeFragmentAction.Change(
|
homeFragmentStore.dispatch(
|
||||||
collections = components.core.tabCollectionStorage.cachedTabCollections,
|
HomeFragmentAction.Change(
|
||||||
mode = currentMode.getCurrentMode(),
|
collections = components.core.tabCollectionStorage.cachedTabCollections,
|
||||||
tabs = getListOfSessions().toTabs(),
|
mode = currentMode.getCurrentMode(),
|
||||||
topSites = components.core.topSiteStorage.cachedTopSites
|
tabs = getListOfSessions().toTabs(),
|
||||||
))
|
topSites = components.core.topSiteStorage.cachedTopSites
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
requireComponents.backgroundServices.accountManager.register(currentMode, owner = this)
|
requireComponents.backgroundServices.accountManager.register(currentMode, owner = this)
|
||||||
requireComponents.backgroundServices.accountManager.register(object : AccountObserver {
|
requireComponents.backgroundServices.accountManager.register(object : AccountObserver {
|
||||||
|
@ -377,7 +381,8 @@ class HomeFragment : Fragment() {
|
||||||
}, owner = this)
|
}, owner = this)
|
||||||
|
|
||||||
if (context.settings().showPrivateModeContextualFeatureRecommender &&
|
if (context.settings().showPrivateModeContextualFeatureRecommender &&
|
||||||
browsingModeManager.mode.isPrivate) {
|
browsingModeManager.mode.isPrivate
|
||||||
|
) {
|
||||||
recommendPrivateBrowsingShortcut()
|
recommendPrivateBrowsingShortcut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,7 +522,8 @@ class HomeFragment : Fragment() {
|
||||||
// Otherwise, we will encounter an activity token error.
|
// Otherwise, we will encounter an activity token error.
|
||||||
privateBrowsingButton.post {
|
privateBrowsingButton.post {
|
||||||
privateBrowsingRecommend.showAsDropDown(
|
privateBrowsingRecommend.showAsDropDown(
|
||||||
privateBrowsingButton, 0, CFR_Y_OFFSET, Gravity.TOP or Gravity.END)
|
privateBrowsingButton, 0, CFR_Y_OFFSET, Gravity.TOP or Gravity.END
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -528,7 +534,9 @@ class HomeFragment : Fragment() {
|
||||||
homeFragmentStore.dispatch(
|
homeFragmentStore.dispatch(
|
||||||
HomeFragmentAction.ModeChange(
|
HomeFragmentAction.ModeChange(
|
||||||
mode = currentMode.getCurrentMode(),
|
mode = currentMode.getCurrentMode(),
|
||||||
tabs = getListOfSessions().toTabs()))
|
tabs = getListOfSessions().toTabs()
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,10 +641,16 @@ class HomeFragment : Fragment() {
|
||||||
|
|
||||||
private fun removeAllTabsWithUndo(listOfSessionsToDelete: Sequence<Session>, private: Boolean) {
|
private fun removeAllTabsWithUndo(listOfSessionsToDelete: Sequence<Session>, private: Boolean) {
|
||||||
homeFragmentStore.dispatch(HomeFragmentAction.TabsChange(emptyList()))
|
homeFragmentStore.dispatch(HomeFragmentAction.TabsChange(emptyList()))
|
||||||
|
listOfSessionsToDelete.forEach {
|
||||||
|
requireComponents.core.pendingSessionDeletionManager.addSession(
|
||||||
|
it.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val deleteOperation: (suspend () -> Unit) = {
|
val deleteOperation: (suspend () -> Unit) = {
|
||||||
listOfSessionsToDelete.forEach {
|
listOfSessionsToDelete.forEach {
|
||||||
sessionManager.remove(it)
|
sessionManager.remove(it)
|
||||||
|
requireComponents.core.pendingSessionDeletionManager.removeSession(it.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deleteAllSessionsJob = deleteOperation
|
deleteAllSessionsJob = deleteOperation
|
||||||
|
@ -651,6 +665,11 @@ class HomeFragment : Fragment() {
|
||||||
view!!,
|
view!!,
|
||||||
snackbarMessage,
|
snackbarMessage,
|
||||||
getString(R.string.snackbar_deleted_undo), {
|
getString(R.string.snackbar_deleted_undo), {
|
||||||
|
listOfSessionsToDelete.forEach {
|
||||||
|
requireComponents.core.pendingSessionDeletionManager.removeSession(
|
||||||
|
it.id
|
||||||
|
)
|
||||||
|
}
|
||||||
if (private) {
|
if (private) {
|
||||||
requireComponents.analytics.metrics.track(Event.PrivateBrowsingSnackbarUndoTapped)
|
requireComponents.analytics.metrics.track(Event.PrivateBrowsingSnackbarUndoTapped)
|
||||||
}
|
}
|
||||||
|
@ -664,11 +683,13 @@ class HomeFragment : Fragment() {
|
||||||
|
|
||||||
private fun removeTabWithUndo(sessionId: String, private: Boolean) {
|
private fun removeTabWithUndo(sessionId: String, private: Boolean) {
|
||||||
val sessionManager = requireComponents.core.sessionManager
|
val sessionManager = requireComponents.core.sessionManager
|
||||||
|
requireComponents.core.pendingSessionDeletionManager.addSession(sessionId)
|
||||||
val deleteOperation: (suspend () -> Unit) = {
|
val deleteOperation: (suspend () -> Unit) = {
|
||||||
sessionManager.findSessionById(sessionId)
|
sessionManager.findSessionById(sessionId)
|
||||||
?.let { session ->
|
?.let { session ->
|
||||||
pendingSessionDeletion = null
|
pendingSessionDeletion = null
|
||||||
sessionManager.remove(session)
|
sessionManager.remove(session)
|
||||||
|
requireComponents.core.pendingSessionDeletionManager.removeSession(sessionId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -684,6 +705,7 @@ class HomeFragment : Fragment() {
|
||||||
view!!,
|
view!!,
|
||||||
snackbarMessage,
|
snackbarMessage,
|
||||||
getString(R.string.snackbar_deleted_undo), {
|
getString(R.string.snackbar_deleted_undo), {
|
||||||
|
requireComponents.core.pendingSessionDeletionManager.removeSession(sessionId)
|
||||||
pendingSessionDeletion = null
|
pendingSessionDeletion = null
|
||||||
emitSessionChanges()
|
emitSessionChanges()
|
||||||
},
|
},
|
||||||
|
@ -777,8 +799,12 @@ class HomeFragment : Fragment() {
|
||||||
border?.visibility = View.GONE
|
border?.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAnimationStart(animation: Animator?) { /* noop */ }
|
override fun onAnimationStart(animation: Animator?) { /* noop */
|
||||||
override fun onAnimationRepeat(animation: Animator?) { /* noop */ }
|
}
|
||||||
|
|
||||||
|
override fun onAnimationRepeat(animation: Animator?) { /* noop */
|
||||||
|
}
|
||||||
|
|
||||||
override fun onAnimationEnd(animation: Animator?) {
|
override fun onAnimationEnd(animation: Animator?) {
|
||||||
border?.animate()?.alpha(0.0F)?.setStartDelay(ANIM_ON_SCREEN_DELAY)
|
border?.animate()?.alpha(0.0F)?.setStartDelay(ANIM_ON_SCREEN_DELAY)
|
||||||
?.setDuration(FADE_ANIM_DURATION)
|
?.setDuration(FADE_ANIM_DURATION)
|
||||||
|
|
Loading…
Reference in New Issue