diff --git a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt index d88ee4acadc2..fcb4d309be43 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt @@ -575,6 +575,7 @@ class BrowserTabViewModelTest { highlightsOnboardingExperimentManager = mockHighlightsOnboardingExperimentManager, brokenSitePrompt = mockBrokenSitePrompt, extendedOnboardingPixelsPlugin = extendedOnboardingPixelsPlugin, + userBrowserProperties = mockUserBrowserProperties, ) val siteFactory = SiteFactoryImpl( diff --git a/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaViewModelTest.kt b/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaViewModelTest.kt index 3da6c2439ff4..c254d2dd19ab 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaViewModelTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaViewModelTest.kt @@ -54,6 +54,7 @@ import com.duckduckgo.app.trackerdetection.model.TrackerType import com.duckduckgo.app.trackerdetection.model.TrackingEvent import com.duckduckgo.app.widget.ui.WidgetCapabilities import com.duckduckgo.brokensite.api.BrokenSitePrompt +import com.duckduckgo.browser.api.UserBrowserProperties import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.common.test.InstantSchedulersRule import com.duckduckgo.duckplayer.api.DuckPlayer @@ -124,6 +125,8 @@ class CtaViewModelTest { private val mockBrokenSitePrompt: BrokenSitePrompt = mock() + private val mockUserBrowserProperties: UserBrowserProperties = mock() + private val requiredDaxOnboardingCtas: List = listOf( CtaId.DAX_INTRO, CtaId.DAX_DIALOG_SERP, @@ -183,6 +186,7 @@ class CtaViewModelTest { highlightsOnboardingExperimentManager = mockHighlightsOnboardingExperimentManager, brokenSitePrompt = mockBrokenSitePrompt, extendedOnboardingPixelsPlugin = extendedOnboardingPixelsPlugin, + userBrowserProperties = mockUserBrowserProperties, ) } diff --git a/app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt b/app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt index 5858f4e9a25a..429c4902c5c2 100644 --- a/app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt @@ -41,6 +41,7 @@ import com.duckduckgo.app.onboarding.ui.page.extendedonboarding.HighlightsOnboar import com.duckduckgo.app.onboarding.ui.page.extendedonboarding.testPrivacyProOnboardingPrimaryButtonMetricPixel import com.duckduckgo.app.onboarding.ui.page.extendedonboarding.testPrivacyProOnboardingSecondaryButtonMetricPixel import com.duckduckgo.app.onboarding.ui.page.extendedonboarding.testPrivacyProOnboardingShownMetricPixel +import com.duckduckgo.app.pixels.AppPixelName import com.duckduckgo.app.pixels.AppPixelName.ONBOARDING_SKIP_MAJOR_NETWORK_UNIQUE import com.duckduckgo.app.privacy.db.UserAllowListRepository import com.duckduckgo.app.settings.db.SettingsDataStore @@ -49,6 +50,7 @@ import com.duckduckgo.app.statistics.pixels.Pixel.PixelType.Unique import com.duckduckgo.app.tabs.model.TabRepository import com.duckduckgo.app.widget.ui.WidgetCapabilities import com.duckduckgo.brokensite.api.BrokenSitePrompt +import com.duckduckgo.browser.api.UserBrowserProperties import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.duckplayer.api.DuckPlayer @@ -90,6 +92,7 @@ class CtaViewModel @Inject constructor( private val highlightsOnboardingExperimentManager: HighlightsOnboardingExperimentManager, private val brokenSitePrompt: BrokenSitePrompt, private val extendedOnboardingPixelsPlugin: ExtendedOnboardingPixelsPlugin, + private val userBrowserProperties: UserBrowserProperties, ) { @ExperimentalCoroutinesApi @VisibleForTesting @@ -158,6 +161,20 @@ class CtaViewModel @Inject constructor( } } } + + // Temporary pixel + val isVisitSiteSuggestionsCta = + cta is DaxBubbleCta.DaxIntroVisitSiteOptionsCta || cta is DaxBubbleCta.DaxExperimentIntroVisitSiteOptionsCta || + cta is OnboardingDaxDialogCta.DaxSiteSuggestionsCta || cta is OnboardingDaxDialogCta.DaxExperimentSiteSuggestionsCta + if (isVisitSiteSuggestionsCta) { + if (userBrowserProperties.daysSinceInstalled() <= MIN_DAYS_TO_COUNT_ONBOARDING_CTA_SHOWN) { + val count = onboardingStore.visitSiteCtaDisplayCount ?: 0 + pixel.fire(AppPixelName.ONBOARDING_VISIT_SITE_CTA_SHOWN, mapOf("count" to count.toString())) + onboardingStore.visitSiteCtaDisplayCount = count + 1 + } else { + onboardingStore.clearVisitSiteCtaDisplayCount() + } + } } suspend fun registerDaxBubbleCtaDismissed(cta: Cta) { @@ -572,5 +589,6 @@ class CtaViewModel @Inject constructor( companion object { private const val MAX_TABS_OPEN_FIRE_EDUCATION = 2 + private const val MIN_DAYS_TO_COUNT_ONBOARDING_CTA_SHOWN = 3 } } diff --git a/app/src/main/java/com/duckduckgo/app/onboarding/store/OnboardingStore.kt b/app/src/main/java/com/duckduckgo/app/onboarding/store/OnboardingStore.kt index 0da349b25523..3efc9324ff34 100644 --- a/app/src/main/java/com/duckduckgo/app/onboarding/store/OnboardingStore.kt +++ b/app/src/main/java/com/duckduckgo/app/onboarding/store/OnboardingStore.kt @@ -20,7 +20,9 @@ import com.duckduckgo.app.cta.ui.DaxBubbleCta.DaxDialogIntroOption interface OnboardingStore { var onboardingDialogJourney: String? + var visitSiteCtaDisplayCount: Int fun getSearchOptions(): List fun getSitesOptions(): List fun getExperimentSearchOptions(): List + fun clearVisitSiteCtaDisplayCount() } diff --git a/app/src/main/java/com/duckduckgo/app/onboarding/store/OnboardingStoreImpl.kt b/app/src/main/java/com/duckduckgo/app/onboarding/store/OnboardingStoreImpl.kt index 87a4d1227c7d..55bd021a824c 100644 --- a/app/src/main/java/com/duckduckgo/app/onboarding/store/OnboardingStoreImpl.kt +++ b/app/src/main/java/com/duckduckgo/app/onboarding/store/OnboardingStoreImpl.kt @@ -35,6 +35,10 @@ class OnboardingStoreImpl @Inject constructor( get() = preferences.getString(ONBOARDING_JOURNEY, null) set(dialogJourney) = preferences.edit { putString(ONBOARDING_JOURNEY, dialogJourney) } + override var visitSiteCtaDisplayCount: Int + get() = preferences.getInt(VISIT_SITE_CTA_DISPLAY_COUNT, 0) + set(count) = preferences.edit { putInt(VISIT_SITE_CTA_DISPLAY_COUNT, count) } + override fun getSearchOptions(): List { val country = Locale.getDefault().country val language = Locale.getDefault().language @@ -207,8 +211,13 @@ class OnboardingStoreImpl @Inject constructor( ) } + override fun clearVisitSiteCtaDisplayCount() { + preferences.edit { remove(VISIT_SITE_CTA_DISPLAY_COUNT) } + } + companion object { const val FILENAME = "com.duckduckgo.app.onboarding.settings" const val ONBOARDING_JOURNEY = "onboardingJourney" + const val VISIT_SITE_CTA_DISPLAY_COUNT = "visitSiteCtaDisplayCount" } } diff --git a/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt b/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt index b3bf6f0ee348..180dacc82e60 100644 --- a/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt +++ b/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt @@ -49,6 +49,7 @@ enum class AppPixelName(override val pixelName: String) : Pixel.PixelName { ONBOARDING_DAX_CTA_OK_BUTTON("m_odc_ok"), ONBOARDING_DAX_CTA_CANCEL_BUTTON("m_onboarding_dax_cta_cancel"), ONBOARDING_SKIP_MAJOR_NETWORK_UNIQUE("m_onboarding_skip_major_network_unique"), + ONBOARDING_VISIT_SITE_CTA_SHOWN("onboarding_visit_site_cta_shown"), BROWSER_MENU_ALLOWLIST_ADD("mb_wla"), BROWSER_MENU_ALLOWLIST_REMOVE("mb_wlr"), diff --git a/app/src/test/java/com/duckduckgo/app/cta/ui/OnboardingDaxDialogTests.kt b/app/src/test/java/com/duckduckgo/app/cta/ui/OnboardingDaxDialogTests.kt index acc8c066f62e..2bdf27b047be 100644 --- a/app/src/test/java/com/duckduckgo/app/cta/ui/OnboardingDaxDialogTests.kt +++ b/app/src/test/java/com/duckduckgo/app/cta/ui/OnboardingDaxDialogTests.kt @@ -36,6 +36,7 @@ import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.tabs.model.TabRepository import com.duckduckgo.app.widget.ui.WidgetCapabilities import com.duckduckgo.brokensite.api.BrokenSitePrompt +import com.duckduckgo.browser.api.UserBrowserProperties import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.duckplayer.api.DuckPlayer @@ -78,6 +79,7 @@ class OnboardingDaxDialogTests { private val mockHighlightsOnboardingExperimentManager: HighlightsOnboardingExperimentManager = mock() private val mockBrokenSitePrompt: BrokenSitePrompt = mock() private val mockExtendedOnboardingPixelsPlugin: ExtendedOnboardingPixelsPlugin = mock() + private val mockUserBrowserProperties: UserBrowserProperties = mock() val mockEnabledToggle: Toggle = org.mockito.kotlin.mock { on { it.isEnabled() } doReturn true } val mockDisabledToggle: Toggle = org.mockito.kotlin.mock { on { it.isEnabled() } doReturn false } @@ -99,6 +101,7 @@ class OnboardingDaxDialogTests { mockHighlightsOnboardingExperimentManager, mockBrokenSitePrompt, mockExtendedOnboardingPixelsPlugin, + mockUserBrowserProperties, ) }