Skip to content

Commit 956864f

Browse files
authored
SERP Settings Sync: Add and update pixels (#7073)
Task/Issue URL: https://app.asana.com/1/137249556945/project/1207908166761516/task/1211846254913290?focus=true ### Description Added and updated pixels for SERP settings sync work to match other platforms. - `serp_settings_open_hide-ai-generated-images` is going to replace an existing pixel name - Original privacy triage is [here](https://app.asana.com/1/137249556945/project/69071770703008/task/1211733807255546?focus=true) - This triggers when the `Hide Ai Generated` option is opened and highlighted in SERP settings. - `serp_settings_open_duck-ai` is new - This triggers when a user clicks the 'Open Duck.AI button' in the SERP settings menu which takes them to AI Features settings page. - Does not need ATB ### Steps to test this PR - [x] Open SERP settings and click on "Hide AI Generated" option - [x] Verify the `serp_settings_open_hide-ai-generated-images` pixel is fired and no ATB - [x] Open SERP settings and click on "Open Duck.AI" button - [x] Verify the `serp_settings_open_duck-ai` pixel is fired with no ATB and you're taken to the AI Features settings page
1 parent c685c68 commit 956864f

File tree

9 files changed

+297
-18
lines changed

9 files changed

+297
-18
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// See https://app.asana.com/1/137249556945/project/1207908166761516/task/1211846254913290
2+
{
3+
"serp_settings_open_hide-ai-generated-images": {
4+
// See DuckChatSettingsViewModel.kt for usage.
5+
"description": "When the `Hide Ai Generated` option is opened and highlighted in SERP settings.",
6+
"owners": ["mikescamell"],
7+
"triggers": ["other"],
8+
"suffixes": ["form_factor"],
9+
"parameters": ["appVersion"]
10+
},
11+
"serp_settings_open_duck-ai": {
12+
// See OpenNativeSettingsHandler.kt for usage.
13+
"description": "When a user clicks the 'Open Duck.AI button' in the SERP settings menu which takes them to AI Features settings page.",
14+
"owners": ["mikescamell"],
15+
"triggers": ["other"],
16+
"suffixes": ["form_factor"],
17+
"parameters": ["appVersion"]
18+
}
19+
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENT
6464
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_SHOWN_COUNT
6565
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_SHOWN_DAILY
6666
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENTAL_OMNIBAR_TEXT_AREA_FOCUSED
67-
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_HIDE_AI_GENERATED_IMAGES_BUTTON_CLICKED
6867
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_IS_ENABLED_DAILY
6968
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_KEYBOARD_RETURN_PRESSED
7069
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_MENU_SETTING_OFF
@@ -92,6 +91,7 @@ import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_START_NEW_
9291
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_START_NEW_CONVERSATION_BUTTON_CLICKED
9392
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_USER_DISABLED
9493
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_USER_ENABLED
94+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.SERP_SETTINGS_OPEN_HIDE_AI_GENERATED_IMAGES
9595
import com.duckduckgo.duckchat.impl.repository.DuckChatFeatureRepository
9696
import com.squareup.anvil.annotations.ContributesBinding
9797
import com.squareup.anvil.annotations.ContributesMultibinding
@@ -154,7 +154,7 @@ enum class DuckChatPixelName(override val pixelName: String) : Pixel.PixelName {
154154
DUCK_CHAT_ADDRESS_BAR_IS_ENABLED_DAILY("aichat_address_bar_is_enabled_daily"),
155155
DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_IS_ENABLED_DAILY("aichat_experimental_address_bar_is_enabled_daily"),
156156
DUCK_CHAT_SEARCH_ASSIST_SETTINGS_BUTTON_CLICKED("aichat_search_assist_settings_button_clicked"),
157-
DUCK_CHAT_HIDE_AI_GENERATED_IMAGES_BUTTON_CLICKED("aichat_hide_ai_generated_images_button_clicked"),
157+
SERP_SETTINGS_OPEN_HIDE_AI_GENERATED_IMAGES("serp_settings_open_hide-ai-generated-images"),
158158
DUCK_CHAT_START_NEW_CONVERSATION("aichat_start_new_conversation"),
159159
DUCK_CHAT_START_NEW_CONVERSATION_BUTTON_CLICKED("aichat_start_new_conversation_button_clicked"),
160160
DUCK_CHAT_OPEN_HISTORY("aichat_open_history"),
@@ -233,7 +233,7 @@ class DuckChatParamRemovalPlugin @Inject constructor() : PixelParamRemovalPlugin
233233
DUCK_CHAT_ADDRESS_BAR_IS_ENABLED_DAILY.pixelName to PixelParameter.removeAtb(),
234234
DUCK_CHAT_EXPERIMENTAL_ADDRESS_BAR_IS_ENABLED_DAILY.pixelName to PixelParameter.removeAtb(),
235235
DUCK_CHAT_SEARCH_ASSIST_SETTINGS_BUTTON_CLICKED.pixelName to PixelParameter.removeAtb(),
236-
DUCK_CHAT_HIDE_AI_GENERATED_IMAGES_BUTTON_CLICKED.pixelName to PixelParameter.removeAtb(),
236+
SERP_SETTINGS_OPEN_HIDE_AI_GENERATED_IMAGES.pixelName to PixelParameter.removeAtb(),
237237
DUCK_CHAT_START_NEW_CONVERSATION.pixelName to PixelParameter.removeAtb(),
238238
DUCK_CHAT_START_NEW_CONVERSATION_BUTTON_CLICKED.pixelName to PixelParameter.removeAtb(),
239239
DUCK_CHAT_OPEN_HISTORY.pixelName to PixelParameter.removeAtb(),

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ class DuckChatSettingsViewModel @AssistedInject constructor(
168168
titleRes = R.string.duckAiSerpSettingsTitle,
169169
),
170170
)
171-
pixel.fire(DuckChatPixelName.DUCK_CHAT_HIDE_AI_GENERATED_IMAGES_BUTTON_CLICKED)
171+
pixel.fire(DuckChatPixelName.SERP_SETTINGS_OPEN_HIDE_AI_GENERATED_IMAGES)
172172
}
173173
}
174174

