From ea335e8bb93fa04880150fa4f015f428082722b1 Mon Sep 17 00:00:00 2001 From: nalcalag Date: Wed, 8 Oct 2025 16:19:14 +0200 Subject: [PATCH 1/2] Added new pixel for vpn menu item selected --- .../com/duckduckgo/app/browser/BrowserTabViewModel.kt | 8 ++++++-- .../main/java/com/duckduckgo/app/pixels/AppPixelName.kt | 1 + .../java/com/duckduckgo/app/statistics/pixels/Pixel.kt | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt index 4daa44d8f5f1..2869cacc3934 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt @@ -4465,18 +4465,22 @@ class BrowserTabViewModel @Inject constructor( fun onVpnMenuClicked() { val vpnMenuState = currentBrowserViewState().vpnMenuState - when (vpnMenuState) { + val statusParam = when (vpnMenuState) { VpnMenuState.NotSubscribed -> { command.value = LaunchPrivacyPro("https://duckduckgo.com/pro?origin=funnel_appmenu_android".toUri()) + "pill" } VpnMenuState.NotSubscribedNoPill -> { command.value = LaunchPrivacyPro("https://duckduckgo.com/pro?origin=funnel_appmenu_android".toUri()) + "no_pill" } is VpnMenuState.Subscribed -> { command.value = LaunchVpnManagement + "subscribed" } - VpnMenuState.Hidden -> {} // Should not happen as menu item should not be visible + VpnMenuState.Hidden -> "" // Should not happen as menu item should not be visible } + pixel.fire(AppPixelName.MENU_ACTION_VPN_PRESSED, mapOf(PixelParameter.STATUS to statusParam)) } fun onAutoConsentPopUpHandled(isCosmetic: Boolean) { 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 297b60f67b77..78d38a2e5ca8 100644 --- a/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt +++ b/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt @@ -263,6 +263,7 @@ enum class AppPixelName(override val pixelName: String) : Pixel.PixelName { MENU_ACTION_APP_LINKS_OPEN_PRESSED("m_nav_app_links_open_menu_item_pressed"), MENU_ACTION_DOWNLOADS_PRESSED("m_nav_downloads_menu_item_pressed"), MENU_ACTION_AUTOFILL_PRESSED("m_nav_autofill_menu_item_pressed"), + MENU_ACTION_VPN_PRESSED("m_nav_vpn_menu_item_pressed"), FIREPROOF_WEBSITE_ADDED("m_fw_a"), FIREPROOF_WEBSITE_REMOVE("m_fw_r"), diff --git a/statistics/statistics-api/src/main/java/com/duckduckgo/app/statistics/pixels/Pixel.kt b/statistics/statistics-api/src/main/java/com/duckduckgo/app/statistics/pixels/Pixel.kt index 463c0d79a056..5a810d763d0c 100644 --- a/statistics/statistics-api/src/main/java/com/duckduckgo/app/statistics/pixels/Pixel.kt +++ b/statistics/statistics-api/src/main/java/com/duckduckgo/app/statistics/pixels/Pixel.kt @@ -71,6 +71,7 @@ interface Pixel { const val IS_TAB_SWITCHER_BUTTON_SHOWN = "is_tab_switcher_button_shown" const val IS_FIRE_BUTTON_SHOWN = "is_fire_button_shown" const val IS_BROWSER_MENU_BUTTON_SHOWN = "is_browser_menu_button_shown" + const val STATUS = "status" } object PixelValues { From fcf05d791f548ea775226b0d8fa23e7d67ef30df Mon Sep 17 00:00:00 2001 From: nalcalag Date: Wed, 8 Oct 2025 16:56:38 +0200 Subject: [PATCH 2/2] Added tests --- .../app/browser/BrowserTabViewModelTest.kt | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) 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 cf77eee3da7c..c72ba5563797 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt @@ -7902,4 +7902,46 @@ class BrowserTabViewModelTest { override fun getPlugins(): Collection = listOf(plugin) } + + @Test + fun whenVpnMenuClickedWithNotSubscribedStateThenPixelFiredWithPillStatus() { + testee.browserViewState.value = browserViewState().copy( + vpnMenuState = VpnMenuState.NotSubscribed, + ) + + testee.onVpnMenuClicked() + + verify(mockPixel).fire( + AppPixelName.MENU_ACTION_VPN_PRESSED, + mapOf(PixelParameter.STATUS to "pill"), + ) + } + + @Test + fun whenVpnMenuClickedWithNotSubscribedNoPillStateThenPixelFiredWithNoPillStatus() { + testee.browserViewState.value = browserViewState().copy( + vpnMenuState = VpnMenuState.NotSubscribedNoPill, + ) + + testee.onVpnMenuClicked() + + verify(mockPixel).fire( + AppPixelName.MENU_ACTION_VPN_PRESSED, + mapOf(PixelParameter.STATUS to "no_pill"), + ) + } + + @Test + fun whenVpnMenuClickedWithSubscribedStateThenPixelFiredWithSubscribedStatus() { + testee.browserViewState.value = browserViewState().copy( + vpnMenuState = VpnMenuState.Subscribed(isVpnEnabled = true), + ) + + testee.onVpnMenuClicked() + + verify(mockPixel).fire( + AppPixelName.MENU_ACTION_VPN_PRESSED, + mapOf(PixelParameter.STATUS to "subscribed"), + ) + } }