diff --git a/app/metrics.yaml b/app/metrics.yaml
index c4264aa84..818cf629d 100644
--- a/app/metrics.yaml
+++ b/app/metrics.yaml
@@ -447,6 +447,56 @@ events:
expires: 113
onboarding:
+ syn_cfr_shown:
+ type: event
+ description: |
+ The Sync Onboarding CFR was shown to the user
+ bugs:
+ - https://github.com/mozilla-mobile/fenix/issues/26489
+ data_reviews:
+ - https://github.com/mozilla-mobile/fenix/pull/26507
+ data_sensitivity:
+ - interaction
+ notification_emails:
+ - android-probes@mozilla.com
+ expires: 119
+ metadata:
+ tags:
+ - Onboarding
+ sync_cfr_implicit_dismissal:
+ type: event
+ description: |
+ The Sync Onboarding CFR was dismissed by the user by interacting
+ with the outside of the popup.
+ bugs:
+ - https://github.com/mozilla-mobile/fenix/issues/26489
+ data_reviews:
+ - https://github.com/mozilla-mobile/fenix/pull/26507
+ data_sensitivity:
+ - interaction
+ notification_emails:
+ - android-probes@mozilla.com
+ expires: 119
+ metadata:
+ tags:
+ - Onboarding
+ sync_cfr_explicit_dismissal:
+ type: event
+ description: |
+ The Sync Onboarding CFR was dismissed by the user by clicking on
+ the "X" button to close the popup.
+ bugs:
+ - https://github.com/mozilla-mobile/fenix/issues/26489
+ data_reviews:
+ - https://github.com/mozilla-mobile/fenix/pull/26507
+ data_sensitivity:
+ - interaction
+ notification_emails:
+ - android-probes@mozilla.com
+ expires: 119
+ metadata:
+ tags:
+ - Onboarding
fxa_auto_signin:
type: event
description:
diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt
index 17d4e172a..89839a790 100644
--- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt
+++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt
@@ -23,6 +23,7 @@ import org.mozilla.fenix.home.OnboardingState
import org.mozilla.fenix.home.recentbookmarks.RecentBookmark
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem
import org.mozilla.fenix.onboarding.JumpBackInCFRDialog
+import org.mozilla.fenix.onboarding.SyncCFRPresenter
import org.mozilla.fenix.utils.Settings
// This method got a little complex with the addition of the tab tray feature flag
@@ -198,6 +199,14 @@ class SessionControlView(
super.onLayoutCompleted(state)
JumpBackInCFRDialog(view).showIfNeeded()
+
+ if (context.settings().showSyncCFR) {
+ SyncCFRPresenter(
+ context = context,
+ recyclerView = view,
+ ).showSyncCFR()
+ context.settings().showSyncCFR = false
+ }
}
}
}
diff --git a/app/src/main/java/org/mozilla/fenix/onboarding/SyncCFRPresenter.kt b/app/src/main/java/org/mozilla/fenix/onboarding/SyncCFRPresenter.kt
new file mode 100644
index 000000000..0f27a9041
--- /dev/null
+++ b/app/src/main/java/org/mozilla/fenix/onboarding/SyncCFRPresenter.kt
@@ -0,0 +1,75 @@
+/* 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.onboarding
+
+import android.content.Context
+import android.view.View
+import androidx.compose.ui.unit.dp
+import androidx.recyclerview.widget.RecyclerView
+import mozilla.components.service.glean.private.NoExtras
+import org.mozilla.fenix.GleanMetrics.Onboarding
+import org.mozilla.fenix.R
+import org.mozilla.fenix.compose.cfr.CFRPopup
+import org.mozilla.fenix.compose.cfr.CFRPopupProperties
+import org.mozilla.fenix.home.recentsyncedtabs.view.RecentSyncedTabViewHolder
+
+/**
+ * Vertical padding needed to improve the visual alignment of the popup and respect the UX design.
+ */
+private const val CFR_TO_ANCHOR_VERTICAL_PADDING = -16
+
+/**
+ * Delegate for handling sync onboarding CFR.
+ *
+ * @param [Context] used for various Android interactions.
+ * @param [RecyclerView] will serve as anchor for the sync CFR.
+ */
+class SyncCFRPresenter(
+ private val context: Context,
+ private val recyclerView: RecyclerView,
+) {
+
+ private var syncCFR: CFRPopup? = null
+
+ /**
+ * Check if [view] is available to show sync CFR.
+ */
+ fun showSyncCFR() {
+ findSyncTabsView()?.let {
+ CFRPopup(
+ text = context.getString(R.string.sync_cfr_message),
+ anchor = it,
+ properties = CFRPopupProperties(
+ indicatorDirection = CFRPopup.IndicatorDirection.DOWN,
+ popupVerticalOffset = CFR_TO_ANCHOR_VERTICAL_PADDING.dp,
+ ),
+ onDismiss = {
+ when (it) {
+ true -> Onboarding.syncCfrExplicitDismissal.record(NoExtras())
+ false -> Onboarding.syncCfrImplicitDismissal.record(NoExtras())
+ }
+ }
+ ) {
+ }.apply {
+ syncCFR = this
+ show()
+ Onboarding.synCfrShown.record(NoExtras())
+ }
+ }
+ }
+
+ private fun findSyncTabsView(): View? {
+ val count = recyclerView.adapter?.itemCount ?: return null
+
+ for (index in count downTo 0) {
+ val viewHolder = recyclerView.findViewHolderForAdapterPosition(index)
+ if (viewHolder is RecentSyncedTabViewHolder) {
+ return viewHolder.composeView
+ }
+ }
+
+ return null
+ }
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 99ab2a476..f8bc7c04a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -281,6 +281,8 @@
Sign in
Skip
+
+ Your tabs are syncing! Pick up where you left off on your other device.