diff --git a/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt b/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt index 923c655a8..018812fab 100644 --- a/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt +++ b/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt @@ -8,23 +8,17 @@ import android.content.Context import android.os.Build import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting.PRIVATE -import androidx.lifecycle.ProcessLifecycleOwner import mozilla.components.browser.storage.sync.PlacesBookmarksStorage import mozilla.components.browser.storage.sync.PlacesHistoryStorage -import mozilla.components.concept.push.Bus import mozilla.components.concept.sync.AccountObserver import mozilla.components.concept.sync.AuthType import mozilla.components.concept.sync.DeviceCapability -import mozilla.components.concept.sync.DeviceEvent -import mozilla.components.concept.sync.DeviceEventsObserver -import mozilla.components.concept.sync.DevicePushSubscription import mozilla.components.concept.sync.DeviceType import mozilla.components.concept.sync.OAuthAccount +import mozilla.components.feature.accounts.push.FxaPushSupportFeature +import mozilla.components.feature.accounts.push.SendTabFeature import mozilla.components.feature.push.AutoPushFeature -import mozilla.components.feature.push.AutoPushSubscription import mozilla.components.feature.push.PushConfig -import mozilla.components.feature.push.PushSubscriptionObserver -import mozilla.components.feature.push.PushType import mozilla.components.lib.crash.CrashReporter import mozilla.components.lib.dataprotect.SecureAbove22Preferences import mozilla.components.service.fxa.DeviceConfig @@ -115,16 +109,6 @@ class BackgroundServices( GlobalSyncableStoreProvider.configureKeyStorage(secureAbove22Preferences) } - private val deviceEventObserver = object : DeviceEventsObserver { - private val logger = Logger("DeviceEventsObserver") - override fun onEvents(events: List) { - logger.info("Received ${events.size} device event(s)") - events.filterIsInstance().forEach { - notificationManager.showReceivedTabs(it) - } - } - } - private val telemetryAccountObserver = TelemetryAccountObserver( context, context.components.analytics.metrics @@ -132,8 +116,6 @@ class BackgroundServices( val accountAbnormalities = AccountAbnormalities(context, crashReporter) - private val pushAccountObserver by lazy { push?.let { PushAccountObserver(it) } } - val accountManager = makeAccountManager(context, serverConfig, deviceConfig, syncConfig) @VisibleForTesting(otherwise = PRIVATE) @@ -185,11 +167,6 @@ class BackgroundServices( ).also { accountManager -> // TODO this needs to change once we have a SyncManager context.settings().fxaHasSyncedItems = syncConfig?.supportedEngines?.isNotEmpty() ?: false - accountManager.registerForDeviceEvents( - deviceEventObserver, - ProcessLifecycleOwner.get(), - false - ) // Register a telemetry account observer to keep track of FxA auth metrics. accountManager.register(telemetryAccountObserver) @@ -200,39 +177,13 @@ class BackgroundServices( // Enable push if it's configured. push?.let { autoPushFeature -> - // Register the push account observer so we know how to update our push subscriptions. - accountManager.register(pushAccountObserver!!) - - val logger = Logger("AutoPushFeature") - - // Notify observers for Services' messages. - autoPushFeature.registerForPushMessages( - PushType.Services, - object : Bus.Observer { - override fun onEvent(type: PushType, message: String) { - accountManager.authenticatedAccount()?.deviceConstellation() - ?.processRawEventAsync(message) - } - }) - - // Notify observers for subscription changes. - autoPushFeature.registerForSubscriptions(object : PushSubscriptionObserver { - override fun onSubscriptionAvailable(subscription: AutoPushSubscription) { - // Update for only the services subscription. - if (subscription.type == PushType.Services) { - logger.info("New push subscription received for FxA") - accountManager.authenticatedAccount()?.deviceConstellation() - ?.setDevicePushSubscriptionAsync( - DevicePushSubscription( - endpoint = subscription.endpoint, - publicKey = subscription.publicKey, - authKey = subscription.authKey - ) - ) - } - } - }) + FxaPushSupportFeature(context, accountManager, autoPushFeature) } + + SendTabFeature(accountManager) { device, tabs -> + notificationManager.showReceivedTabs(context, device, tabs) + } + accountAbnormalities.accountManagerInitializedAsync( accountManager, accountManager.initAsync() @@ -242,7 +193,7 @@ class BackgroundServices( /** * Provides notification functionality, manages notification channels. */ - val notificationManager by lazy { + private val notificationManager by lazy { NotificationManager(context) } } @@ -290,30 +241,3 @@ class TelemetryAccountObserver( context.settings().fxaSignedIn = false } } - -/** - * When we login/logout of FxA, we need to update our push subscriptions to match the newly - * logged in account. - * - * We added the push service to the AccountManager observer so that we can control when the - * service will start/stop. Firebase was added when landing the push service to ensure it works - * as expected without causing any (as many) side effects. - * - * In order to use Firebase with Leanplum and other marketing features, we need it always - * running so we cannot leave this code in place when we implement those features. - * - * We should have this removed when we are more confident - * of the send-tab/push feature: https://github.com/mozilla-mobile/fenix/issues/4063 - */ -@VisibleForTesting(otherwise = PRIVATE) -class PushAccountObserver(private val push: AutoPushFeature) : AccountObserver { - override fun onLoggedOut() { - push.unsubscribeForType(PushType.Services) - } - - override fun onAuthenticated(account: OAuthAccount, authType: AuthType) { - if (authType != AuthType.Existing) { - push.subscribeForType(PushType.Services) - } - } -} diff --git a/app/src/main/java/org/mozilla/fenix/components/NotificationManager.kt b/app/src/main/java/org/mozilla/fenix/components/NotificationManager.kt index 98b484c60..24d3034f5 100644 --- a/app/src/main/java/org/mozilla/fenix/components/NotificationManager.kt +++ b/app/src/main/java/org/mozilla/fenix/components/NotificationManager.kt @@ -17,7 +17,7 @@ import android.os.Build.VERSION.SDK_INT import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.getSystemService -import mozilla.components.concept.sync.DeviceEvent +import mozilla.components.concept.sync.Device import mozilla.components.concept.sync.TabData import mozilla.components.support.base.log.logger.Logger import org.mozilla.fenix.R @@ -49,11 +49,11 @@ class NotificationManager(private val context: Context) { private val logger = Logger("NotificationManager") - fun showReceivedTabs(event: DeviceEvent.TabReceived) { + fun showReceivedTabs(context: Context, device: Device?, tabs: List) { // In the future, experiment with displaying multiple tabs from the same device as as Notification Groups. // For now, a single notification per tab received will suffice. - logger.debug("Showing ${event.entries.size} tab(s) received from deviceID=${event.from?.id}") - event.entries.forEach { tab -> + logger.debug("Showing ${tabs.size} tab(s) received from deviceID=${device?.id}") + tabs.forEach { tab -> val intent = Intent(Intent.ACTION_VIEW, Uri.parse(tab.url)) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.putExtra(RECEIVE_TABS_TAG, true) @@ -62,7 +62,7 @@ class NotificationManager(private val context: Context) { val builder = NotificationCompat.Builder(context, RECEIVE_TABS_CHANNEL_ID) .setSmallIcon(R.drawable.ic_status_logo) - .setTitle(event, tab) + .setSendTabTitle(context, device, tab) .setWhen(System.currentTimeMillis()) .setContentText(tab.url) .setContentIntent(pendingIntent) @@ -104,12 +104,18 @@ class NotificationManager(private val context: Context) { notificationManager.createNotificationChannel(channel) } - private fun NotificationCompat.Builder.setTitle( - event: DeviceEvent.TabReceived, + private fun NotificationCompat.Builder.setSendTabTitle( + context: Context, + device: Device?, tab: TabData ): NotificationCompat.Builder { - event.from?.let { device -> - setContentTitle(context.getString(R.string.fxa_tab_received_from_notification_name, device.displayName)) + device?.let { + setContentTitle( + context.getString( + R.string.fxa_tab_received_from_notification_name, + it.displayName + ) + ) return this } diff --git a/app/src/test/java/org/mozilla/fenix/components/BackgroundServicesTest.kt b/app/src/test/java/org/mozilla/fenix/components/BackgroundServicesTest.kt index ace9dbde9..4af70eeb5 100644 --- a/app/src/test/java/org/mozilla/fenix/components/BackgroundServicesTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/BackgroundServicesTest.kt @@ -15,7 +15,6 @@ import mozilla.components.concept.sync.AuthType import mozilla.components.concept.sync.OAuthAccount import mozilla.components.feature.push.AutoPushFeature import mozilla.components.feature.push.PushConfig -import mozilla.components.feature.push.PushType import mozilla.components.service.fxa.DeviceConfig import mozilla.components.service.fxa.ServerConfig import mozilla.components.service.fxa.SyncConfig @@ -76,53 +75,6 @@ class BackgroundServicesTest { assertNull(backgroundServices.syncConfig) } - @Test - fun `push account observer`() { - val push = mockk() - val observer = PushAccountObserver(push) - val registry = ObserverRegistry() - registry.register(observer) - val account = mockk() - - // Being explicit here (vs using 'any()') ensures that any change to which PushType variants - // are being subscribed/unsubscribed will break these tests, forcing developer to expand them. - every { push.subscribeForType(PushType.Services) } just Runs - every { push.unsubscribeForType(PushType.Services) } just Runs - - // 'Existing' auth type doesn't trigger subscription - we're already subscribed. - registry.notifyObservers { onAuthenticated(account, AuthType.Existing) } - verify(exactly = 0) { push.subscribeForType(any()) } - - // Every other auth type does. - registry.notifyObservers { onAuthenticated(account, AuthType.Signin) } - verify(exactly = 1) { push.subscribeForType(eq(PushType.Services)) } - - registry.notifyObservers { onAuthenticated(account, AuthType.Signup) } - verify(exactly = 2) { push.subscribeForType(eq(PushType.Services)) } - - registry.notifyObservers { onAuthenticated(account, AuthType.Recovered) } - verify(exactly = 3) { push.subscribeForType(eq(PushType.Services)) } - - registry.notifyObservers { onAuthenticated(account, AuthType.Shared) } - verify(exactly = 4) { push.subscribeForType(eq(PushType.Services)) } - - registry.notifyObservers { onAuthenticated(account, AuthType.Pairing) } - verify(exactly = 5) { push.subscribeForType(eq(PushType.Services)) } - - registry.notifyObservers { onAuthenticated(account, AuthType.OtherExternal(null)) } - verify(exactly = 6) { push.subscribeForType(eq(PushType.Services)) } - - registry.notifyObservers { onAuthenticated(account, AuthType.OtherExternal("someAction")) } - verify(exactly = 7) { push.subscribeForType(eq(PushType.Services)) } - - // None of the above unsubscribed. - verify(exactly = 0) { push.unsubscribeForType(any()) } - - // Finally, log-out should unsubscribe. - registry.notifyObservers { onLoggedOut() } - verify(exactly = 1) { push.unsubscribeForType(eq(PushType.Services)) } - } - @Test fun `telemetry account observer`() { val metrics = mockk()