Skip to content

Commit cfd7b09

Browse files
authored
Add Input Screen pixels (#6514)
Task/Issue URL: https://app.asana.com/1/137249556945/project/488551667048375/task/1210948174591274?focus=true ### Description - Adds a daily pixel for the state of the “Experimental Address Bar” setting - Sends a pixel when the “Experimental Address Bar” setting is turned on - Sends a pixel when the “Experimental Address Bar” setting is turned off ### Steps to test this PR - [x] Launch the app - [x] Verify that `aichat_experimental_address_bar_is_enabled_daily` is sent - [x] Toggle on the the “Experimental Address Bar” setting - [x] Verify that `aichat_experimental_address_bar_setting_on` is sent - [x] Toggle off the the “Experimental Address Bar” setting - [x] Verify that `aichat_experimental_address_bar_setting_off` is sent
1 parent 028f5ce commit cfd7b09

File tree

5 files changed

+114
-4
lines changed

5 files changed

+114
-4
lines changed

duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/RealDuckChat.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,11 @@ class RealDuckChat @Inject constructor(
271271
}
272272

273273
override suspend fun setInputScreenUserSetting(enabled: Boolean) {
274+
if (enabled) {
275+
pixel.fire(DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_SETTING_ON)
276+
} else {
277+
pixel.fire(DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_SETTING_OFF)
278+
}
274279
duckChatFeatureRepository.setInputScreenUserSetting(enabled)
275280
cacheUserSettings()
276281
}

duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/pixel/DuckChatDailyPixelSender.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ class DuckChatDailyPixelSender @Inject constructor(
6060
parameters = mapOf(PixelParameter.IS_ENABLED to duckChatFeatureRepository.shouldShowInAddressBar().toString()),
6161
type = Daily(),
6262
)
63+
pixel.fire(
64+
pixel = DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_IS_ENABLED_DAILY,
65+
parameters = mapOf(PixelParameter.IS_ENABLED to duckChatFeatureRepository.isInputScreenUserSettingEnabled().toString()),
66+
type = Daily(),
67+
)
6368
}
6469
}
6570
}

duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/pixel/DuckChatPixels.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ import com.duckduckgo.duckchat.impl.ReportMetric.USER_DID_SUBMIT_FIRST_PROMPT
3030
import com.duckduckgo.duckchat.impl.ReportMetric.USER_DID_SUBMIT_PROMPT
3131
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_ADDRESS_BAR_IS_ENABLED_DAILY
3232
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_BROWSER_MENU_IS_ENABLED_DAILY
33+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_IS_ENABLED_DAILY
34+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_SETTING_OFF
35+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_SETTING_ON
3336
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENT_SEARCHBAR_BUTTON_OPEN
3437
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_IS_ENABLED_DAILY
3538
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_MENU_SETTING_OFF
@@ -103,13 +106,16 @@ enum class DuckChatPixelName(override val pixelName: String) : Pixel.PixelName {
103106
DUCK_CHAT_MENU_SETTING_ON("aichat_menu_setting_on"),
104107
DUCK_CHAT_SEARCHBAR_SETTING_OFF("aichat_searchbar_setting_off"),
105108
DUCK_CHAT_SEARCHBAR_SETTING_ON("aichat_searchbar_setting_on"),
109+
DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_SETTING_OFF("aichat_experimental_address_bar_setting_off"),
110+
DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_SETTING_ON("aichat_experimental_address_bar_setting_on"),
106111
DUCK_CHAT_SETTINGS_PRESSED("settings_aichat_pressed"),
107112
DUCK_CHAT_SETTINGS_DISPLAYED("m_aichat_settings_displayed"),
108113
DUCK_CHAT_SEARCHBAR_BUTTON_OPEN("aichat_searchbar_button_open"),
109114
DUCK_CHAT_EXPERIMENT_SEARCHBAR_BUTTON_OPEN("aichat_experiment_searchbar_button_open"),
110115
DUCK_CHAT_IS_ENABLED_DAILY("aichat_is_enabled_daily"),
111116
DUCK_CHAT_BROWSER_MENU_IS_ENABLED_DAILY("aichat_browser_menu_is_enabled_daily"),
112117
DUCK_CHAT_ADDRESS_BAR_IS_ENABLED_DAILY("aichat_address_bar_is_enabled_daily"),
118+
DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_IS_ENABLED_DAILY("aichat_experimental_address_bar_is_enabled_daily"),
113119
DUCK_CHAT_SEARCH_ASSIST_SETTINGS_BUTTON_CLICKED("aichat_search_assist_settings_button_clicked"),
114120
DUCK_CHAT_START_NEW_CONVERSATION("aichat_start_new_conversation"),
115121
DUCK_CHAT_START_NEW_CONVERSATION_BUTTON_CLICKED("aichat_start_new_conversation_button_clicked"),
@@ -139,13 +145,16 @@ class DuckChatParamRemovalPlugin @Inject constructor() : PixelParamRemovalPlugin
139145
DUCK_CHAT_MENU_SETTING_ON.pixelName to PixelParameter.removeAtb(),
140146
DUCK_CHAT_SEARCHBAR_SETTING_OFF.pixelName to PixelParameter.removeAtb(),
141147
DUCK_CHAT_SEARCHBAR_SETTING_ON.pixelName to PixelParameter.removeAtb(),
148+
DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_SETTING_OFF.pixelName to PixelParameter.removeAtb(),
149+
DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_SETTING_ON.pixelName to PixelParameter.removeAtb(),
142150
DUCK_CHAT_SETTINGS_PRESSED.pixelName to PixelParameter.removeAtb(),
143151
DUCK_CHAT_SETTINGS_DISPLAYED.pixelName to PixelParameter.removeAtb(),
144152
DUCK_CHAT_SEARCHBAR_BUTTON_OPEN.pixelName to PixelParameter.removeAtb(),
145153
DUCK_CHAT_EXPERIMENT_SEARCHBAR_BUTTON_OPEN.pixelName to PixelParameter.removeAtb(),
146154
DUCK_CHAT_IS_ENABLED_DAILY.pixelName to PixelParameter.removeAtb(),
147155
DUCK_CHAT_BROWSER_MENU_IS_ENABLED_DAILY.pixelName to PixelParameter.removeAtb(),
148156
DUCK_CHAT_ADDRESS_BAR_IS_ENABLED_DAILY.pixelName to PixelParameter.removeAtb(),
157+
DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_IS_ENABLED_DAILY.pixelName to PixelParameter.removeAtb(),
149158
DUCK_CHAT_SEARCH_ASSIST_SETTINGS_BUTTON_CLICKED.pixelName to PixelParameter.removeAtb(),
150159
DUCK_CHAT_START_NEW_CONVERSATION.pixelName to PixelParameter.removeAtb(),
151160
DUCK_CHAT_START_NEW_CONVERSATION_BUTTON_CLICKED.pixelName to PixelParameter.removeAtb(),

duckchat/duckchat-impl/src/test/kotlin/com/duckduckgo/duckchat/impl/RealDuckChatTest.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -674,19 +674,21 @@ class RealDuckChatTest {
674674
}
675675

676676
@Test
677-
fun `when enable input screen user setting then repository updated`() = runTest {
677+
fun `when enable input screen user setting then repository updated and pixel fired`() = runTest {
678678
testee.setInputScreenUserSetting(true)
679679

680-
val inOrder = inOrder(mockDuckChatFeatureRepository)
680+
val inOrder = inOrder(mockDuckChatFeatureRepository, mockPixel)
681+
inOrder.verify(mockPixel).fire(DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_SETTING_ON)
681682
inOrder.verify(mockDuckChatFeatureRepository).setInputScreenUserSetting(true)
682683
inOrder.verify(mockDuckChatFeatureRepository).isInputScreenUserSettingEnabled()
683684
}
684685

685686
@Test
686-
fun `when disable input screen user setting then repository updated`() = runTest {
687+
fun `when disable input screen user setting then repository updated and pixel fired`() = runTest {
687688
testee.setInputScreenUserSetting(false)
688689

689-
val inOrder = inOrder(mockDuckChatFeatureRepository)
690+
val inOrder = inOrder(mockDuckChatFeatureRepository, mockPixel)
691+
inOrder.verify(mockPixel).fire(DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_SETTING_OFF)
690692
inOrder.verify(mockDuckChatFeatureRepository).setInputScreenUserSetting(false)
691693
inOrder.verify(mockDuckChatFeatureRepository).isInputScreenUserSettingEnabled()
692694
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright (c) 2025 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.duckchat.impl.pixel
18+
19+
import androidx.lifecycle.LifecycleOwner
20+
import com.duckduckgo.app.statistics.pixels.Pixel
21+
import com.duckduckgo.app.statistics.pixels.Pixel.PixelParameter
22+
import com.duckduckgo.app.statistics.pixels.Pixel.PixelType.Daily
23+
import com.duckduckgo.common.test.CoroutineTestRule
24+
import com.duckduckgo.duckchat.impl.repository.DuckChatFeatureRepository
25+
import kotlinx.coroutines.ExperimentalCoroutinesApi
26+
import kotlinx.coroutines.test.advanceUntilIdle
27+
import kotlinx.coroutines.test.runTest
28+
import org.junit.Before
29+
import org.junit.Rule
30+
import org.junit.Test
31+
import org.mockito.kotlin.mock
32+
import org.mockito.kotlin.verify
33+
import org.mockito.kotlin.whenever
34+
35+
@OptIn(ExperimentalCoroutinesApi::class)
36+
class DuckChatDailyPixelSenderTest {
37+
38+
@get:Rule
39+
var coroutineRule = CoroutineTestRule()
40+
41+
private val mockPixel: Pixel = mock()
42+
private val mockDuckChatFeatureRepository: DuckChatFeatureRepository = mock()
43+
private val mockLifecycleOwner: LifecycleOwner = mock()
44+
45+
private lateinit var testee: DuckChatDailyPixelSender
46+
47+
@Before
48+
fun setup() {
49+
testee = DuckChatDailyPixelSender(
50+
pixel = mockPixel,
51+
duckChatFeatureRepository = mockDuckChatFeatureRepository,
52+
dispatcherProvider = coroutineRule.testDispatcherProvider,
53+
coroutineScope = coroutineRule.testScope,
54+
)
55+
}
56+
57+
@Test
58+
fun `when onStart then fire daily pixels with correct parameters`() = runTest {
59+
whenever(mockDuckChatFeatureRepository.isDuckChatUserEnabled()).thenReturn(true)
60+
whenever(mockDuckChatFeatureRepository.shouldShowInBrowserMenu()).thenReturn(false)
61+
whenever(mockDuckChatFeatureRepository.shouldShowInAddressBar()).thenReturn(true)
62+
whenever(mockDuckChatFeatureRepository.isInputScreenUserSettingEnabled()).thenReturn(false)
63+
64+
testee.onStart(mockLifecycleOwner)
65+
66+
advanceUntilIdle()
67+
68+
verify(mockPixel).fire(
69+
pixel = DuckChatPixelName.DUCK_CHAT_IS_ENABLED_DAILY,
70+
parameters = mapOf(PixelParameter.IS_ENABLED to "true"),
71+
type = Daily(),
72+
)
73+
verify(mockPixel).fire(
74+
pixel = DuckChatPixelName.DUCK_CHAT_BROWSER_MENU_IS_ENABLED_DAILY,
75+
parameters = mapOf(PixelParameter.IS_ENABLED to "false"),
76+
type = Daily(),
77+
)
78+
verify(mockPixel).fire(
79+
pixel = DuckChatPixelName.DUCK_CHAT_ADDRESS_BAR_IS_ENABLED_DAILY,
80+
parameters = mapOf(PixelParameter.IS_ENABLED to "true"),
81+
type = Daily(),
82+
)
83+
verify(mockPixel).fire(
84+
pixel = DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_IS_ENABLED_DAILY,
85+
parameters = mapOf(PixelParameter.IS_ENABLED to "false"),
86+
type = Daily(),
87+
)
88+
}
89+
}

0 commit comments

Comments
 (0)