diff --git a/app/build.gradle b/app/build.gradle
index 285f0d57d..1b66a53fd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -182,6 +182,7 @@ dependencies {
implementation Deps.mozilla_concept_toolbar
implementation Deps.mozilla_concept_storage
implementation Deps.mozilla_concept_sync
+ implementation Deps.mozilla_concept_push
implementation Deps.mozilla_browser_engine_gecko_nightly
@@ -206,11 +207,13 @@ dependencies {
implementation Deps.mozilla_feature_sitepermissions
implementation Deps.mozilla_feature_intent
implementation Deps.mozilla_feature_search
+ implementation Deps.mozilla_feature_sendtab
implementation Deps.mozilla_feature_session
implementation Deps.mozilla_feature_toolbar
implementation Deps.mozilla_feature_tabs
implementation Deps.mozilla_feature_downloads
implementation Deps.mozilla_feature_prompts
+ implementation Deps.mozilla_feature_push
implementation Deps.mozilla_feature_pwa
implementation Deps.mozilla_feature_qr
implementation Deps.mozilla_feature_readerview
@@ -228,7 +231,10 @@ dependencies {
implementation Deps.mozilla_support_ktx
implementation Deps.mozilla_support_rustlog
implementation Deps.mozilla_support_rusthttp
+
implementation Deps.mozilla_lib_crash
+ implementation Deps.mozilla_lib_push_firebase
+
implementation Deps.thirdparty_sentry
implementation Deps.kotlin_stdlib
@@ -236,8 +242,8 @@ dependencies {
implementation Deps.androidx_appcompat
implementation Deps.androidx_constraintlayout
- implementation Deps.androidx_preference
- implementation Deps.androidx_work_runtime
+ implementation Deps.androidx_preference_ktx
+ implementation Deps.androidx_work_runtime_ktx
implementation Deps.google_material
androidTestImplementation Deps.uiautomator
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3d113c59e..d33e0a548 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -104,6 +104,21 @@
android:label="@string/settings"
android:theme="@style/AppTheme"
android:parentActivityName=".BrowserActivity" />
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/org/mozilla/reference/browser/BrowserActivity.kt b/app/src/main/java/org/mozilla/reference/browser/BrowserActivity.kt
index b17641f2c..7c3d04445 100644
--- a/app/src/main/java/org/mozilla/reference/browser/BrowserActivity.kt
+++ b/app/src/main/java/org/mozilla/reference/browser/BrowserActivity.kt
@@ -25,7 +25,6 @@ import org.mozilla.reference.browser.browser.CrashIntegration
import org.mozilla.reference.browser.ext.components
import org.mozilla.reference.browser.ext.isCrashReportActive
import org.mozilla.reference.browser.tabs.TabsTouchHelper
-import org.mozilla.reference.browser.telemetry.DataReportingNotification
/**
* Activity that holds the [BrowserFragment].
@@ -61,7 +60,7 @@ open class BrowserActivity : AppCompatActivity() {
lifecycle.addObserver(crashIntegration)
}
- DataReportingNotification.checkAndNotifyPolicy(this)
+ NotificationManager.checkAndNotifyPolicy(this)
}
override fun onBackPressed() {
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 b984e5ed3..c50b819b7 100644
--- a/app/src/main/java/org/mozilla/reference/browser/BrowserApplication.kt
+++ b/app/src/main/java/org/mozilla/reference/browser/BrowserApplication.kt
@@ -5,6 +5,7 @@
package org.mozilla.reference.browser
import android.app.Application
+import mozilla.components.concept.push.PushProcessor
import mozilla.components.support.base.log.Log
import mozilla.components.support.base.log.sink.AndroidLogSink
import mozilla.components.support.ktx.android.content.isMainProcess
@@ -36,6 +37,10 @@ open class BrowserApplication : Application() {
components.analytics.initializeGlean()
components.analytics.initializeExperiments()
+
+ components.backgroundServices.pushFeature?.let {
+ PushProcessor.install(it)
+ }
}
override fun onTrimMemory(level: Int) {
diff --git a/app/src/main/java/org/mozilla/reference/browser/IntentRequestCodes.kt b/app/src/main/java/org/mozilla/reference/browser/IntentRequestCodes.kt
new file mode 100644
index 000000000..3c37121c7
--- /dev/null
+++ b/app/src/main/java/org/mozilla/reference/browser/IntentRequestCodes.kt
@@ -0,0 +1,12 @@
+/*
+ * 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.reference.browser
+
+object IntentRequestCodes {
+ const val REQUEST_CODE_DATA_REPORTING = 0
+ const val REQUEST_CODE_SEND_TAB = 1
+}
diff --git a/app/src/main/java/org/mozilla/reference/browser/NotificationManager.kt b/app/src/main/java/org/mozilla/reference/browser/NotificationManager.kt
new file mode 100644
index 000000000..9f2fa73df
--- /dev/null
+++ b/app/src/main/java/org/mozilla/reference/browser/NotificationManager.kt
@@ -0,0 +1,219 @@
+/* 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.reference.browser
+
+import android.annotation.TargetApi
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager as AndroidNotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.content.SharedPreferences
+import android.net.Uri
+import android.os.Build
+import android.os.Build.VERSION.SDK_INT
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import androidx.preference.PreferenceManager
+import mozilla.components.concept.sync.Device
+import mozilla.components.concept.sync.TabData
+import mozilla.components.support.base.log.logger.Logger
+import org.mozilla.reference.browser.IntentRequestCodes.REQUEST_CODE_DATA_REPORTING
+import org.mozilla.reference.browser.IntentRequestCodes.REQUEST_CODE_SEND_TAB
+
+/**
+ * Manages notification channels and allows displaying different supported types of notifications.
+ */
+object NotificationManager {
+
+ // Send Tab
+ private const val RECEIVE_TABS_TAG = "org.mozilla.reference.browser.receivedTabs"
+ private const val RECEIVE_TABS_CHANNEL_ID = "org.mozilla.reference.browser.ReceivedTabsChannel"
+
+ // Data Reporting
+ private const val PRIVACY_NOTICE_URL = "https://www.mozilla.org/en-US/privacy/firefox/"
+ private const val DATA_REPORTING_VERSION = 1
+ private const val DATA_REPORTING_TAG = "org.mozilla.reference.browser.DataReporting"
+ private const val DATA_REPORTING_NOTIFICATION_ID = 1
+ private const val PREFS_POLICY_VERSION = "datareporting.policy.dataSubmissionPolicyVersion"
+ private const val PREFS_POLICY_NOTIFIED_TIME =
+ "datareporting.policy.dataSubmissionPolicyNotifiedTime"
+
+ // Default
+ private const val NOTIFICATION_CHANNEL_ID = "default-notification-channel"
+
+ // Use an incrementing notification ID since they have the same tag.
+ private var notificationIdCount = 0
+ private val logger = Logger("NotificationManager")
+
+ 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 ${tabs.size} tab(s) received from deviceID=${device?.id}")
+
+ tabs.forEach { tab ->
+ val intent = Intent(Intent.ACTION_VIEW, Uri.parse(tab.url)).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ }
+ val pendingIntent: PendingIntent = PendingIntent.getActivity(
+ context,
+ REQUEST_CODE_SEND_TAB,
+ intent,
+ PendingIntent.FLAG_ONE_SHOT
+ )
+ val importance = if (SDK_INT >= Build.VERSION_CODES.N) {
+ // We pick 'IMPORTANCE_HIGH' priority 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.
+ AndroidNotificationManager.IMPORTANCE_HIGH
+ } else {
+ null
+ }
+ val channelId = getNotificationChannelId(
+ context,
+ RECEIVE_TABS_CHANNEL_ID,
+ context.getString(R.string.fxa_received_tab_channel_name),
+ context.getString(R.string.fxa_received_tab_channel_description),
+ importance
+ )
+
+ val builder = NotificationCompat.Builder(context, channelId)
+ .setSmallIcon(R.drawable.ic_notification)
+ .setSendTabTitle(context, device, tab)
+ .setWhen(System.currentTimeMillis())
+ .setContentText(tab.url)
+ .setContentIntent(pendingIntent)
+ .setAutoCancel(true)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setDefaults(Notification.DEFAULT_VIBRATE or Notification.DEFAULT_SOUND)
+ .setCategory(NotificationCompat.CATEGORY_REMINDER)
+
+ NotificationManagerCompat.from(context).notify(
+ RECEIVE_TABS_TAG,
+ notificationIdCount++,
+ builder.build()
+ )
+ }
+ }
+
+ fun checkAndNotifyPolicy(context: Context) {
+ val preferences = PreferenceManager.getDefaultSharedPreferences(context)
+ val currentVersion = preferences.getInt(PREFS_POLICY_VERSION, -1)
+
+ if (currentVersion < 1) {
+ // This is a first run, so notify user about data policy.
+ notifyDataPolicy(context, preferences)
+ }
+ }
+
+ /**
+ * Launch a notification of the data policy, and record notification time and version.
+ */
+ private fun notifyDataPolicy(context: Context, preferences: SharedPreferences) {
+ val resources = context.resources
+
+ val notificationTitle = resources.getString(R.string.datareporting_notification_title)
+ val notificationSummary = resources.getString(R.string.datareporting_notification_summary)
+
+ val intent = Intent(Intent.ACTION_VIEW).apply {
+ data = Uri.parse(PRIVACY_NOTICE_URL)
+ setPackage(context.packageName)
+ }
+
+ val pendingIntent =
+ PendingIntent.getActivity(context, REQUEST_CODE_DATA_REPORTING, intent, 0)
+
+ val notificationBuilder = NotificationCompat.Builder(
+ context,
+ getNotificationChannelId(context)
+ ).apply {
+ setContentTitle(notificationTitle)
+ setContentText(notificationSummary)
+ setSmallIcon(R.drawable.ic_notification)
+ setAutoCancel(true)
+ setContentIntent(pendingIntent)
+ setStyle(NotificationCompat.BigTextStyle().bigText(notificationSummary))
+ }
+
+ NotificationManagerCompat.from(context)
+ .notify(DATA_REPORTING_TAG, DATA_REPORTING_NOTIFICATION_ID, notificationBuilder.build())
+
+ preferences.edit()
+ .putLong(PREFS_POLICY_NOTIFIED_TIME, System.currentTimeMillis())
+ .putInt(PREFS_POLICY_VERSION, DATA_REPORTING_VERSION)
+ .apply()
+ }
+
+ private fun getNotificationChannelId(
+ context: Context,
+ channelId: String = NOTIFICATION_CHANNEL_ID,
+ channelName: String = context.resources.getString(R.string.default_notification_channel),
+ description: String? = null,
+ channelImportance: Int? = null
+ ): String {
+ if (SDK_INT >= Build.VERSION_CODES.O) {
+ val importance = channelImportance ?: AndroidNotificationManager.IMPORTANCE_DEFAULT
+ createNotificationChannelIfNeeded(
+ context,
+ channelId,
+ channelName,
+ description,
+ importance
+ )
+ }
+
+ return channelId
+ }
+
+ @TargetApi(Build.VERSION_CODES.O)
+ private fun createNotificationChannelIfNeeded(
+ context: Context,
+ channelId: String,
+ channelName: String,
+ channelDescription: String?,
+ importance: Int = AndroidNotificationManager.IMPORTANCE_DEFAULT
+ ) {
+ val notificationManager =
+ context.getSystemService(Context.NOTIFICATION_SERVICE) as AndroidNotificationManager
+
+ if (null != notificationManager.getNotificationChannel(channelId)) {
+ return
+ }
+
+ val channel = NotificationChannel(
+ channelId,
+ channelName,
+ importance
+ ).apply {
+ description = channelDescription
+ }
+
+ notificationManager.createNotificationChannel(channel)
+ }
+
+ private fun NotificationCompat.Builder.setSendTabTitle(
+ context: Context,
+ device: Device?,
+ tab: TabData
+ ): NotificationCompat.Builder {
+ device?.let {
+ setContentTitle(
+ context.getString(
+ R.string.fxa_tab_received_from_notification_name,
+ it.displayName
+ )
+ )
+ return this
+ }
+
+ if (tab.title.isEmpty()) {
+ setContentTitle(context.getString(R.string.fxa_tab_received_notification_name))
+ } else {
+ setContentTitle(tab.title)
+ }
+ return this
+ }
+}
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 c5818eeec..f06c8547b 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
@@ -6,18 +6,27 @@ package org.mozilla.reference.browser.components
import android.content.Context
import android.os.Build
+import androidx.lifecycle.ProcessLifecycleOwner
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import mozilla.components.browser.storage.sync.PlacesHistoryStorage
+import mozilla.components.concept.sync.AccountObserver
+import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.DeviceCapability
import mozilla.components.concept.sync.DeviceType
+import mozilla.components.concept.sync.OAuthAccount
+import mozilla.components.feature.push.AutoPushFeature
+import mozilla.components.feature.push.PushConfig
+import mozilla.components.feature.sendtab.SendTabFeature
import mozilla.components.service.fxa.DeviceConfig
import mozilla.components.service.fxa.ServerConfig
import mozilla.components.service.fxa.SyncConfig
import mozilla.components.service.fxa.SyncEngine
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.service.fxa.sync.GlobalSyncableStoreProvider
+import org.mozilla.reference.browser.push.FirebasePush
+import org.mozilla.reference.browser.NotificationManager
/**
* Component group for background services. These are components that need to be accessed from
@@ -48,18 +57,65 @@ class BackgroundServices(
syncPeriodInMinutes = 240L
) // four hours
- val accountManager = FxaAccountManager(
- context,
- serverConfig,
- deviceConfig,
- syncConfig,
- // We don't need to specify this explicitly, but `syncConfig` may be disabled due to an 'experiments'
- // flag. In that case, sync scope necessary for syncing won't be acquired during authentication
- // unless we explicitly specify it below.
- // This is a good example of an information leak at the API level.
- // See https://github.com/mozilla-mobile/android-components/issues/3732
- setOf("https://identity.mozilla.com/apps/oldsync")
- ).also {
- CoroutineScope(Dispatchers.Main).launch { it.initAsync().await() }
+ val accountManager by lazy {
+ FxaAccountManager(
+ context,
+ serverConfig,
+ deviceConfig,
+ syncConfig,
+ // We don't need to specify this explicitly, but `syncConfig` may be disabled due to an 'experiments'
+ // flag. In that case, sync scope necessary for syncing won't be acquired during authentication
+ // unless we explicitly specify it below.
+ // This is a good example of an information leak at the API level.
+ // See https://github.com/mozilla-mobile/android-components/issues/3732
+ setOf("https://identity.mozilla.com/apps/oldsync")
+ ).also {
+ // We don't need the push service unless we're signed in.
+ it.register(pushServiceObserver, ProcessLifecycleOwner.get(), false)
+
+ // Initializing the feature allows it to start observing events as needed.
+ SendTabFeature(it, pushFeature) { device, tabs ->
+ NotificationManager.showReceivedTabs(context, device, tabs)
+ }
+
+ CoroutineScope(Dispatchers.Main).launch { it.initAsync().await() }
+ }
+ }
+
+ val pushFeature by lazy {
+ pushConfig?.let { config ->
+ AutoPushFeature(context, pushService, config)
+ }
+ }
+
+ /**
+ * The push configuration data class used to initialize the AutoPushFeature.
+ *
+ * If we have the `project_id` resource, then we know that the Firebase configuration and API
+ * keys are available for the FCM service to be used.
+ */
+ private val pushConfig by lazy {
+ val resId = context.resources.getIdentifier("project_id", "string", context.packageName)
+ if (resId == 0) {
+ return@lazy null
+ }
+ val projectId = context.resources.getString(resId)
+ PushConfig(projectId)
+ }
+
+ private val pushService by lazy { FirebasePush() }
+
+ private val pushServiceObserver by lazy {
+ object : AccountObserver {
+ override fun onAuthenticated(account: OAuthAccount, authType: AuthType) {
+ if (authType != AuthType.Existing) {
+ pushService.start(context)
+ }
+ }
+
+ override fun onLoggedOut() {
+ pushService.stop()
+ }
+ }
}
}
diff --git a/app/src/main/java/org/mozilla/reference/browser/push/FirebasePush.kt b/app/src/main/java/org/mozilla/reference/browser/push/FirebasePush.kt
new file mode 100644
index 000000000..69f62c4af
--- /dev/null
+++ b/app/src/main/java/org/mozilla/reference/browser/push/FirebasePush.kt
@@ -0,0 +1,9 @@
+/* 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.reference.browser.push
+
+import mozilla.components.lib.push.firebase.AbstractFirebasePushService
+
+class FirebasePush : AbstractFirebasePushService()
diff --git a/app/src/main/java/org/mozilla/reference/browser/telemetry/DataReportingNotification.kt b/app/src/main/java/org/mozilla/reference/browser/telemetry/DataReportingNotification.kt
deleted file mode 100644
index c65a8c2e7..000000000
--- a/app/src/main/java/org/mozilla/reference/browser/telemetry/DataReportingNotification.kt
+++ /dev/null
@@ -1,101 +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.reference.browser.telemetry
-
-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.content.SharedPreferences
-import android.net.Uri
-import android.os.Build
-import android.preference.PreferenceManager
-import androidx.core.app.NotificationCompat
-import androidx.core.app.NotificationManagerCompat
-import org.mozilla.reference.browser.R
-
-/**
- * Data Reporting notification to be shown on the first app start and whenever the data policy version changes.
- */
-object DataReportingNotification {
- private const val PREFS_POLICY_NOTIFIED_TIME = "datareporting.policy.dataSubmissionPolicyNotifiedTime"
- private const val PREFS_POLICY_VERSION = "datareporting.policy.dataSubmissionPolicyVersion"
-
- private const val PRIVACY_NOTICE_URL = "https://www.mozilla.org/en-US/privacy/firefox/"
-
- private const val DATA_REPORTING_VERSION = 1
-
- private const val NOTIFICATION_ID = 1
-
- private const val NOTIFICATION_CHANNEL_ID = "default-notification-channel"
-
- fun checkAndNotifyPolicy(context: Context) {
- val preferences = PreferenceManager.getDefaultSharedPreferences(context)
- val currentVersion = preferences.getInt(PREFS_POLICY_VERSION, -1)
-
- if (currentVersion < 1) {
- // This is a first run, so notify user about data policy.
- notifyDataPolicy(context, preferences)
- }
- }
-
- /**
- * Launch a notification of the data policy, and record notification time and version.
- */
- private fun notifyDataPolicy(context: Context, preferences: SharedPreferences) {
- val resources = context.resources
-
- val notificationTitle = resources.getString(R.string.datareporting_notification_title)
- val notificationSummary = resources.getString(R.string.datareporting_notification_summary)
-
- val intent = Intent(Intent.ACTION_VIEW).apply {
- data = Uri.parse(PRIVACY_NOTICE_URL)
- setPackage(context.packageName)
- }
-
- val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
-
- val notificationBuilder = NotificationCompat.Builder(context, getNotificationChannelId(context))
- .setContentTitle(notificationTitle)
- .setContentText(notificationSummary)
- .setSmallIcon(R.drawable.ic_notification)
- .setAutoCancel(true)
- .setContentIntent(pendingIntent)
- .setStyle(NotificationCompat.BigTextStyle().bigText(notificationSummary))
-
- NotificationManagerCompat.from(context)
- .notify(NOTIFICATION_ID, notificationBuilder.build())
-
- preferences.edit()
- .putLong(PREFS_POLICY_NOTIFIED_TIME, System.currentTimeMillis())
- .putInt(PREFS_POLICY_VERSION, DATA_REPORTING_VERSION)
- .apply()
- }
-
- private fun getNotificationChannelId(context: Context): String {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- createNotificationChannelIfNeeded(context)
- }
-
- return NOTIFICATION_CHANNEL_ID
- }
-
- @TargetApi(Build.VERSION_CODES.O)
- private fun createNotificationChannelIfNeeded(context: Context) {
- val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- if (null != notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID)) {
- return
- }
-
- val channel = NotificationChannel(
- NOTIFICATION_CHANNEL_ID,
- context.resources.getString(R.string.default_notification_channel),
- NotificationManager.IMPORTANCE_DEFAULT)
-
- notificationManager.createNotificationChannel(channel)
- }
-}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 17fb91d62..e92469120 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -155,4 +155,14 @@
Share with…
Copied!
+
+
+ Received tabs
+
+ Notifications for tabs received from other Firefox devices.
+
+ Tab Received
+
+ Tab from %s
+
diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt
index 0254c9b2e..aa4fffa04 100644
--- a/buildSrc/src/main/java/Dependencies.kt
+++ b/buildSrc/src/main/java/Dependencies.kt
@@ -39,6 +39,7 @@ object Deps {
const val mozilla_concept_toolbar = "org.mozilla.components:concept-toolbar:${Versions.mozilla_android_components}"
const val mozilla_concept_storage = "org.mozilla.components:concept-storage:${Versions.mozilla_android_components}"
const val mozilla_concept_sync = "org.mozilla.components:concept-sync:${Versions.mozilla_android_components}"
+ const val mozilla_concept_push = "org.mozilla.components:concept-push:${Versions.mozilla_android_components}"
const val mozilla_browser_awesomebar = "org.mozilla.components:browser-awesomebar:${Versions.mozilla_android_components}"
const val mozilla_browser_engine_gecko = "org.mozilla.components:browser-engine-gecko:${Versions.mozilla_android_components}"
@@ -65,12 +66,14 @@ object Deps {
const val mozilla_feature_sitepermissions = "org.mozilla.components:feature-sitepermissions:${Versions.mozilla_android_components}"
const val mozilla_feature_intent = "org.mozilla.components:feature-intent:${Versions.mozilla_android_components}"
const val mozilla_feature_search = "org.mozilla.components:feature-search:${Versions.mozilla_android_components}"
+ const val mozilla_feature_sendtab = "org.mozilla.components:feature-sendtab:${Versions.mozilla_android_components}"
const val mozilla_feature_session = "org.mozilla.components:feature-session:${Versions.mozilla_android_components}"
const val mozilla_feature_toolbar = "org.mozilla.components:feature-toolbar:${Versions.mozilla_android_components}"
const val mozilla_feature_tabs = "org.mozilla.components:feature-tabs:${Versions.mozilla_android_components}"
const val mozilla_feature_downloads = "org.mozilla.components:feature-downloads:${Versions.mozilla_android_components}"
const val mozilla_feature_storage = "org.mozilla.components:feature-storage:${Versions.mozilla_android_components}"
const val mozilla_feature_prompts = "org.mozilla.components:feature-prompts:${Versions.mozilla_android_components}"
+ const val mozilla_feature_push = "org.mozilla.components:feature-push:${Versions.mozilla_android_components}"
const val mozilla_feature_pwa = "org.mozilla.components:feature-pwa:${Versions.mozilla_android_components}"
const val mozilla_feature_qr = "org.mozilla.components:feature-qr:${Versions.mozilla_android_components}"
const val mozilla_feature_readerview = "org.mozilla.components:feature-readerview:${Versions.mozilla_android_components}"
@@ -90,13 +93,14 @@ object Deps {
const val mozilla_support_rusthttp = "org.mozilla.components:support-rusthttp:${Versions.mozilla_android_components}"
const val mozilla_lib_crash = "org.mozilla.components:lib-crash:${Versions.mozilla_android_components}"
+ const val mozilla_lib_push_firebase = "org.mozilla.components:lib-push-firebase:${Versions.mozilla_android_components}"
const val thirdparty_sentry = "io.sentry:sentry-android:${Versions.thirdparty_sentry}"
const val androidx_appcompat = "androidx.appcompat:appcompat:${Versions.androidx_appcompat}"
const val androidx_constraintlayout = "androidx.constraintlayout:constraintlayout:${Versions.androidx_constraintlayout}"
- const val androidx_preference = "androidx.preference:preference-ktx:${Versions.androidx_preference}"
- const val androidx_work_runtime = "androidx.work:work-runtime-ktx:${Versions.workmanager}"
+ const val androidx_preference_ktx = "androidx.preference:preference-ktx:${Versions.androidx_preference}"
+ const val androidx_work_runtime_ktx = "androidx.work:work-runtime-ktx:${Versions.workmanager}"
const val google_material = "com.google.android.material:material:${Versions.google_material}"
const val tools_androidgradle = "com.android.tools.build:gradle:${Versions.android_gradle_plugin}"