From 6a8d0f324e2c94377480e54b566aacbfc8a9b1b9 Mon Sep 17 00:00:00 2001 From: Grisha Kruglov Date: Thu, 23 May 2019 13:13:27 -0700 Subject: [PATCH] Closes #2770: Allow receiving tabs from FxA devices (#2772) * Closes #2770: Allow receiving tabs from FxA devices Now that we're on a-c 0.54, we can land this since it supports device capability migration. This patch adds a SEND_TAB device capability, making Fenix a valid target in the Send Tab device list on Desktop Firefox. Additionally, it adds a notification manager which manages notification channels and knows how to display "received tabs" notifications". * Post: remove unusued test file that's causing issues --- CHANGELOG.md | 1 + .../fenix/components/BackgroundServices.kt | 18 +++- .../mozilla/fenix/components/Components.kt | 4 +- .../fenix/components/NotificationManager.kt | 87 +++++++++++++++++++ .../org/mozilla/fenix/components/Utilities.kt | 7 ++ .../fenix/settings/AccountSettingsFragment.kt | 8 ++ app/src/main/res/values/strings.xml | 6 ++ .../components/TestBackgroundServices.kt | 18 ---- 8 files changed, 128 insertions(+), 21 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/components/NotificationManager.kt delete mode 100644 app/src/test/java/org/mozilla/fenix/components/TestBackgroundServices.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e5c6e113..af0023fe9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- #2770 - Added ability to receive tabs from other FxA devices - #919 - Enabled bookmark synchronization - #916 - Added the ability to save and delete bookmarks - #356 - Added the ability to delete history 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 38351b6f2..e36f920ca 100644 --- a/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt +++ b/app/src/main/java/org/mozilla/fenix/components/BackgroundServices.kt @@ -5,11 +5,15 @@ package org.mozilla.fenix.components import android.content.Context +import androidx.lifecycle.ProcessLifecycleOwner import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import mozilla.components.browser.storage.sync.PlacesBookmarksStorage import mozilla.components.browser.storage.sync.PlacesHistoryStorage +import mozilla.components.concept.sync.DeviceCapability +import mozilla.components.concept.sync.DeviceEvent +import mozilla.components.concept.sync.DeviceEventsObserver import mozilla.components.concept.sync.DeviceType import mozilla.components.feature.sync.BackgroundSyncManager import mozilla.components.feature.sync.GlobalSyncableStoreProvider @@ -26,7 +30,8 @@ import org.mozilla.fenix.test.Mockable class BackgroundServices( context: Context, historyStorage: PlacesHistoryStorage, - bookmarkStorage: PlacesBookmarksStorage + bookmarkStorage: PlacesBookmarksStorage, + notificationManager: NotificationManager ) { companion object { const val CLIENT_ID = "a2270f727f45f648" @@ -51,13 +56,22 @@ class BackgroundServices( it.addStore("bookmarks") } + private val deviceEventObserver = object : DeviceEventsObserver { + override fun onEvents(events: List) { + events.filter { it is DeviceEvent.TabReceived }.forEach { + notificationManager.showReceivedTabs(it as DeviceEvent.TabReceived) + } + } + } + val accountManager = FxaAccountManager( context, config, scopes, - DeviceTuple("Fenix", DeviceType.MOBILE, emptyList()), + DeviceTuple("Fenix", DeviceType.MOBILE, listOf(DeviceCapability.SEND_TAB)), syncManager ).also { + it.registerForDeviceEvents(deviceEventObserver, ProcessLifecycleOwner.get(), true) CoroutineScope(Dispatchers.Main).launch { it.initAsync().await() } } } diff --git a/app/src/main/java/org/mozilla/fenix/components/Components.kt b/app/src/main/java/org/mozilla/fenix/components/Components.kt index 710cd09cc..ec278249a 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Components.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Components.kt @@ -12,7 +12,9 @@ import org.mozilla.fenix.test.Mockable */ @Mockable class Components(private val context: Context) { - val backgroundServices by lazy { BackgroundServices(context, core.historyStorage, core.bookmarksStorage) } + val backgroundServices by lazy { + BackgroundServices(context, core.historyStorage, core.bookmarksStorage, utils.notificationManager) + } val services by lazy { Services(backgroundServices.accountManager, useCases.tabsUseCases) } val core by lazy { Core(context) } val search by lazy { Search(context) } diff --git a/app/src/main/java/org/mozilla/fenix/components/NotificationManager.kt b/app/src/main/java/org/mozilla/fenix/components/NotificationManager.kt new file mode 100644 index 000000000..772a34943 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/components/NotificationManager.kt @@ -0,0 +1,87 @@ +/* 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.TargetApi +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import mozilla.components.concept.sync.DeviceEvent +import org.mozilla.fenix.R + +/** + * Manages notification channels and allows displaying different types of notifications. + */ +class NotificationManager(private val context: Context) { + companion object { + const val RECEIVE_TABS_TAG = "ReceivedTabs" + const val RECEIVE_TABS_CHANNEL_ID = "ReceivedTabsChannel" + } + + init { + // Create the notification channels we are going to use, but only on API 26+ because the NotificationChannel + // class is new and not in the support library. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel( + RECEIVE_TABS_CHANNEL_ID, + // Pick 'high' because this is a user-triggered action that is expected to be part of a continuity flow. + // That is, user is expected to be waiting for this notification on their device; make it obvious. + NotificationManager.IMPORTANCE_HIGH, + // Name and description are shown in the 'app notifications' settings for the app. + context.getString(R.string.fxa_received_tab_channel_name), + context.getString(R.string.fxa_received_tab_channel_description) + ) + } + } + + fun showReceivedTabs(event: DeviceEvent.TabReceived) { + // 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. + event.entries.forEach { tab -> + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(tab.url)) + val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, 0) + + val builder = NotificationCompat.Builder(context, RECEIVE_TABS_CHANNEL_ID) + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle(tab.title) + .setContentText(tab.url) + .setContentIntent(pendingIntent) + // Explicitly set a priority for Last synced: never + + + Received tabs + + Notifications for tabs received from other Firefox devices. + Tracking Protection diff --git a/app/src/test/java/org/mozilla/fenix/components/TestBackgroundServices.kt b/app/src/test/java/org/mozilla/fenix/components/TestBackgroundServices.kt deleted file mode 100644 index 89be247a4..000000000 --- a/app/src/test/java/org/mozilla/fenix/components/TestBackgroundServices.kt +++ /dev/null @@ -1,18 +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 android.content.Context -import mozilla.components.browser.storage.sync.PlacesBookmarksStorage -import mozilla.components.browser.storage.sync.PlacesHistoryStorage -import mozilla.components.feature.sync.BackgroundSyncManager - -class TestBackgroundServices( - context: Context, - historyStorage: PlacesHistoryStorage, - bookmarksStorage: PlacesBookmarksStorage -) : BackgroundServices(context, historyStorage, bookmarksStorage) { - override val syncManager = BackgroundSyncManager("") -}