Skip to content

Commit 05c58cc

Browse files
authored
Post visual update: Send pixel when address bar focused (#6655)
Task/Issue URL: https://app.asana.com/1/137249556945/project/488551667048375/task/1211021593099148?focus=true ### Description This PR adds a pixel (`m_addressbar_focus_ntp`) that is sent when the address bar is focused on NTP. It contains the following parameters: - ~~"is_duck_ai_button_shown"~~"is_tab_switcher_button_shown" - "is_fire_button_shown" - "is_browser_menu_button_shown" ### Steps to test this PR _NTP_ - [x] Open a NTP - [x] Focus on the address bar - [x] Verify the `m_addressbar_focus_ntp` pixel is sent with the correct parameters - [x] Start typing - [x] Verify the pixel is sent once with all parameters set to false (the buttons are not visible) _Browser_ - [x] Load some website - [x] Tap on the address bar to add focus - [x] Verify that pixel (`m_addressbar_focus_ntp`) is not sent
1 parent e973f81 commit 05c58cc

File tree

4 files changed

+100
-1
lines changed

4 files changed

+100
-1
lines changed

app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarLayoutViewModel.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
7474
import kotlinx.coroutines.flow.SharingStarted
7575
import kotlinx.coroutines.flow.combine
7676
import kotlinx.coroutines.flow.distinctUntilChanged
77+
import kotlinx.coroutines.flow.filter
7778
import kotlinx.coroutines.flow.flowOn
7879
import kotlinx.coroutines.flow.launchIn
80+
import kotlinx.coroutines.flow.map
7981
import kotlinx.coroutines.flow.onEach
8082
import kotlinx.coroutines.flow.receiveAsFlow
8183
import kotlinx.coroutines.flow.stateIn
@@ -216,6 +218,25 @@ class OmnibarLayoutViewModel @Inject constructor(
216218
it.copy(showChatMenu = showDuckAiButton)
217219
}
218220
}.launchIn(viewModelScope)
221+
222+
viewState.map {
223+
NewTabPixelParams(
224+
isNtp = it.viewMode == NewTab,
225+
isFocused = it.hasFocus,
226+
isTabSwitcherButtonVisible = it.showTabsMenu,
227+
isFireButtonVisible = it.showFireIcon,
228+
isBrowserMenuButtonVisible = it.showBrowserMenu,
229+
)
230+
}.distinctUntilChanged()
231+
.filter { it.isNtp && it.isFocused }
232+
.onEach {
233+
val params = mapOf(
234+
Pixel.PixelParameter.IS_TAB_SWITCHER_BUTTON_SHOWN to it.isTabSwitcherButtonVisible.toString(),
235+
Pixel.PixelParameter.IS_FIRE_BUTTON_SHOWN to it.isFireButtonVisible.toString(),
236+
Pixel.PixelParameter.IS_BROWSER_MENU_BUTTON_SHOWN to it.isBrowserMenuButtonVisible.toString(),
237+
)
238+
pixel.fire(pixel = AppPixelName.ADDRESS_BAR_NTP_FOCUSED, parameters = params)
239+
}.launchIn(viewModelScope)
219240
}
220241

221242
fun onFindInPageRequested() {
@@ -867,4 +888,12 @@ class OmnibarLayoutViewModel @Inject constructor(
867888
command.send(Command.LaunchInputScreen(query = textToPreFill))
868889
}
869890
}
891+
892+
private data class NewTabPixelParams(
893+
val isNtp: Boolean,
894+
val isFocused: Boolean,
895+
val isTabSwitcherButtonVisible: Boolean,
896+
val isFireButtonVisible: Boolean,
897+
val isBrowserMenuButtonVisible: Boolean,
898+
)
870899
}

