You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
iceraven-browser/app/src/main/java/org/mozilla/fenix/components/Components.kt

237 lines
9.3 KiB
Kotlin

/* 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.annotation.SuppressLint
import android.app.Application
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.core.app.NotificationManagerCompat
import com.google.android.play.core.review.ReviewManagerFactory
import mozilla.components.feature.addons.AddonManager
import mozilla.components.feature.addons.amo.AMOAddonsProvider
import mozilla.components.feature.addons.migration.DefaultSupportedAddonsChecker
import mozilla.components.feature.addons.update.DefaultAddonUpdater
import mozilla.components.feature.autofill.AutofillConfiguration
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import mozilla.components.support.base.android.NotificationsDelegate
import mozilla.components.support.base.worker.Frequency
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.Config
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.R
import org.mozilla.fenix.autofill.AutofillConfirmActivity
import org.mozilla.fenix.autofill.AutofillSearchActivity
import org.mozilla.fenix.autofill.AutofillUnlockActivity
import org.mozilla.fenix.components.appstate.AppState
import org.mozilla.fenix.components.metrics.MetricsMiddleware
import org.mozilla.fenix.datastore.pocketStoriesSelectedCategoriesDataStore
import org.mozilla.fenix.ext.asRecentTabs
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.filterState
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.sort
import org.mozilla.fenix.home.PocketUpdatesMiddleware
import org.mozilla.fenix.home.blocklist.BlocklistHandler
import org.mozilla.fenix.home.blocklist.BlocklistMiddleware
import org.mozilla.fenix.messaging.state.MessagingMiddleware
import org.mozilla.fenix.onboarding.FenixOnboarding
import org.mozilla.fenix.perf.AppStartReasonProvider
import org.mozilla.fenix.perf.StartupActivityLog
import org.mozilla.fenix.perf.StartupStateProvider
import org.mozilla.fenix.perf.StrictModeManager
import org.mozilla.fenix.perf.lazyMonitored
import org.mozilla.fenix.utils.ClipboardHandler
import org.mozilla.fenix.utils.Settings
import org.mozilla.fenix.wifi.WifiConnectionMonitor
import java.util.concurrent.TimeUnit
private const val AMO_COLLECTION_MAX_CACHE_AGE = 2 * 24 * 60L // Two days in minutes
/**
* Provides access to all components. This class is an implementation of the Service Locator
* pattern, which helps us manage the dependencies in our app.
*
* Note: these aren't just "components" from "android-components": they're any "component" that
* can be considered a building block of our app.
*/
class Components(private val context: Context) {
val backgroundServices by lazyMonitored {
BackgroundServices(
context,
push,
analytics.crashReporter,
[fenix] Closes https://github.com/mozilla-mobile/fenix/issues/7450: Lazy storage initialization Make sure that we actually lazily initialize our storage layers. With this patch applied, storage layers (history, logins, bookmarks) will be initialized when first accessed. We will no longer block GeckoEngine init, for example, on waiting for the logins storage to initialize (which needs to access the costly securePrefStorage). Similarly, BackgroundServices init will no longer require initialized instances of the storage components - references to their "lazy wrappers" will suffice. In practice, this change changes when our storage layers are initialized in the following ways. Currently, we will initialize everything on startup. This includes loading our megazord, as well. With this change, init path depends on if the user is signed-into FxA or not. If user is not an FxA user: - on startup, none of the storage layers are initialized - history storage will be initialized once, whenever: - first non-customTab page is loaded (access to the HistoryDelegate) - first interaction with the awesomebar - history UI is accessed - bookmarks storage will be initialized once, whenever: - something is bookmarked, or we need to figure out if something's bookmarked - bookmarks UI is accessed - logins storage will be initialized once, whenever: - first page is loaded with a login/password fields that can be autofilled - (or some other interaction by GV with the autofill/loginStorage delegates) - logins UI is accessed - all of these storages will be initialized if the user logs into FxA and starts syncing data - except, if a storage is not chosen to be synced, it will not be initialized If user is an FxA user: - on startup, none of the storage layers are initialized - sometime shortly after startup is complete, when a sync worker runs in the background, all storage layers that are enabled to sync will be initialized. This change also means that we delay loading the megazord until first access (as described above).
4 years ago
core.lazyHistoryStorage,
core.lazyBookmarksStorage,
core.lazyPasswordsStorage,
core.lazyRemoteTabsStorage,
core.lazyAutofillStorage,
strictMode,
)
}
val services by lazyMonitored { Services(context, backgroundServices.accountManager) }
val core by lazyMonitored { Core(context, analytics.crashReporter, strictMode) }
@Suppress("Deprecation")
val useCases by lazyMonitored {
UseCases(
context,
core.engine,
core.store,
core.webAppShortcutManager,
core.topSitesStorage,
core.bookmarksStorage,
core.historyStorage,
appStore,
core.client,
strictMode,
)
}
private val notificationManagerCompat = NotificationManagerCompat.from(context)
val notificationsDelegate: NotificationsDelegate by lazyMonitored {
NotificationsDelegate(
notificationManagerCompat,
)
}
val intentProcessors by lazyMonitored {
IntentProcessors(
context,
core.store,
useCases.sessionUseCases,
useCases.tabsUseCases,
useCases.customTabsUseCases,
useCases.searchUseCases,
core.webAppManifestStorage,
core.engine,
)
}
val addonsProvider by lazyMonitored {
// Check if we have a customized (overridden) AMO collection (supported in Nightly & Beta)
if (FeatureFlags.customExtensionCollectionFeature && context.settings().amoCollectionOverrideConfigured()) {
AMOAddonsProvider(
context,
core.client,
collectionUser = context.settings().overrideAmoUser,
collectionName = context.settings().overrideAmoCollection,
)
}
// Use build config otherwise
else if (!BuildConfig.AMO_COLLECTION_USER.isNullOrEmpty() &&
!BuildConfig.AMO_COLLECTION_NAME.isNullOrEmpty()
) {
AMOAddonsProvider(
context,
core.client,
serverURL = BuildConfig.AMO_SERVER_URL,
collectionUser = BuildConfig.AMO_COLLECTION_USER,
collectionName = BuildConfig.AMO_COLLECTION_NAME,
maxCacheAgeInMinutes = AMO_COLLECTION_MAX_CACHE_AGE,
)
}
// Fall back to defaults
else {
AMOAddonsProvider(context, core.client, maxCacheAgeInMinutes = AMO_COLLECTION_MAX_CACHE_AGE)
}
}
@Suppress("MagicNumber")
val addonUpdater by lazyMonitored {
DefaultAddonUpdater(context, Frequency(12, TimeUnit.HOURS), notificationsDelegate)
}
@Suppress("MagicNumber")
val supportedAddonsChecker by lazyMonitored {
DefaultSupportedAddonsChecker(
context,
Frequency(12, TimeUnit.HOURS),
)
}
val addonManager by lazyMonitored {
AddonManager(core.store, core.engine, addonsProvider, addonUpdater)
}
val analytics by lazyMonitored { Analytics(context, performance.visualCompletenessQueue.queue) }
val publicSuffixList by lazyMonitored { PublicSuffixList(context) }
val clipboardHandler by lazyMonitored { ClipboardHandler(context) }
val performance by lazyMonitored { PerformanceComponent() }
val push by lazyMonitored { Push(context, analytics.crashReporter) }
val wifiConnectionMonitor by lazyMonitored { WifiConnectionMonitor(context as Application) }
val strictMode by lazyMonitored { StrictModeManager(Config, this) }
val settings by lazyMonitored { Settings(context) }
val fenixOnboarding by lazyMonitored { FenixOnboarding(context) }
val reviewPromptController by lazyMonitored {
ReviewPromptController(
manager = ReviewManagerFactory.create(context),
reviewSettings = FenixReviewSettings(settings),
)
}
@delegate:SuppressLint("NewApi")
val autofillConfiguration by lazyMonitored {
AutofillConfiguration(
storage = core.passwordsStorage,
publicSuffixList = publicSuffixList,
unlockActivity = AutofillUnlockActivity::class.java,
confirmActivity = AutofillConfirmActivity::class.java,
searchActivity = AutofillSearchActivity::class.java,
applicationName = context.getString(R.string.app_name),
httpClient = core.client,
)
}
val appStartReasonProvider by lazyMonitored { AppStartReasonProvider() }
val startupActivityLog by lazyMonitored { StartupActivityLog() }
val startupStateProvider by lazyMonitored { StartupStateProvider(startupActivityLog, appStartReasonProvider) }
val appStore by lazyMonitored {
val blocklistHandler = BlocklistHandler(settings)
AppStore(
initialState = AppState(
collections = core.tabCollectionStorage.cachedTabCollections,
expandedCollections = emptySet(),
topSites = core.topSitesStorage.cachedTopSites.sort(),
recentBookmarks = emptyList(),
showCollectionPlaceholder = settings.showCollectionsPlaceholderOnHome,
// Provide an initial state for recent tabs to prevent re-rendering on the home screen.
// This will otherwise cause a visual jump as the section gets rendered from no state
// to some state.
recentTabs = if (settings.showRecentTabsFeature) {
core.store.state.asRecentTabs()
} else {
emptyList()
},
recentHistory = emptyList(),
).run { filterState(blocklistHandler) },
middlewares = listOf(
BlocklistMiddleware(blocklistHandler),
PocketUpdatesMiddleware(
core.pocketStoriesService,
context.pocketStoriesSelectedCategoriesDataStore,
),
MessagingMiddleware(
messagingStorage = analytics.messagingStorage,
),
MetricsMiddleware(metrics = analytics.metrics),
),
)
}
}
/**
* Returns the [Components] object from within a [Composable].
*/
val components: Components
@Composable
get() = LocalContext.current.components