diff --git a/app/src/main/java/org/mozilla/reference/browser/BrowserApplication.kt b/app/src/main/java/org/mozilla/reference/browser/BrowserApplication.kt index 5eae38262..859a92b94 100644 --- a/app/src/main/java/org/mozilla/reference/browser/BrowserApplication.kt +++ b/app/src/main/java/org/mozilla/reference/browser/BrowserApplication.kt @@ -6,9 +6,12 @@ package org.mozilla.reference.browser import android.app.Application import java.util.concurrent.TimeUnit +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import mozilla.components.browser.state.action.SystemAction import mozilla.components.concept.engine.webextension.isUnsupported @@ -27,7 +30,20 @@ import org.mozilla.reference.browser.push.PushFxaIntegration import org.mozilla.reference.browser.push.WebPushEngineIntegration open class BrowserApplication : Application() { - val components by lazy { Components(this) } + /** + * Scope for long-running work that must outlive any individual activity. [SupervisorJob] keeps a failure in one + * child from cancelling the others. + */ + private val applicationScope: CoroutineScope = + CoroutineScope( + SupervisorJob() + + Dispatchers.Main + + CoroutineExceptionHandler { _, throwable -> + Logger.error("ApplicationScope: Unhandled error: ${throwable.message}", throwable) + } + ) + + val components by lazy { Components(this, applicationScope) } override fun onCreate() { super.onCreate() diff --git a/app/src/main/java/org/mozilla/reference/browser/Components.kt b/app/src/main/java/org/mozilla/reference/browser/Components.kt index 518c1d8b4..71a523989 100644 --- a/app/src/main/java/org/mozilla/reference/browser/Components.kt +++ b/app/src/main/java/org/mozilla/reference/browser/Components.kt @@ -7,6 +7,7 @@ package org.mozilla.reference.browser import android.annotation.SuppressLint import android.content.Context import androidx.core.app.NotificationManagerCompat +import kotlinx.coroutines.CoroutineScope import mozilla.components.feature.autofill.AutofillConfiguration import mozilla.components.feature.downloads.DefaultFileSizeFormatter import mozilla.components.feature.downloads.DownloadEstimator @@ -26,8 +27,11 @@ import org.mozilla.reference.browser.components.UseCases import org.mozilla.reference.browser.components.Utilities /** Provides access to all components. */ -class Components(private val context: Context) { - val core by lazy { Core(context, analytics.crashReporter) } +class Components( + private val context: Context, + val applicationScope: CoroutineScope, +) { + val core by lazy { Core(context, analytics.crashReporter, applicationScope) } val useCases by lazy { UseCases( context, @@ -45,6 +49,7 @@ class Components(private val context: Context) { core.lazyHistoryStorage, core.lazyRemoteTabsStorage, core.lazyLoginsStorage, + applicationScope, ) } val analytics by lazy { Analytics(context) } @@ -56,6 +61,7 @@ class Components(private val context: Context) { useCases.searchUseCases, useCases.tabsUseCases, useCases.customTabsUseCases, + applicationScope, ) } val services by lazy { Services(context, backgroundServices.accountManager, useCases.tabsUseCases) } diff --git a/app/src/main/java/org/mozilla/reference/browser/autofill/AutofillService.kt b/app/src/main/java/org/mozilla/reference/browser/autofill/AutofillService.kt index 1a175f843..a60edc924 100644 --- a/app/src/main/java/org/mozilla/reference/browser/autofill/AutofillService.kt +++ b/app/src/main/java/org/mozilla/reference/browser/autofill/AutofillService.kt @@ -10,4 +10,5 @@ import org.mozilla.reference.browser.ext.components class AutofillService : AbstractAutofillService() { override val configuration: AutofillConfiguration by lazy { components.autofillConfiguration } + override val applicationScope by lazy { components.applicationScope } } diff --git a/app/src/main/java/org/mozilla/reference/browser/components/BackgroundServices.kt b/app/src/main/java/org/mozilla/reference/browser/components/BackgroundServices.kt index f1a07040a..146e44141 100644 --- a/app/src/main/java/org/mozilla/reference/browser/components/BackgroundServices.kt +++ b/app/src/main/java/org/mozilla/reference/browser/components/BackgroundServices.kt @@ -46,6 +46,7 @@ class BackgroundServices( placesHistoryStorage: Lazy, remoteTabsStorage: Lazy, loginsStorage: Lazy, + applicationScope: CoroutineScope, ) { companion object { const val CLIENT_ID = "3c49430b43dfba77" @@ -91,7 +92,7 @@ class BackgroundServices( NotificationManager.showReceivedTabs(context, device, tabs) } - push.feature?.let { push -> FxaPushSupportFeature(context, accountManager, push) } + push.feature?.let { push -> FxaPushSupportFeature(context, accountManager, push, applicationScope) } SyncedTabsIntegration(context, accountManager).launch() diff --git a/app/src/main/java/org/mozilla/reference/browser/components/Core.kt b/app/src/main/java/org/mozilla/reference/browser/components/Core.kt index adbf05e96..ff8231502 100644 --- a/app/src/main/java/org/mozilla/reference/browser/components/Core.kt +++ b/app/src/main/java/org/mozilla/reference/browser/components/Core.kt @@ -9,6 +9,7 @@ import android.content.SharedPreferences import android.os.Environment import androidx.preference.PreferenceManager import java.util.concurrent.TimeUnit +import kotlinx.coroutines.CoroutineScope import mozilla.components.browser.engine.gecko.permission.GeckoSitePermissionsStorage import mozilla.components.browser.icons.BrowserIcons import mozilla.components.browser.session.storage.SessionStorage @@ -65,6 +66,7 @@ private const val DAY_IN_MINUTES = 24 * 60L class Core( private val context: Context, crashReporter: CrashReporter, + private val applicationScope: CoroutineScope, ) { /** The browser engine component initialized based on the build configuration (see build variants). */ val engine: Engine by lazy { @@ -114,6 +116,7 @@ class Core( RegionMiddleware( context, LocationService.default(), + applicationScope = applicationScope, ), SearchMiddleware(context), RecordingDevicesMiddleware(context, context.components.notificationsDelegate), @@ -141,7 +144,7 @@ class Core( /** The storage component for persisting browser tab sessions. */ val sessionStorage: SessionStorage by lazy { - SessionStorage(context, engine) + SessionStorage(context, engine, applicationScope = applicationScope) } /** The storage component to persist browsing history (with the exception of private sessions). */ @@ -203,7 +206,7 @@ class Core( } val fileUploadsDirCleaner: FileUploadsDirCleaner by lazy { - FileUploadsDirCleaner { context.cacheDir } + FileUploadsDirCleaner(scope = applicationScope) { context.cacheDir } } private fun provideDefaultAddonProvider(): AMOAddonsProvider = diff --git a/app/src/main/java/org/mozilla/reference/browser/components/Utilities.kt b/app/src/main/java/org/mozilla/reference/browser/components/Utilities.kt index 0bd8785a5..e25a19ea5 100644 --- a/app/src/main/java/org/mozilla/reference/browser/components/Utilities.kt +++ b/app/src/main/java/org/mozilla/reference/browser/components/Utilities.kt @@ -5,6 +5,7 @@ package org.mozilla.reference.browser.components import android.content.Context +import kotlinx.coroutines.CoroutineScope import mozilla.components.browser.state.store.BrowserStore import mozilla.components.feature.customtabs.CustomTabIntentProcessor import mozilla.components.feature.intent.processing.TabIntentProcessor @@ -17,6 +18,7 @@ import mozilla.components.feature.tabs.TabsUseCases import mozilla.components.lib.publicsuffixlist.PublicSuffixList /** Component group for miscellaneous components. */ +@Suppress("LongParameterList") class Utilities( private val context: Context, private val store: BrowserStore, @@ -24,6 +26,7 @@ class Utilities( private val searchUseCases: SearchUseCases, private val tabsUseCases: TabsUseCases, private val customTabsUseCases: CustomTabsUseCases, + private val applicationScope: CoroutineScope, ) { /** Provides intent processing functionality for Progressive Web App and Custom Tab intents. */ val externalIntentProcessors by lazy { @@ -46,7 +49,8 @@ class Utilities( * processors. */ val intentProcessors by lazy { - externalIntentProcessors + TabIntentProcessor(tabsUseCases, searchUseCases.newTabSearch) + externalIntentProcessors + + TabIntentProcessor(tabsUseCases, searchUseCases.newTabSearch, applicationScope = applicationScope) } val publicSuffixList by lazy { diff --git a/app/src/main/java/org/mozilla/reference/browser/settings/AccountSettingsFragment.kt b/app/src/main/java/org/mozilla/reference/browser/settings/AccountSettingsFragment.kt index f363d203c..8b7df9b3b 100644 --- a/app/src/main/java/org/mozilla/reference/browser/settings/AccountSettingsFragment.kt +++ b/app/src/main/java/org/mozilla/reference/browser/settings/AccountSettingsFragment.kt @@ -120,6 +120,7 @@ class AccountSettingsFragment : PreferenceFragmentCompat() { pref: Preference?, failed: Boolean = false, ) { + @Suppress("DEPRECATION") // getLastSynced is deprecated see bug 2067060 val lastSyncTime = getLastSynced(context) pref?.summary = diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0ad6db991..aa52be173 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] # Android Components -android-components = "157.0.20260831085937" +android-components = "157.0.20260901103215" # AGP android-gradle-plugin = "9.3.1"