app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ enum class AppPixelName(override val pixelName: String) : Pixel.PixelName {
340340
ADDRESS_BAR_NEW_TAB_PAGE_CLOSED("m_addressbar_focus_close_ntp"),
341341
ADDRESS_BAR_WEBSITE_CLOSED("m_addressbar_focus_close_website"),
342342
ADDRESS_BAR_SERP_CLOSED("m_addressbar_focus_close_serp"),
343+
ADDRESS_BAR_NTP_FOCUSED("m_addressbar_focus_ntp"),
343344

344345
KEYBOARD_GO_NEW_TAB_CLICKED("m_keyboard_go_click_ntp"),
345346
KEYBOARD_GO_WEBSITE_CLICKED("m_keyboard_go_click_website"),

app/src/test/java/com/duckduckgo/app/browser/omnibar/OmnibarLayoutViewModelTest.kt

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,18 @@ import kotlin.reflect.KClass
4747
import kotlinx.coroutines.flow.MutableStateFlow
4848
import kotlinx.coroutines.flow.flowOf
4949
import kotlinx.coroutines.test.runTest
50-
import org.junit.Assert.*
50+
import org.junit.Assert.assertEquals
51+
import org.junit.Assert.assertFalse
52+
import org.junit.Assert.assertTrue
5153
import org.junit.Before
5254
import org.junit.Rule
5355
import org.junit.Test
5456
import org.junit.runner.RunWith
5557
import org.mockito.kotlin.any
5658
import org.mockito.kotlin.doAnswer
59+
import org.mockito.kotlin.eq
5760
import org.mockito.kotlin.mock
61+
import org.mockito.kotlin.never
5862
import org.mockito.kotlin.verify
5963
import org.mockito.kotlin.verifyNoInteractions
6064
import org.mockito.kotlin.whenever
@@ -1706,4 +1710,66 @@ class OmnibarLayoutViewModelTest {
17061710
private fun Command.assertCommand(expectedType: KClass<out Command>) {
17071711
assertTrue(String.format("Unexpected command type: %s", this::class.simpleName), this::class == expectedType)
17081712
}
1713+
1714+
@Test
1715+
fun whenOmnibarFocusedOnNewTabThenAddressBarNtpFocusedPixelSent() = runTest {
1716+
givenSiteLoaded("")
1717+
testee.onViewModeChanged(ViewMode.NewTab)
1718+
testee.onOmnibarFocusChanged(true, "")
1719+
1720+
verify(pixel).fire(
1721+
AppPixelName.ADDRESS_BAR_NTP_FOCUSED,
1722+
mapOf(
1723+
Pixel.PixelParameter.IS_TAB_SWITCHER_BUTTON_SHOWN to "true",
1724+
Pixel.PixelParameter.IS_FIRE_BUTTON_SHOWN to "true",
1725+
Pixel.PixelParameter.IS_BROWSER_MENU_BUTTON_SHOWN to "true",
1726+
),
1727+
)
1728+
}
1729+
1730+
@Test
1731+
fun whenOmnibarFocusedOnWebsiteThenAddressBarNtpFocusedPixelNotSent() = runTest {
1732+
givenSiteLoaded(RANDOM_URL)
1733+
testee.onViewModeChanged(ViewMode.Browser(RANDOM_URL))
1734+
testee.onOmnibarFocusChanged(true, "")
1735+
1736+
verify(pixel, never()).fire(
1737+
eq(AppPixelName.ADDRESS_BAR_NTP_FOCUSED),
1738+
any(),
1739+
any(),
1740+
any(),
1741+
)
1742+
}
1743+
1744+
@Test
1745+
fun whenOmnibarFocusedOnSerpThenAddressBarNtpFocusedPixelNotSent() = runTest {
1746+
givenSiteLoaded(SERP_URL)
1747+
testee.onViewModeChanged(ViewMode.Browser(SERP_URL))
1748+
testee.onOmnibarFocusChanged(true, "")
1749+
1750+
verify(pixel, never()).fire(
1751+
eq(AppPixelName.ADDRESS_BAR_NTP_FOCUSED),
1752+
any(),
1753+
any(),
1754+
any(),
1755+
)
1756+
}
1757+
1758+
@Test
1759+
fun whenOmnibarFocusedOnNewTabWithAllButtonsDisabledThenCorrectParametersSent() = runTest {
1760+
givenSiteLoaded("")
1761+
testee.onViewModeChanged(ViewMode.NewTab)
1762+
1763+
// Simulate focusing the omnibar with text, which hides the fire button
1764+
testee.onOmnibarFocusChanged(true, "some query")
1765+
1766+
verify(pixel).fire(
1767+
AppPixelName.ADDRESS_BAR_NTP_FOCUSED,
1768+
mapOf(
1769+
Pixel.PixelParameter.IS_TAB_SWITCHER_BUTTON_SHOWN to "false",
1770+
Pixel.PixelParameter.IS_FIRE_BUTTON_SHOWN to "false",
1771+
Pixel.PixelParameter.IS_BROWSER_MENU_BUTTON_SHOWN to "false",
1772+
),
1773+
)
1774+
}
17091775
}

statistics/statistics-api/src/main/java/com/duckduckgo/app/statistics/pixels/Pixel.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ interface Pixel {
6868
const val TAB_INACTIVE_3W = "tab_inactive_3w"
6969
const val IS_ENABLED = "is_enabled"
7070
const val FROM_FOCUSED_NTP = "from_focused_ntp"
71+
const val IS_TAB_SWITCHER_BUTTON_SHOWN = "is_tab_switcher_button_shown"
72+
const val IS_FIRE_BUTTON_SHOWN = "is_fire_button_shown"
73+
const val IS_BROWSER_MENU_BUTTON_SHOWN = "is_browser_menu_button_shown"
7174
}
7275

7376
object PixelValues {

0 commit comments

Comments
 (0)