tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 29b0098fccac21323bcba0ddf411ce6df24734ef
parent 2c8ade6308ae273882889432db500bd72ca4b8c8
Author: Mugurell <Mugurell@users.noreply.github.com>
Date:   Mon, 17 Nov 2025 17:52:52 +0000

Bug 1996643 - part 9 - Remove the Fenix specific StoreProvider in favor of the upstream one r=android-reviewers,matt-tighe,nalexander

Differential Revision: https://phabricator.services.mozilla.com/D272162

Diffstat:
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt | 6+++---
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationStore.kt | 2++
Dmobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/StoreProvider.kt | 61-------------------------------------------------------------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/exceptions/login/LoginExceptionsFragment.kt | 10++++------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/exceptions/trackingprotection/TrackingProtectionExceptionsFragment.kt | 10++++------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt | 7+++----
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragment.kt | 18++++++++----------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/library/recentlyclosed/RecentlyClosedFragment.kt | 16+++++++---------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/nimbus/NimbusBranchesFragment.kt | 8++++----
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/account/AccountSettingsFragment.kt | 26++++++++++++--------------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/AddressManagementFragment.kt | 8++++----
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/advanced/LocaleSettingsFragment.kt | 10++++------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/autofill/AutofillSettingFragment.kt | 6++----
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/creditcards/CreditCardsManagementFragment.kt | 8++++----
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/AddLoginFragment.kt | 12+++++-------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/EditLoginFragment.kt | 11++++-------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/LoginDetailFragment.kt | 14++++++--------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/SavedLoginsFragment.kt | 12+++++-------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/quicksettings/protections/cookiebanners/CookieBannerPanelDialogFragment.kt | 26++++++++++++--------------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt | 26++++++++++++++------------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/ui/TabManagementFragment.kt | 26++++++++++++++------------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt | 26++++++++++++--------------
Dmobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/StoreProviderTest.kt | 100-------------------------------------------------------------------------------
Mmobile/android/fenix/docs/architectureexample/HistoryFragmentExample.kt | 18++++++++----------
24 files changed, 141 insertions(+), 326 deletions(-)

diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt @@ -15,8 +15,8 @@ import androidx.fragment.app.DialogFragment import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.navArgs import mozilla.components.lib.state.ext.consumeFrom +import mozilla.components.lib.state.helpers.StoreProvider.Companion.storeProvider import org.mozilla.fenix.R -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.databinding.FragmentCreateCollectionBinding import org.mozilla.fenix.ext.requireComponents @@ -42,9 +42,9 @@ class CollectionCreationFragment : DialogFragment() { _binding = FragmentCreateCollectionBinding.inflate(inflater, container, false) val args: CollectionCreationFragmentArgs by navArgs() - collectionCreationStore = StoreProvider.get(this) { + collectionCreationStore = storeProvider.get { restoredState -> CollectionCreationStore( - createInitialCollectionCreationState( + restoredState ?: createInitialCollectionCreationState( browserState = requireComponents.core.store.state, tabCollectionStorage = requireComponents.core.tabCollectionStorage, publicSuffixList = requireComponents.publicSuffixList, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationStore.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationStore.kt @@ -58,6 +58,8 @@ fun createInitialCollectionCreationState( selectedTabIds: Array<String>?, selectedTabCollectionId: Long, ): CollectionCreationState { + println("Mugurel: create initial state") + val tabs = browserState.getTabs(tabIds, publicSuffixList) val selectedTabs = if (selectedTabIds != null) { browserState.getTabs(selectedTabIds, publicSuffixList).toSet() diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/StoreProvider.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/StoreProvider.kt @@ -1,61 +0,0 @@ -/* 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 androidx.annotation.VisibleForTesting -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.ViewModelStoreOwner -import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.CoroutineScope -import mozilla.components.lib.state.Store - -/** - * Generic ViewModel wrapper of a [Store] helping to persist it across process/activity recreations. - * - * @param createStore [Store] factory receiving also the [ViewModel.viewModelScope] associated with this [ViewModel]. - */ -class StoreProvider<T : Store<*, *>>( - createStore: (CoroutineScope) -> T, -) : ViewModel() { - - @VisibleForTesting - @PublishedApi - internal val store: T = createStore(viewModelScope) - - companion object { - /** - * Returns an existing [Store] instance or creates a new one scoped to a [ViewModelStoreOwner]. - * - * @see [ViewModelProvider.get]. - */ - inline fun <reified T : Store<*, *>> get( - owner: ViewModelStoreOwner, - noinline createStore: (CoroutineScope) -> T, - ): T { - val factory = StoreProviderFactory(createStore) - val viewModel: StoreProvider<*> = - ViewModelProvider(owner, factory).get(T::class.java.name, StoreProvider::class.java) - return viewModel.store as T - } - } -} - -/** - * [ViewModel] factory to create [StoreProvider] instances that will wrap a [Store] instance - * helping to persist it across process/activity recreations. - * - * @param createStore [Store] factory receiving also the [ViewModel.viewModelScope] associated with this [ViewModel]. - */ -@VisibleForTesting -class StoreProviderFactory<T : Store<*, *>>( - private val createStore: (CoroutineScope) -> T, -) : ViewModelProvider.Factory { - - @Suppress("UNCHECKED_CAST") - override fun <VM : ViewModel> create(modelClass: Class<VM>): VM { - return StoreProvider(createStore) as VM - } -} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/exceptions/login/LoginExceptionsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/exceptions/login/LoginExceptionsFragment.kt @@ -14,8 +14,8 @@ import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.plus import mozilla.components.lib.state.ext.consumeFrom +import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore import org.mozilla.fenix.R -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.databinding.FragmentExceptionsBinding import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.showToolbar @@ -44,11 +44,9 @@ class LoginExceptionsFragment : Fragment() { container, false, ) - exceptionsStore = StoreProvider.get(this) { - ExceptionsFragmentStore( - ExceptionsFragmentState(items = emptyList()), - ) - } + exceptionsStore = fragmentStore(ExceptionsFragmentState(items = emptyList())) { + ExceptionsFragmentStore(it) + }.value exceptionsInteractor = DefaultLoginExceptionsInteractor( ioScope = viewLifecycleOwner.lifecycleScope + Dispatchers.IO, loginExceptionStorage = requireComponents.core.loginExceptionStorage, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/exceptions/trackingprotection/TrackingProtectionExceptionsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/exceptions/trackingprotection/TrackingProtectionExceptionsFragment.kt @@ -10,9 +10,9 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import mozilla.components.lib.state.ext.consumeFrom +import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.databinding.FragmentExceptionsBinding import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.showToolbar @@ -42,11 +42,9 @@ class TrackingProtectionExceptionsFragment : Fragment() { container, false, ) - exceptionsStore = StoreProvider.get(this) { - ExceptionsFragmentStore( - ExceptionsFragmentState(items = emptyList()), - ) - } + exceptionsStore = fragmentStore(ExceptionsFragmentState(items = emptyList())) { + ExceptionsFragmentStore(it) + }.value exceptionsInteractor = DefaultTrackingProtectionExceptionsInteractor( activity = activity as HomeActivity, exceptionsStore = exceptionsStore, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/library/history/HistoryFragment.kt @@ -101,7 +101,6 @@ import org.mozilla.fenix.addons.showSnackBar import org.mozilla.fenix.browser.browsingmode.BrowsingMode import org.mozilla.fenix.components.AppStore import org.mozilla.fenix.components.QrScanFenixFeature -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.VoiceSearchFeature import org.mozilla.fenix.components.appstate.AppAction import org.mozilla.fenix.components.history.DefaultPagedHistoryProvider @@ -197,16 +196,16 @@ class HistoryFragment : LibraryPageFragment<History>(), UserInteractionHandler, ): View { _binding = FragmentHistoryBinding.inflate(inflater, container, false) val view = binding.root - historyStore = StoreProvider.get(this) { + historyStore = fragmentStore(HistoryFragmentState.initial) { HistoryFragmentStore( - initialState = HistoryFragmentState.initial, + initialState = it, middleware = listOf( HistoryTelemetryMiddleware( isInPrivateMode = requireComponents.appStore.state.mode == BrowsingMode.Private, ), ), ) - } + }.value searchStore = buildSearchStore(toolbarStore).value _historyView = HistoryView( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/library/historymetadata/HistoryMetadataGroupFragment.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.map import mozilla.components.lib.state.ext.consumeFrom import mozilla.components.lib.state.ext.flowScoped +import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore import mozilla.components.support.base.feature.UserInteractionHandler import mozilla.components.support.ktx.kotlin.toShortUrl import mozilla.components.ui.widgets.withCenterAlignedButtons @@ -35,7 +36,6 @@ import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.addons.showSnackBar import org.mozilla.fenix.browser.browsingmode.BrowsingMode -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.databinding.FragmentHistoryMetadataGroupBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.nav @@ -86,15 +86,13 @@ class HistoryMetadataGroupFragment : _binding = FragmentHistoryMetadataGroupBinding.inflate(inflater, container, false) val historyItems = args.historyMetadataItems.filterIsInstance<History.Metadata>() - historyMetadataGroupStore = StoreProvider.get(this) { - HistoryMetadataGroupFragmentStore( - HistoryMetadataGroupFragmentState( - items = historyItems, - pendingDeletionItems = requireContext().components.appStore.state.pendingDeletionHistoryItems, - isEmpty = historyItems.isEmpty(), - ), - ) - } + historyMetadataGroupStore = fragmentStore( + HistoryMetadataGroupFragmentState( + items = historyItems, + pendingDeletionItems = requireContext().components.appStore.state.pendingDeletionHistoryItems, + isEmpty = historyItems.isEmpty(), + ), + ) { HistoryMetadataGroupFragmentStore(it) }.value interactor = DefaultHistoryMetadataGroupInteractor( controller = DefaultHistoryMetadataGroupController( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/library/recentlyclosed/RecentlyClosedFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/library/recentlyclosed/RecentlyClosedFragment.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.map import mozilla.components.browser.state.state.recover.RecoverableTab import mozilla.components.lib.state.ext.consumeFrom import mozilla.components.lib.state.ext.flowScoped +import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore import mozilla.components.support.base.feature.UserInteractionHandler import mozilla.telemetry.glean.private.NoExtras import org.mozilla.fenix.BrowserDirection @@ -28,7 +29,6 @@ import org.mozilla.fenix.GleanMetrics.RecentlyClosedTabs import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.browser.browsingmode.BrowsingMode -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.databinding.FragmentRecentlyClosedTabsBinding import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.setTextColor @@ -106,14 +106,12 @@ class RecentlyClosedFragment : savedInstanceState: Bundle?, ): View { val binding = FragmentRecentlyClosedTabsBinding.inflate(inflater, container, false) - recentlyClosedFragmentStore = StoreProvider.get(this) { - RecentlyClosedFragmentStore( - RecentlyClosedFragmentState( - items = listOf(), - selectedTabs = emptySet(), - ), - ) - } + recentlyClosedFragmentStore = fragmentStore( + RecentlyClosedFragmentState( + items = listOf(), + selectedTabs = emptySet(), + ), + ) { RecentlyClosedFragmentStore(it) }.value recentlyClosedController = DefaultRecentlyClosedController( navController = findNavController(), browserStore = requireComponents.core.store, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/nimbus/NimbusBranchesFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/nimbus/NimbusBranchesFragment.kt @@ -15,9 +15,9 @@ import androidx.navigation.fragment.navArgs import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import mozilla.components.lib.state.ext.consumeFrom +import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore import mozilla.components.support.base.log.logger.Logger import org.mozilla.fenix.R -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.compose.core.Action import org.mozilla.fenix.compose.snackbar.Snackbar import org.mozilla.fenix.compose.snackbar.SnackbarState @@ -49,9 +49,9 @@ class NimbusBranchesFragment : Fragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - nimbusBranchesStore = StoreProvider.get(this) { - NimbusBranchesStore(NimbusBranchesState(branches = emptyList())) - } + nimbusBranchesStore = fragmentStore(NimbusBranchesState(branches = emptyList())) { + NimbusBranchesStore(it) + }.value controller = NimbusBranchesController( isTelemetryEnabled = { requireContext().settings().isTelemetryEnabled }, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/account/AccountSettingsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/account/AccountSettingsFragment.kt @@ -30,6 +30,7 @@ import mozilla.components.concept.sync.AccountObserver import mozilla.components.concept.sync.ConstellationState import mozilla.components.concept.sync.DeviceConstellationObserver import mozilla.components.lib.state.ext.consumeFrom +import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore import mozilla.components.service.fxa.SyncEngine import mozilla.components.service.fxa.manager.FxaAccountManager import mozilla.components.service.fxa.manager.SyncEnginesStorage @@ -42,7 +43,6 @@ import mozilla.components.ui.widgets.withCenterAlignedButtons import mozilla.telemetry.glean.private.NoExtras import org.mozilla.fenix.GleanMetrics.SyncAccount import org.mozilla.fenix.R -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.accounts.FenixFxAEntryPoint import org.mozilla.fenix.compose.snackbar.Snackbar import org.mozilla.fenix.compose.snackbar.SnackbarState @@ -144,20 +144,18 @@ class AccountSettingsFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.account_settings_preferences, rootKey) - accountSettingsStore = StoreProvider.get(this) { - AccountSettingsFragmentStore( - AccountSettingsFragmentState( - lastSyncedDate = if (getLastSynced(requireContext()) == 0L) { - LastSyncTime.Never - } else { - LastSyncTime.Success(getLastSynced(requireContext())) - }, - deviceName = requireComponents.backgroundServices.defaultDeviceName( - requireContext(), - ), + accountSettingsStore = fragmentStore( + AccountSettingsFragmentState( + lastSyncedDate = if (getLastSynced(requireContext()) == 0L) { + LastSyncTime.Never + } else { + LastSyncTime.Success(getLastSynced(requireContext())) + }, + deviceName = requireComponents.backgroundServices.defaultDeviceName( + requireContext(), ), - ) - } + ), + ) { AccountSettingsFragmentStore(it) }.value accountManager = requireComponents.backgroundServices.accountManager accountManager.register(accountStateObserver, this, true) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/AddressManagementFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/address/AddressManagementFragment.kt @@ -16,10 +16,10 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import mozilla.components.lib.state.ext.consumeFrom import mozilla.components.lib.state.ext.observeAsComposableState +import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore import mozilla.telemetry.glean.private.NoExtras import org.mozilla.fenix.GleanMetrics.Addresses import org.mozilla.fenix.R -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.settings.address.controller.DefaultAddressManagementController @@ -44,9 +44,9 @@ class AddressManagementFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle?, ): View { - store = StoreProvider.get(this) { - AutofillFragmentStore(AutofillFragmentState()) - } + store = fragmentStore(AutofillFragmentState()) { + AutofillFragmentStore(it) + }.value interactor = DefaultAddressManagementInteractor( controller = DefaultAddressManagementController( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/advanced/LocaleSettingsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/advanced/LocaleSettingsFragment.kt @@ -17,10 +17,10 @@ import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment import androidx.lifecycle.Lifecycle import mozilla.components.lib.state.ext.consumeFrom +import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore import mozilla.components.support.ktx.android.view.hideKeyboard import mozilla.components.support.locale.LocaleUseCases import org.mozilla.fenix.R -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.databinding.FragmentLocaleSettingsBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.showToolbar @@ -49,11 +49,9 @@ class LocaleSettingsFragment : Fragment(), MenuProvider { val browserStore = requireContext().components.core.store val localeUseCase = LocaleUseCases(browserStore) - localeSettingsStore = StoreProvider.get(this) { - LocaleSettingsStore( - createInitialLocaleSettingsState(requireContext()), - ) - } + localeSettingsStore = fragmentStore(createInitialLocaleSettingsState(requireContext())) { + LocaleSettingsStore(it) + }.value interactor = LocaleSettingsInteractor( controller = DefaultLocaleSettingsController( activity = requireActivity(), diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/autofill/AutofillSettingFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/autofill/AutofillSettingFragment.kt @@ -25,12 +25,12 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import mozilla.components.lib.state.ext.consumeFrom +import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore import mozilla.components.service.fxa.SyncEngine import mozilla.components.service.sync.autofill.AutofillCreditCardsAddressesStorage import mozilla.components.ui.widgets.withCenterAlignedButtons import org.mozilla.fenix.NavGraphDirections import org.mozilla.fenix.R -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.accounts.FenixFxAEntryPoint import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.runIfFragmentIsAttached @@ -79,9 +79,7 @@ class AutofillSettingFragment : BiometricPromptPreferenceFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - store = StoreProvider.get(this) { - AutofillFragmentStore(AutofillFragmentState()) - } + store = fragmentStore(AutofillFragmentState()) { AutofillFragmentStore(it) }.value loadAutofillState() } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/creditcards/CreditCardsManagementFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/creditcards/CreditCardsManagementFragment.kt @@ -13,9 +13,9 @@ import androidx.navigation.fragment.findNavController import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import mozilla.components.lib.state.ext.consumeFrom +import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore import org.mozilla.fenix.R import org.mozilla.fenix.SecureFragment -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.databinding.ComponentCreditCardsBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.redirectToReAuth @@ -44,9 +44,9 @@ class CreditCardsManagementFragment : SecureFragment() { ): View? { val view = inflater.inflate(CreditCardsManagementView.LAYOUT_ID, container, false) - store = StoreProvider.get(this) { - AutofillFragmentStore(AutofillFragmentState()) - } + store = fragmentStore(AutofillFragmentState()) { + AutofillFragmentStore(it) + }.value interactor = DefaultCreditCardsManagementInteractor( controller = DefaultCreditCardsManagementController( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/AddLoginFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/AddLoginFragment.kt @@ -24,6 +24,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import mozilla.components.lib.state.ext.consumeFrom +import mozilla.components.lib.state.helpers.StoreProvider.Companion.navBackStackStore import mozilla.components.support.ktx.android.view.hideKeyboard import mozilla.components.support.ktx.android.view.showKeyboard import mozilla.components.support.ktx.util.URLStringUtils @@ -31,7 +32,6 @@ import org.mozilla.fenix.GleanMetrics.Logins import org.mozilla.fenix.R import org.mozilla.fenix.biometricauthentication.AuthenticationStatus import org.mozilla.fenix.biometricauthentication.BiometricAuthenticationManager -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.databinding.FragmentAddLoginBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.registerForActivityResult @@ -83,12 +83,10 @@ class AddLoginFragment : Fragment(R.layout.fragment_add_login), MenuProvider { _binding = FragmentAddLoginBinding.bind(view) - loginsFragmentStore = - StoreProvider.get(findNavController().getBackStackEntry(R.id.savedLogins)) { - LoginsFragmentStore( - createInitialLoginsListState(requireContext().settings()), - ) - } + loginsFragmentStore = findNavController().getBackStackEntry(R.id.savedLogins) + .navBackStackStore(createInitialLoginsListState(requireContext().settings())) { + LoginsFragmentStore(it) + }.value interactor = AddLoginInteractor( SavedLoginsStorageController( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/EditLoginFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/EditLoginFragment.kt @@ -25,13 +25,13 @@ import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import com.google.android.material.textfield.TextInputLayout import mozilla.components.lib.state.ext.consumeFrom +import mozilla.components.lib.state.helpers.StoreProvider.Companion.navBackStackStore import mozilla.components.support.ktx.android.view.hideKeyboard import mozilla.telemetry.glean.private.NoExtras import org.mozilla.fenix.GleanMetrics.Logins import org.mozilla.fenix.R import org.mozilla.fenix.biometricauthentication.AuthenticationStatus import org.mozilla.fenix.biometricauthentication.BiometricAuthenticationManager -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.databinding.FragmentEditLoginBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.registerForActivityResult @@ -53,7 +53,6 @@ import org.mozilla.fenix.settings.logins.togglePasswordReveal class EditLoginFragment : Fragment(R.layout.fragment_edit_login), MenuProvider { private val args by navArgs<EditLoginFragmentArgs>() - private lateinit var loginsFragmentStore: LoginsFragmentStore private lateinit var interactor: EditLoginInteractor private lateinit var oldLogin: SavedLogin @@ -89,11 +88,9 @@ class EditLoginFragment : Fragment(R.layout.fragment_edit_login), MenuProvider { oldLogin = args.savedLoginItem - loginsFragmentStore = - StoreProvider.get(findNavController().getBackStackEntry(R.id.savedLogins)) { - LoginsFragmentStore( - createInitialLoginsListState(requireContext().settings()), - ) + val loginsFragmentStore by findNavController().getBackStackEntry(R.id.savedLogins) + .navBackStackStore(createInitialLoginsListState(requireContext().settings())) { + LoginsFragmentStore(it) } interactor = EditLoginInteractor( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/LoginDetailFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/LoginDetailFragment.kt @@ -29,6 +29,7 @@ import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import com.google.android.material.dialog.MaterialAlertDialogBuilder import mozilla.components.lib.state.ext.consumeFrom +import mozilla.components.lib.state.helpers.StoreProvider.Companion.navBackStackStore import mozilla.components.ui.widgets.withCenterAlignedButtons import mozilla.telemetry.glean.private.NoExtras import org.mozilla.fenix.BrowserDirection @@ -38,7 +39,6 @@ import org.mozilla.fenix.R import org.mozilla.fenix.SecureFragment import org.mozilla.fenix.biometricauthentication.AuthenticationStatus import org.mozilla.fenix.biometricauthentication.BiometricAuthenticationManager -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.compose.snackbar.Snackbar import org.mozilla.fenix.compose.snackbar.SnackbarState import org.mozilla.fenix.databinding.FragmentLoginDetailBinding @@ -65,7 +65,6 @@ class LoginDetailFragment : SecureFragment(R.layout.fragment_login_detail), Menu private val args by navArgs<LoginDetailFragmentArgs>() private var login: SavedLogin? = null - private lateinit var savedLoginsStore: LoginsFragmentStore private lateinit var loginDetailsBindingDelegate: LoginDetailsBindingDelegate private lateinit var interactor: LoginDetailInteractor private var menu: Menu? = null @@ -91,12 +90,6 @@ class LoginDetailFragment : SecureFragment(R.layout.fragment_login_detail), Menu setSecureContentVisibility(true) } - savedLoginsStore = - StoreProvider.get(findNavController().getBackStackEntry(R.id.savedLogins)) { - LoginsFragmentStore( - createInitialLoginsListState(requireContext().settings()), - ) - } loginDetailsBindingDelegate = LoginDetailsBindingDelegate(binding) return view @@ -106,6 +99,11 @@ class LoginDetailFragment : SecureFragment(R.layout.fragment_login_detail), Menu super.onViewCreated(view, savedInstanceState) requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED) + val savedLoginsStore by findNavController().getBackStackEntry(R.id.savedLogins) + .navBackStackStore(createInitialLoginsListState(requireContext().settings())) { + LoginsFragmentStore(it) + } + interactor = LoginDetailInteractor( SavedLoginsStorageController( passwordsStorage = requireContext().components.core.passwordsStorage, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/SavedLoginsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/SavedLoginsFragment.kt @@ -37,6 +37,7 @@ import mozilla.components.concept.menu.MenuController import mozilla.components.concept.menu.Orientation import mozilla.components.lib.state.ext.consumeFrom import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore +import mozilla.components.lib.state.helpers.StoreProvider.Companion.navBackStackStore import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.Config import org.mozilla.fenix.HomeActivity @@ -45,7 +46,6 @@ import org.mozilla.fenix.SecureFragment import org.mozilla.fenix.biometricauthentication.AuthenticationStatus import org.mozilla.fenix.biometricauthentication.BiometricAuthenticationManager import org.mozilla.fenix.components.LogMiddleware -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.databinding.FragmentSavedLoginsBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.hideToolbar @@ -216,12 +216,10 @@ class SavedLoginsFragment : SecureFragment(), MenuProvider { _binding = FragmentSavedLoginsBinding.bind(view) - savedLoginsStore = - StoreProvider.get(findNavController().getBackStackEntry(R.id.savedLogins)) { - LoginsFragmentStore( - createInitialLoginsListState(requireContext().settings()), - ) - } + savedLoginsStore = findNavController().getBackStackEntry(R.id.savedLogins) + .navBackStackStore(createInitialLoginsListState(requireContext().settings())) { + LoginsFragmentStore(it) + }.value loginsListController = LoginsListController( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/quicksettings/protections/cookiebanners/CookieBannerPanelDialogFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/quicksettings/protections/cookiebanners/CookieBannerPanelDialogFragment.kt @@ -17,9 +17,9 @@ import kotlinx.coroutines.plus import mozilla.components.browser.state.selector.findTabOrCustomTab import mozilla.components.browser.state.state.SessionState import mozilla.components.lib.state.ext.consumeFrom +import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore import org.mozilla.fenix.R import org.mozilla.fenix.android.FenixDialogFragment -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.databinding.FragmentCookieBannerHandlingDetailsDialogBinding import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.trackingprotection.ProtectionsState @@ -52,19 +52,17 @@ class CookieBannerPanelDialogFragment : FenixDialogFragment() { val rootView = inflateRootView(container) val tab = store.state.findTabOrCustomTab(provideCurrentTabId()) - protectionsStore = StoreProvider.get(this) { - ProtectionsStore( - ProtectionsState( - tab = tab, - url = args.url, - isTrackingProtectionEnabled = args.trackingProtectionEnabled, - cookieBannerUIMode = args.cookieBannerUIMode, - listTrackers = listOf(), - mode = ProtectionsState.Mode.Normal, - lastAccessedCategory = "", - ), - ) - } + protectionsStore = fragmentStore( + ProtectionsState( + tab = tab, + url = args.url, + isTrackingProtectionEnabled = args.trackingProtectionEnabled, + cookieBannerUIMode = args.cookieBannerUIMode, + listTrackers = listOf(), + mode = ProtectionsState.Mode.Normal, + lastAccessedCategory = "", + ), + ) { ProtectionsStore(it) }.value val controller = DefaultCookieBannerDetailsController( context = requireContext(), diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt @@ -40,6 +40,7 @@ import mozilla.components.feature.accounts.push.CloseTabsUseCases import mozilla.components.feature.downloads.ui.DownloadCancelDialogFragment import mozilla.components.feature.tabs.tabstray.TabsFeature import mozilla.components.lib.state.ext.observeAsState +import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import mozilla.components.support.ktx.android.util.AndroidDisplayUnitConverter import mozilla.telemetry.glean.private.NoExtras @@ -49,7 +50,6 @@ import org.mozilla.fenix.GleanMetrics.TabsTray import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.NavGraphDirections import org.mozilla.fenix.R -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.compose.core.Action import org.mozilla.fenix.compose.snackbar.Snackbar import org.mozilla.fenix.compose.snackbar.SnackbarState @@ -159,22 +159,24 @@ class TabsTrayFragment : AppCompatDialogFragment() { }, ) - tabsTrayStore = StoreProvider.get(this) { + tabsTrayStore = fragmentStore( + TabsTrayState( + selectedPage = initialPage, + mode = initialMode, + inactiveTabs = inactiveTabs, + inactiveTabsExpanded = initialInactiveExpanded, + normalTabs = normalTabs, + privateTabs = requireComponents.core.store.state.privateTabs, + selectedTabId = requireComponents.core.store.state.selectedTabId, + ), + ) { TabsTrayStore( - initialState = TabsTrayState( - selectedPage = initialPage, - mode = initialMode, - inactiveTabs = inactiveTabs, - inactiveTabsExpanded = initialInactiveExpanded, - normalTabs = normalTabs, - privateTabs = requireComponents.core.store.state.privateTabs, - selectedTabId = requireComponents.core.store.state.selectedTabId, - ), + initialState = it, middlewares = listOf( TabsTrayTelemetryMiddleware(requireComponents.nimbus.events), ), ) - } + }.value navigationInteractor = DefaultNavigationInteractor( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/ui/TabManagementFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tabstray/ui/TabManagementFragment.kt @@ -43,6 +43,7 @@ import mozilla.components.feature.accounts.push.CloseTabsUseCases import mozilla.components.feature.downloads.ui.DownloadCancelDialogFragment import mozilla.components.feature.tabs.tabstray.TabsFeature import mozilla.components.lib.state.ext.observeAsState +import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import mozilla.components.support.ktx.android.view.setSystemBarsBackground import mozilla.telemetry.glean.private.NoExtras @@ -51,7 +52,6 @@ import org.mozilla.fenix.GleanMetrics.PrivateBrowsingLocked import org.mozilla.fenix.GleanMetrics.TabsTray import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.ext.actualInactiveTabs import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.getBottomToolbarHeight @@ -146,22 +146,24 @@ class TabManagementFragment : DialogFragment() { }, ) - tabsTrayStore = StoreProvider.get(this) { + tabsTrayStore = fragmentStore( + TabsTrayState( + selectedPage = initialPage, + mode = initialMode, + inactiveTabs = inactiveTabs, + inactiveTabsExpanded = initialInactiveExpanded, + normalTabs = normalTabs, + privateTabs = requireComponents.core.store.state.privateTabs, + selectedTabId = requireComponents.core.store.state.selectedTabId, + ), + ) { TabsTrayStore( - initialState = TabsTrayState( - selectedPage = initialPage, - mode = initialMode, - inactiveTabs = inactiveTabs, - inactiveTabsExpanded = initialInactiveExpanded, - normalTabs = normalTabs, - privateTabs = requireComponents.core.store.state.privateTabs, - selectedTabId = requireComponents.core.store.state.selectedTabId, - ), + initialState = it, middlewares = listOf( TabsTrayTelemetryMiddleware(requireComponents.nimbus.events), ), ) - } + }.value tabManagerController = DefaultTabManagerController( accountManager = requireComponents.backgroundServices.accountManager, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt @@ -36,6 +36,7 @@ import mozilla.components.browser.state.store.BrowserStore import mozilla.components.feature.session.TrackingProtectionUseCases import mozilla.components.lib.state.ext.consumeFlow import mozilla.components.lib.state.ext.observe +import mozilla.components.lib.state.helpers.StoreProvider.Companion.fragmentStore import mozilla.components.support.base.feature.UserInteractionHandler import mozilla.components.support.base.log.logger.Logger import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifAnyChanged @@ -44,7 +45,6 @@ import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.GleanMetrics.TrackingProtection import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R -import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.databinding.FragmentTrackingProtectionBinding import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.requireComponents @@ -89,19 +89,17 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), UserInt val view = inflateRootView(container) val tab = store.state.findTabOrCustomTab(provideCurrentTabId()) - protectionsStore = StoreProvider.get(this) { - ProtectionsStore( - ProtectionsState( - tab = tab, - url = args.url, - isTrackingProtectionEnabled = args.trackingProtectionEnabled, - cookieBannerUIMode = args.cookieBannerUIMode, - listTrackers = listOf(), - mode = ProtectionsState.Mode.Normal, - lastAccessedCategory = "", - ), - ) - } + protectionsStore = fragmentStore( + ProtectionsState( + tab = tab, + url = args.url, + isTrackingProtectionEnabled = args.trackingProtectionEnabled, + cookieBannerUIMode = args.cookieBannerUIMode, + listTrackers = listOf(), + mode = ProtectionsState.Mode.Normal, + lastAccessedCategory = "", + ), + ) { ProtectionsStore(it) }.value trackingProtectionInteractor = TrackingProtectionPanelInteractor( context = requireContext(), fragment = this, diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/StoreProviderTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/StoreProviderTest.kt @@ -1,100 +0,0 @@ -/* 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 androidx.fragment.app.Fragment -import kotlinx.coroutines.CoroutineScope -import mozilla.components.lib.state.Action -import mozilla.components.lib.state.State -import mozilla.components.lib.state.Store -import mozilla.components.support.test.robolectric.createAddedTestFragment -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertSame -import org.junit.Assert.assertTrue -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner - -@RunWith(RobolectricTestRunner::class) -class StoreProviderTest { - - private class BasicState : State - - private val basicStore = Store(BasicState(), { state, _: Action -> state }) - - @Test - fun `factory returns store provider`() { - var createCalled = false - val factory = StoreProviderFactory { - createCalled = true - basicStore - } - - assertFalse(createCalled) - - assertEquals(basicStore, factory.create(StoreProvider::class.java).store) - - assertTrue(createCalled) - } - - @Test - fun `get returns store`() { - val fragment = createAddedTestFragment { Fragment() } - - val store = StoreProvider.get(fragment) { basicStore } - assertEquals(basicStore, store) - } - - @Test - fun `get only calls createStore if needed`() { - val fragment = createAddedTestFragment { Fragment() } - - var createCalled = false - val createStore: (CoroutineScope) -> Store<BasicState, Action> = { - createCalled = true - basicStore - } - - StoreProvider.get(fragment, createStore) - assertTrue(createCalled) - - createCalled = false - StoreProvider.get(fragment, createStore) - assertFalse(createCalled) - } - - @Test - fun `GIVEN different stores are persisted WHEN requesting them THEN get their unique instances`() { - val fragment = createAddedTestFragment { Fragment() } - var createACalled = false - val storeAFactory: (CoroutineScope) -> Store<BasicState, Action> = { - createACalled = true - basicStore - } - var createBCalled = false - val storeBFactory: (CoroutineScope) -> StoreB = { - createBCalled = true - StoreB(BasicState()) - } - - val storeA: Store<BasicState, Action> = StoreProvider.get(fragment, storeAFactory) - val storeB: StoreB = StoreProvider.get(fragment, storeBFactory) - assertTrue(createACalled) - assertTrue(createBCalled) - - createACalled = false - createBCalled = false - assertSame(storeA, StoreProvider.get(fragment, storeAFactory)) - assertSame(storeB, StoreProvider.get(fragment, storeBFactory)) - assertFalse(createACalled) - assertFalse(createBCalled) - } - - private class StoreB(initialState: BasicState) : Store<BasicState, Action>( - initialState, - { state, _: Action -> state }, - ) -} diff --git a/mobile/android/fenix/docs/architectureexample/HistoryFragmentExample.kt b/mobile/android/fenix/docs/architectureexample/HistoryFragmentExample.kt @@ -6,17 +6,15 @@ // /docs/architecture-overview.md class HistoryFragment : Fragment() { - private val store by lazy { - StoreProvider.get(this) { - HistoryStore( - initialState = HistoryState.initial, - middleware = listOf( - HistoryNavigationMiddleware(findNavController()) - HistoryStorageMiddleware(HistoryStorage()), - HistoryTelemetryMiddleware(), - ) + private val store by storeFragment(HistoryState.initial) { restoredState -> + HistoryStore( + initialState = restoredState, + middleware = listOf( + HistoryNavigationMiddleware(findNavController()) + HistoryStorageMiddleware(HistoryStorage()), + HistoryTelemetryMiddleware(), ) - } + ) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {