Skip to content

Commit 29ca141

Browse files
authored
add Input Screen discovery funnel (#6736)
Task/Issue URL: https://app.asana.com/1/137249556945/project/1208671518894266/task/1211245493245691?focus=true ### Description Adds pixels required to understand discovery and adoption of the Input Screen. ### Steps to test this PR - [x] Set a `message:Pixel sent: m_aichat_experimental_omnibar` logcat filter. - [x] Clean install the app. - [x] Go to Settings -> AI Features. - [x] Verify `m_aichat_experimental_omnibar_first_settings_viewed` pixel is sent. - [x] Enable experimental address bar. - [x] Verify `m_aichat_experimental_omnibar_first_enabled` pixel is sent. - [x] Go back to the browser and click on the omnibar. - [x] Verify `m_aichat_experimental_omnibar_first_interaction` pixel is sent. - [x] Perform search. - [x] Verify `m_aichat_experimental_omnibar_first_search_submission` is sent. - [x] Click on the omnibar. - [x] Verify `m_aichat_experimental_omnibar_first_interaction` pixel **is not** sent. - [x] Switch to Duck.ai and submit a prompt. - [x] Verify "m_aichat_experimental_omnibar_first_prompt_submission" pixel is sent. - [x] Verify "m_aichat_experimental_omnibar_full_conversion_user" pixel is sent. - [x] Perform more searches/prompts (optionally restart the app) and verify no more "first" pixels are sent. - [x] (optional) Retry the funnel. - [x] Go to Settings -> AI Features. - [x] Disable experimental address bar. - [x] Go out of the settings page. - [x] Go back to AI Features. - [x] Verify `m_aichat_experimental_omnibar_first_settings_viewed` pixel is sent and you can redo all funnel steps.
1 parent 792fece commit 29ca141

File tree

11 files changed

+699
-6
lines changed

11 files changed

+699
-6
lines changed

duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/inputscreen/ui/InputScreenActivity.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import com.duckduckgo.di.scopes.ActivityScope
2525
import com.duckduckgo.duckchat.api.inputscreen.BrowserAndInputScreenTransitionProvider
2626
import com.duckduckgo.duckchat.api.inputscreen.InputScreenActivityParams
2727
import com.duckduckgo.duckchat.impl.R
28+
import com.duckduckgo.duckchat.impl.inputscreen.ui.metrics.discovery.InputScreenDiscoveryFunnel
2829
import javax.inject.Inject
2930

3031
@InjectWith(ActivityScope::class)
@@ -34,9 +35,13 @@ class InputScreenActivity : DuckDuckGoActivity() {
3435
@Inject
3536
lateinit var browserAndInputScreenTransitionProvider: BrowserAndInputScreenTransitionProvider
3637

38+
@Inject
39+
lateinit var inputScreenDiscoveryFunnel: InputScreenDiscoveryFunnel
40+
3741
override fun onCreate(savedInstanceState: Bundle?) {
3842
super.onCreate(savedInstanceState)
3943
setContentView(R.layout.activity_input_screen)
44+
inputScreenDiscoveryFunnel.onInputScreenOpened()
4045
}
4146

4247
override fun finish() {
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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.inputscreen.ui.metrics.discovery
18+
19+
import androidx.datastore.core.DataStore
20+
import androidx.datastore.preferences.core.Preferences
21+
import androidx.datastore.preferences.core.edit
22+
import com.duckduckgo.app.di.AppCoroutineScope
23+
import com.duckduckgo.app.statistics.pixels.Pixel
24+
import com.duckduckgo.di.scopes.AppScope
25+
import com.duckduckgo.duckchat.api.DuckAiFeatureState
26+
import com.duckduckgo.duckchat.impl.inputscreen.ui.metrics.discovery.InputScreenDiscoveryFunnelStep.FeatureEnabled
27+
import com.duckduckgo.duckchat.impl.inputscreen.ui.metrics.discovery.InputScreenDiscoveryFunnelStep.FullyConverted
28+
import com.duckduckgo.duckchat.impl.inputscreen.ui.metrics.discovery.InputScreenDiscoveryFunnelStep.OmnibarInteracted
29+
import com.duckduckgo.duckchat.impl.inputscreen.ui.metrics.discovery.InputScreenDiscoveryFunnelStep.PromptSubmitted
30+
import com.duckduckgo.duckchat.impl.inputscreen.ui.metrics.discovery.InputScreenDiscoveryFunnelStep.SearchSubmitted
31+
import com.duckduckgo.duckchat.impl.inputscreen.ui.metrics.discovery.InputScreenDiscoveryFunnelStep.SettingsSeen
32+
import com.squareup.anvil.annotations.ContributesBinding
33+
import javax.inject.Inject
34+
import kotlinx.coroutines.CoroutineScope
35+
import kotlinx.coroutines.flow.firstOrNull
36+
import kotlinx.coroutines.launch
37+
import kotlinx.coroutines.sync.Mutex
38+
import kotlinx.coroutines.sync.withLock
39+
40+
interface InputScreenDiscoveryFunnel {
41+
fun onDuckAiSettingsSeen()
42+
fun onInputScreenEnabled()
43+
fun onInputScreenDisabled()
44+
fun onInputScreenOpened()
45+
fun onSearchSubmitted()
46+
fun onPromptSubmitted()
47+
}
48+
49+
@ContributesBinding(AppScope::class)
50+
class InputScreenDiscoveryFunnelImpl @Inject constructor(
51+
@AppCoroutineScope private val coroutineScope: CoroutineScope,
52+
@InputScreenDiscoveryFunnelStore private val dataStore: DataStore<Preferences>,
53+
private val pixel: Pixel,
54+
private val duckAiFeatureState: DuckAiFeatureState,
55+
) : InputScreenDiscoveryFunnel {
56+
57+
private val stepProcessingMutex = Mutex()
58+
59+
override fun onDuckAiSettingsSeen() {
60+
if (!duckAiFeatureState.showInputScreen.value) {
61+
onStep(SettingsSeen)
62+
}
63+
}
64+
65+
override fun onInputScreenEnabled() {
66+
onStep(FeatureEnabled)
67+
}
68+
69+
override fun onInputScreenDisabled() {
70+
resetFunnel()
71+
}
72+
73+
override fun onInputScreenOpened() {
74+
onStep(OmnibarInteracted)
75+
}
76+
77+
override fun onSearchSubmitted() {
78+
onStep(SearchSubmitted)
79+
}
80+
81+
override fun onPromptSubmitted() {
82+
onStep(PromptSubmitted)
83+
}
84+
85+
private fun resetFunnel() {
86+
coroutineScope.launch {
87+
stepProcessingMutex.withLock {
88+
dataStore.edit { preferences ->
89+
preferences[SettingsSeen.prefKey] = false
90+
preferences[FeatureEnabled.prefKey] = false
91+
preferences[OmnibarInteracted.prefKey] = false
92+
preferences[SearchSubmitted.prefKey] = false
93+
preferences[PromptSubmitted.prefKey] = false
94+
preferences[FullyConverted.prefKey] = false
95+
}
96+
}
97+
}
98+
}
99+
100+
private fun onStep(step: InputScreenDiscoveryFunnelStep) = coroutineScope.launch {
101+
stepProcessingMutex.withLock {
102+
val data = dataStore.data.firstOrNull()
103+
val stepReached = data?.get(step.prefKey) == true
104+
if (!stepReached) {
105+
val dependenciesSatisfied = step.dependsOn.all { data?.get(it.prefKey) == true }
106+
if (dependenciesSatisfied) {
107+
pixel.fire(step.pixelName)
108+
dataStore.edit { prefs ->
109+
prefs[step.prefKey] = true
110+
}
111+
checkForFullConversion()
112+
}
113+
}
114+
}
115+
}
116+
117+
private suspend fun checkForFullConversion() {
118+
val data = dataStore.data.firstOrNull()
119+
val isFullyConverted = data?.get(FullyConverted.prefKey) == true
120+
if (!isFullyConverted) {
121+
val fullConversionDependenciesSatisfied = FullyConverted.dependsOn.all { data?.get(it.prefKey) == true }
122+
if (fullConversionDependenciesSatisfied) {
123+
pixel.fire(FullyConverted.pixelName)
124+
dataStore.edit { prefs ->
125+
prefs[FullyConverted.prefKey] = true
126+
}
127+
}
128+
}
129+
}
130+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.inputscreen.ui.metrics.discovery
18+
19+
import android.content.Context
20+
import androidx.datastore.core.DataStore
21+
import androidx.datastore.preferences.core.Preferences
22+
import androidx.datastore.preferences.preferencesDataStore
23+
import com.duckduckgo.di.scopes.AppScope
24+
import com.squareup.anvil.annotations.ContributesTo
25+
import dagger.Module
26+
import dagger.Provides
27+
import javax.inject.Qualifier
28+
29+
private val Context.inputScreenDiscoveryFunnelDataStore: DataStore<Preferences> by preferencesDataStore(
30+
name = "input_screen_discovery_funnel_store",
31+
)
32+
33+
@Qualifier
34+
annotation class InputScreenDiscoveryFunnelStore
35+
36+
@Module
37+
@ContributesTo(AppScope::class)
38+
object InputScreenDiscoveryFunnelModule {
39+
@Provides
40+
@InputScreenDiscoveryFunnelStore
41+
fun provideInputScreenDiscoveryFunnelDataStore(context: Context): DataStore<Preferences> {
42+
return context.inputScreenDiscoveryFunnelDataStore
43+
}
44+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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.inputscreen.ui.metrics.discovery
18+
19+
import androidx.datastore.preferences.core.Preferences.Key
20+
import androidx.datastore.preferences.core.booleanPreferencesKey
21+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName
22+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_ENABLED
23+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_INTERACTION
24+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_PROMPT_SUBMISSION
25+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_SEARCH_SUBMISSION
26+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_SETTINGS_VIEWED
27+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FULL_CONVERSION_USER
28+
29+
internal enum class InputScreenDiscoveryFunnelStep(
30+
val prefKey: Key<Boolean>,
31+
val pixelName: DuckChatPixelName,
32+
val dependsOn: Set<InputScreenDiscoveryFunnelStep> = emptySet(),
33+
) {
34+
35+
SettingsSeen(
36+
prefKey = booleanPreferencesKey("SETTINGS_SEEN"),
37+
pixelName = DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_SETTINGS_VIEWED,
38+
),
39+
40+
FeatureEnabled(
41+
prefKey = booleanPreferencesKey("FEATURE_ENABLED"),
42+
pixelName = DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_ENABLED,
43+
dependsOn = setOf(SettingsSeen),
44+
),
45+
46+
OmnibarInteracted(
47+
prefKey = booleanPreferencesKey("OMNIBAR_INTERACTED"),
48+
pixelName = DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_INTERACTION,
49+
dependsOn = setOf(FeatureEnabled),
50+
),
51+
52+
SearchSubmitted(
53+
prefKey = booleanPreferencesKey("SEARCH_SUBMITTED"),
54+
pixelName = DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_SEARCH_SUBMISSION,
55+
dependsOn = setOf(OmnibarInteracted),
56+
),
57+
58+
PromptSubmitted(
59+
prefKey = booleanPreferencesKey("PROMPT_SUBMITTED"),
60+
pixelName = DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_PROMPT_SUBMISSION,
61+
dependsOn = setOf(OmnibarInteracted),
62+
),
63+
64+
FullyConverted(
65+
prefKey = booleanPreferencesKey("FULLY_CONVERTED"),
66+
pixelName = DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FULL_CONVERSION_USER,
67+
dependsOn = setOf(SearchSubmitted, PromptSubmitted),
68+
),
69+
}

duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/inputscreen/ui/viewmodel/InputScreenViewModel.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import com.duckduckgo.duckchat.impl.inputscreen.ui.command.Command.SwitchToTab
4545
import com.duckduckgo.duckchat.impl.inputscreen.ui.command.InputFieldCommand
4646
import com.duckduckgo.duckchat.impl.inputscreen.ui.command.SearchCommand
4747
import com.duckduckgo.duckchat.impl.inputscreen.ui.command.SearchCommand.ShowRemoveSearchSuggestionDialog
48+
import com.duckduckgo.duckchat.impl.inputscreen.ui.metrics.discovery.InputScreenDiscoveryFunnel
4849
import com.duckduckgo.duckchat.impl.inputscreen.ui.session.InputScreenSessionStore
4950
import com.duckduckgo.duckchat.impl.inputscreen.ui.state.AutoCompleteScrollState
5051
import com.duckduckgo.duckchat.impl.inputscreen.ui.state.InputFieldState
@@ -115,6 +116,7 @@ class InputScreenViewModel @AssistedInject constructor(
115116
private val duckChat: DuckChat,
116117
private val pixel: Pixel,
117118
private val sessionStore: InputScreenSessionStore,
119+
private val inputScreenDiscoveryFunnel: InputScreenDiscoveryFunnel,
118120
) : ViewModel() {
119121

120122
private var hasUserSeenHistoryIAM = false
@@ -358,6 +360,7 @@ class InputScreenViewModel @AssistedInject constructor(
358360
command.value = Command.SubmitSearch(sanitizedQuery)
359361
pixel.fire(DUCK_CHAT_EXPERIMENTAL_OMNIBAR_QUERY_SUBMITTED)
360362
pixel.fire(DUCK_CHAT_EXPERIMENTAL_OMNIBAR_QUERY_SUBMITTED_DAILY, type = Daily())
363+
inputScreenDiscoveryFunnel.onSearchSubmitted()
361364

362365
viewModelScope.launch {
363366
sessionStore.setHasUsedSearchMode(true)
@@ -374,6 +377,7 @@ class InputScreenViewModel @AssistedInject constructor(
374377
}
375378
pixel.fire(DUCK_CHAT_EXPERIMENTAL_OMNIBAR_PROMPT_SUBMITTED)
376379
pixel.fire(DUCK_CHAT_EXPERIMENTAL_OMNIBAR_PROMPT_SUBMITTED_DAILY, type = Daily())
380+
inputScreenDiscoveryFunnel.onPromptSubmitted()
377381

378382
viewModelScope.launch {
379383
sessionStore.setHasUsedChatMode(true)

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENT
3838
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_LEGACY_OMNIBAR_QUERY_SUBMITTED
3939
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_LEGACY_OMNIBAR_QUERY_SUBMITTED_DAILY
4040
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_LEGACY_OMNIBAR_SHOWN
41+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_ENABLED
42+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_INTERACTION
43+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_PROMPT_SUBMISSION
44+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_SEARCH_SUBMISSION
45+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_SETTINGS_VIEWED
46+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FULL_CONVERSION_USER
4147
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_MODE_SWITCHED
4248
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_PROMPT_SUBMITTED
4349
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_PROMPT_SUBMITTED_DAILY
@@ -150,6 +156,12 @@ enum class DuckChatPixelName(override val pixelName: String) : Pixel.PixelName {
150156
DUCK_CHAT_EXPERIMENTAL_LEGACY_OMNIBAR_AICHAT_BUTTON_PRESSED_DAILY("m_aichat_legacy_omnibar_aichat_button_pressed_daily"),
151157
DUCK_CHAT_OPEN_AUTOCOMPLETE_EXPERIMENTAL("m_aichat_open_autocomplete_experimental"),
152158
DUCK_CHAT_OPEN_AUTOCOMPLETE_LEGACY("m_aichat_open_autocomplete_legacy"),
159+
DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_SETTINGS_VIEWED("m_aichat_experimental_omnibar_first_settings_viewed"),
160+
DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_ENABLED("m_aichat_experimental_omnibar_first_enabled"),
161+
DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_INTERACTION("m_aichat_experimental_omnibar_first_interaction"),
162+
DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_SEARCH_SUBMISSION("m_aichat_experimental_omnibar_first_search_submission"),
163+
DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_PROMPT_SUBMISSION("m_aichat_experimental_omnibar_first_prompt_submission"),
164+
DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FULL_CONVERSION_USER("m_aichat_experimental_omnibar_full_conversion_user"),
153165
}
154166

155167
object DuckChatPixelParameters {
@@ -201,6 +213,12 @@ class DuckChatParamRemovalPlugin @Inject constructor() : PixelParamRemovalPlugin
201213
DUCK_CHAT_EXPERIMENTAL_LEGACY_OMNIBAR_QUERY_SUBMITTED_DAILY.pixelName to PixelParameter.removeAtb(),
202214
DUCK_CHAT_EXPERIMENTAL_LEGACY_OMNIBAR_AICHAT_BUTTON_PRESSED.pixelName to PixelParameter.removeAtb(),
203215
DUCK_CHAT_EXPERIMENTAL_LEGACY_OMNIBAR_AICHAT_BUTTON_PRESSED_DAILY.pixelName to PixelParameter.removeAtb(),
216+
DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_SETTINGS_VIEWED.pixelName to PixelParameter.removeAtb(),
217+
DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_ENABLED.pixelName to PixelParameter.removeAtb(),
218+
DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_INTERACTION.pixelName to PixelParameter.removeAtb(),
219+
DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_SEARCH_SUBMISSION.pixelName to PixelParameter.removeAtb(),
220+
DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FIRST_PROMPT_SUBMISSION.pixelName to PixelParameter.removeAtb(),
221+
DUCK_CHAT_EXPERIMENTAL_OMNIBAR_FULL_CONVERSION_USER.pixelName to PixelParameter.removeAtb(),
204222
)
205223
}
206224
}

duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/ui/settings/DuckChatSettingsActivity.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import com.duckduckgo.di.scopes.ActivityScope
4545
import com.duckduckgo.duckchat.api.DuckChatSettingsNoParams
4646
import com.duckduckgo.duckchat.impl.R
4747
import com.duckduckgo.duckchat.impl.databinding.ActivityDuckChatSettingsBinding
48+
import com.duckduckgo.duckchat.impl.inputscreen.ui.metrics.discovery.InputScreenDiscoveryFunnel
4849
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_SETTINGS_DISPLAYED
4950
import com.duckduckgo.duckchat.impl.ui.settings.DuckChatSettingsViewModel.ViewState
5051
import com.duckduckgo.mobile.android.R as CommonR
@@ -78,6 +79,9 @@ class DuckChatSettingsActivity : DuckDuckGoActivity() {
7879
@Inject
7980
lateinit var appTheme: AppTheme
8081

82+
@Inject
83+
lateinit var inputScreenDiscoveryFunnel: InputScreenDiscoveryFunnel
84+
8185
override fun onCreate(savedInstanceState: Bundle?) {
8286
super.onCreate(savedInstanceState)
8387

@@ -88,6 +92,7 @@ class DuckChatSettingsActivity : DuckDuckGoActivity() {
8892
observeViewModel()
8993

9094
pixel.fire(DUCK_CHAT_SETTINGS_DISPLAYED)
95+
inputScreenDiscoveryFunnel.onDuckAiSettingsSeen()
9196
}
9297

9398
private fun observeViewModel() {

duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/ui/settings/DuckChatSettingsViewModel.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import com.duckduckgo.anvil.annotations.ContributesViewModel
2222
import com.duckduckgo.app.statistics.pixels.Pixel
2323
import com.duckduckgo.di.scopes.ActivityScope
2424
import com.duckduckgo.duckchat.impl.DuckChatInternal
25+
import com.duckduckgo.duckchat.impl.inputscreen.ui.metrics.discovery.InputScreenDiscoveryFunnel
2526
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName
2627
import com.duckduckgo.duckchat.impl.ui.settings.DuckChatSettingsViewModel.Command.OpenLink
2728
import com.duckduckgo.duckchat.impl.ui.settings.DuckChatSettingsViewModel.Command.OpenLinkInNewTab
@@ -41,6 +42,7 @@ class DuckChatSettingsViewModel @Inject constructor(
4142
private val duckChat: DuckChatInternal,
4243
private val pixel: Pixel,
4344
private val rebrandingAiFeaturesEnabled: SubscriptionRebrandingFeatureToggle,
45+
private val inputScreenDiscoveryFunnel: InputScreenDiscoveryFunnel,
4446
) : ViewModel() {
4547

4648
private val commandChannel = Channel<Command>(capacity = 1, onBufferOverflow = DROP_OLDEST)
@@ -129,13 +131,15 @@ class DuckChatSettingsViewModel @Inject constructor(
129131
fun onDuckAiInputScreenWithoutAiSelected() {
130132
viewModelScope.launch {
131133
pixel.fire(DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_SETTING_OFF)
134+
inputScreenDiscoveryFunnel.onInputScreenDisabled()
132135
duckChat.setInputScreenUserSetting(enabled = false)
133136
}
134137
}
135138

136139
fun onDuckAiInputScreenWithAiSelected() {
137140
viewModelScope.launch {
138141
pixel.fire(DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_SETTING_ON)
142+
inputScreenDiscoveryFunnel.onInputScreenEnabled()
139143
duckChat.setInputScreenUserSetting(enabled = true)
140144
}
141145
}

0 commit comments

Comments
 (0)