duckchat/duckchat-impl/src/test/kotlin/com/duckduckgo/duckchat/impl/ui/settings/DuckChatSettingsViewModelTest.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,4 +550,11 @@ class DuckChatSettingsViewModelTest {
550550
assertFalse(state.isFullScreenModeEnabled)
551551
}
552552
}
553+
554+
@Test
555+
fun `when onDuckAiHideAiGeneratedImagesClicked then pixel is fired`() =
556+
runTest {
557+
testee.onDuckAiHideAiGeneratedImagesClicked()
558+
verify(mockPixel).fire(DuckChatPixelName.SERP_SETTINGS_OPEN_HIDE_AI_GENERATED_IMAGES)
559+
}
553560
}

settings/settings-impl/src/main/java/com/duckduckgo/settings/impl/serpsettings/messaging/OpenNativeSettingsHandler.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package com.duckduckgo.settings.impl.serpsettings.messaging
1919
import android.content.Context
2020
import android.content.Intent
2121
import com.duckduckgo.app.di.AppCoroutineScope
22+
import com.duckduckgo.app.statistics.pixels.Pixel
2223
import com.duckduckgo.common.utils.AppUrl
2324
import com.duckduckgo.common.utils.DispatcherProvider
2425
import com.duckduckgo.contentscopescripts.api.ContentScopeJsMessageHandlersPlugin
@@ -30,6 +31,7 @@ import com.duckduckgo.js.messaging.api.JsMessageHandler
3031
import com.duckduckgo.js.messaging.api.JsMessaging
3132
import com.duckduckgo.navigation.api.GlobalActivityStarter
3233
import com.duckduckgo.settings.api.SettingsPageFeature
34+
import com.duckduckgo.settings.impl.serpsettings.pixel.SerpSettingsPixelName.SERP_SETTINGS_OPEN_DUCK_AI
3335
import com.squareup.anvil.annotations.ContributesMultibinding
3436
import kotlinx.coroutines.CoroutineScope
3537
import kotlinx.coroutines.launch
@@ -47,6 +49,7 @@ class OpenNativeSettingsHandler @Inject constructor(
4749
private val context: Context,
4850
private val globalActivityStarter: GlobalActivityStarter,
4951
private val settingsPageFeature: SettingsPageFeature,
52+
private val pixel: Pixel,
5053
) : ContentScopeJsMessageHandlersPlugin {
5154

5255
override fun getJsMessageHandler(): JsMessageHandler =
@@ -63,6 +66,7 @@ class OpenNativeSettingsHandler @Inject constructor(
6366

6467
when (val screenParam = params.optString("screen", "")) {
6568
AI_FEATURES_SCREEN_NAME -> {
69+
pixel.fire(SERP_SETTINGS_OPEN_DUCK_AI)
6670
val intent = globalActivityStarter.startIntent(context, DuckChatNativeSettingsNoParams)
6771
intent?.flags = Intent.FLAG_ACTIVITY_NEW_TASK
6872
context.startActivity(intent)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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.settings.impl.serpsettings.pixel
18+
19+
import com.duckduckgo.app.statistics.pixels.Pixel
20+
import com.duckduckgo.common.utils.plugins.pixel.PixelParamRemovalPlugin
21+
import com.duckduckgo.common.utils.plugins.pixel.PixelParamRemovalPlugin.PixelParameter
22+
import com.duckduckgo.di.scopes.AppScope
23+
import com.duckduckgo.settings.impl.serpsettings.pixel.SerpSettingsPixelName.SERP_SETTINGS_OPEN_DUCK_AI
24+
import com.squareup.anvil.annotations.ContributesMultibinding
25+
import javax.inject.Inject
26+
27+
enum class SerpSettingsPixelName(override val pixelName: String) : Pixel.PixelName {
28+
SERP_SETTINGS_OPEN_DUCK_AI("serp_settings_open_duck-ai"),
29+
}
30+
31+
@ContributesMultibinding(AppScope::class)
32+
class SerpSettingsPixelParamRemovalPlugin @Inject constructor() : PixelParamRemovalPlugin {
33+
override fun names(): List<Pair<String, Set<PixelParameter>>> {
34+
return listOf(
35+
SERP_SETTINGS_OPEN_DUCK_AI.pixelName to PixelParameter.removeAtb(),
36+
)
37+
}
38+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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.settings.impl.serpsettings.fakes
18+
19+
import android.content.Context
20+
import android.content.Intent
21+
import android.os.Bundle
22+
import com.duckduckgo.navigation.api.GlobalActivityStarter
23+
import com.duckduckgo.navigation.api.GlobalActivityStarter.ActivityParams
24+
25+
class FakeGlobalActivityStarter : GlobalActivityStarter {
26+
val startedActivities = mutableListOf<ActivityParams>()
27+
val startedDeeplinkActivities = mutableListOf<GlobalActivityStarter.DeeplinkActivityParams>()
28+
var intentToReturn: Intent? = null
29+
30+
override fun start(
31+
context: Context,
32+
params: ActivityParams,
33+
options: Bundle?,
34+
) {
35+
startedActivities.add(params)
36+
}
37+
38+
override fun start(
39+
context: Context,
40+
deeplinkActivityParams: GlobalActivityStarter.DeeplinkActivityParams,
41+
options: Bundle?,
42+
) {
43+
startedDeeplinkActivities.add(deeplinkActivityParams)
44+
}
45+
46+
override fun startIntent(context: Context, params: ActivityParams): Intent? {
47+
startedActivities.add(params)
48+
return intentToReturn
49+
}
50+
51+
override fun startIntent(
52+
context: Context,
53+
deeplinkActivityParams: GlobalActivityStarter.DeeplinkActivityParams,
54+
): Intent? {
55+
startedDeeplinkActivities.add(deeplinkActivityParams)
56+
return intentToReturn
57+
}
58+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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.settings.impl.serpsettings.fakes
18+
19+
import com.duckduckgo.app.statistics.pixels.Pixel
20+
21+
class FakePixel : Pixel {
22+
val firedPixels = mutableListOf<String>()
23+
24+
override fun fire(
25+
pixel: Pixel.PixelName,
26+
parameters: Map<String, String>,
27+
encodedParameters: Map<String, String>,
28+
type: Pixel.PixelType,
29+
) {
30+
firedPixels.add(pixel.pixelName)
31+
}
32+
33+
override fun fire(
34+
pixelName: String,
35+
parameters: Map<String, String>,
36+
encodedParameters: Map<String, String>,
37+
type: Pixel.PixelType,
38+
) {
39+
firedPixels.add(pixelName)
40+
}
41+
42+
override fun enqueueFire(
43+
pixel: Pixel.PixelName,
44+
parameters: Map<String, String>,
45+
encodedParameters: Map<String, String>,
46+
) {
47+
firedPixels.add(pixel.pixelName)
48+
}
49+
50+
override fun enqueueFire(
51+
pixelName: String,
52+
parameters: Map<String, String>,
53+
encodedParameters: Map<String, String>,
54+
) {
55+
firedPixels.add(pixelName)
56+
}
57+
}

0 commit comments

Comments
 (0)