Skip to content

Commit f4bbf0a

Browse files
0nkodaxmobile
andauthored
Duck chat: Search Assist link feature (#6132)
Task/Issue URL: https://app.asana.com/1/137249556945/project/1207418217763355/task/1210188925025667?focus=true ### Description This PR adds the Search Assist settings link to Duck.AI settings. ### Steps to test this PR QA-optional, this is just a feature branch and it's already been tested. --------- Co-authored-by: Dax The Translator <daxmobile@duckduckgo.com>
1 parent 1338587 commit f4bbf0a

File tree

33 files changed

+227
-17
lines changed

33 files changed

+227
-17
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24">
6+
<path
7+
android:pathData="M15.624,2.496c-0.162,-0.65 -1.085,-0.65 -1.248,0l-0.38,1.52a5.47,5.47 0,0 1,-3.98 3.98l-1.52,0.38c-0.649,0.162 -0.649,1.086 0,1.248l1.52,0.38a5.47,5.47 0,0 1,3.98 3.98l0.38,1.52c0.163,0.65 1.086,0.65 1.248,0l0.38,-1.52a5.47,5.47 0,0 1,3.98 -3.98l1.52,-0.38c0.65,-0.162 0.65,-1.086 0,-1.248l-1.52,-0.38a5.47,5.47 0,0 1,-3.98 -3.98l-0.38,-1.52ZM6.984,7.634C7.124,7.46 6.992,7 6.769,7L2.75,7a0.75,0.75 0,0 0,0 1.5h3.417c0.233,0 0.43,-0.164 0.522,-0.379 0.075,-0.173 0.173,-0.338 0.295,-0.487Z"
8+
android:fillColor="?attr/daxColorPrimaryIcon"/>
9+
<path
10+
android:pathData="M12.06,13.214a0.463,0.463 0,0 0,-0.399 -0.214h-8.91a0.75,0.75 0,0 0,0 1.5h9.188c0.325,0 0.566,-0.307 0.45,-0.611a4.038,4.038 0,0 0,-0.329 -0.675ZM18,19.75a0.75,0.75 0,0 1,-0.75 0.75H2.75a0.75,0.75 0,0 1,0 -1.5h14.5a0.75,0.75 0,0 1,0.75 0.75Z"
11+
android:fillColor="?attr/daxColorPrimaryIcon"/>
12+
</vector>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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.annotation.UiThread
20+
import androidx.lifecycle.LifecycleOwner
21+
import com.duckduckgo.app.di.AppCoroutineScope
22+
import com.duckduckgo.app.lifecycle.MainProcessLifecycleObserver
23+
import com.duckduckgo.app.statistics.pixels.Pixel
24+
import com.duckduckgo.app.statistics.pixels.Pixel.PixelParameter
25+
import com.duckduckgo.app.statistics.pixels.Pixel.PixelType.Daily
26+
import com.duckduckgo.common.utils.DispatcherProvider
27+
import com.duckduckgo.di.scopes.AppScope
28+
import com.duckduckgo.duckchat.impl.repository.DuckChatFeatureRepository
29+
import com.squareup.anvil.annotations.ContributesMultibinding
30+
import javax.inject.Inject
31+
import kotlinx.coroutines.CoroutineScope
32+
import kotlinx.coroutines.launch
33+
34+
@ContributesMultibinding(
35+
scope = AppScope::class,
36+
boundType = MainProcessLifecycleObserver::class,
37+
)
38+
class DuckChatDailyPixelSender @Inject constructor(
39+
private val pixel: Pixel,
40+
private val duckChatFeatureRepository: DuckChatFeatureRepository,
41+
private val dispatcherProvider: DispatcherProvider,
42+
@AppCoroutineScope private val coroutineScope: CoroutineScope,
43+
) : MainProcessLifecycleObserver {
44+
45+
@UiThread
46+
override fun onStart(owner: LifecycleOwner) {
47+
coroutineScope.launch(dispatcherProvider.io()) {
48+
pixel.fire(
49+
pixel = DuckChatPixelName.DUCK_CHAT_IS_ENABLED_DAILY,
50+
parameters = mapOf(PixelParameter.IS_ENABLED to duckChatFeatureRepository.isDuckChatUserEnabled().toString()),
51+
type = Daily(),
52+
)
53+
pixel.fire(
54+
pixel = DuckChatPixelName.DUCK_CHAT_BROWSER_MENU_IS_ENABLED_DAILY,
55+
parameters = mapOf(PixelParameter.IS_ENABLED to duckChatFeatureRepository.shouldShowInBrowserMenu().toString()),
56+
type = Daily(),
57+
)
58+
pixel.fire(
59+
pixel = DuckChatPixelName.DUCK_CHAT_ADDRESS_BAR_IS_ENABLED_DAILY,
60+
parameters = mapOf(PixelParameter.IS_ENABLED to duckChatFeatureRepository.shouldShowInAddressBar().toString()),
61+
type = Daily(),
62+
)
63+
}
64+
}
65+
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ import com.duckduckgo.app.statistics.pixels.Pixel
2020
import com.duckduckgo.common.utils.plugins.pixel.PixelParamRemovalPlugin
2121
import com.duckduckgo.common.utils.plugins.pixel.PixelParamRemovalPlugin.PixelParameter
2222
import com.duckduckgo.di.scopes.AppScope
23+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_ADDRESS_BAR_IS_ENABLED_DAILY
24+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_BROWSER_MENU_IS_ENABLED_DAILY
2325
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_EXPERIMENT_SEARCHBAR_BUTTON_OPEN
26+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_IS_ENABLED_DAILY
2427
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_MENU_SETTING_OFF
2528
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_MENU_SETTING_ON
2629
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_OPEN
@@ -29,6 +32,7 @@ import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_OPEN_NEW_T
2932
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_SEARCHBAR_BUTTON_OPEN
3033
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_SEARCHBAR_SETTING_OFF
3134
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_SEARCHBAR_SETTING_ON
35+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_SEARCH_ASSIST_SETTINGS_BUTTON_CLICKED
3236
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_SETTINGS_DISPLAYED
3337
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_SETTINGS_PRESSED
3438
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName.DUCK_CHAT_USER_DISABLED
@@ -50,6 +54,10 @@ enum class DuckChatPixelName(override val pixelName: String) : Pixel.PixelName {
5054
DUCK_CHAT_SETTINGS_DISPLAYED("m_aichat_settings_displayed"),
5155
DUCK_CHAT_SEARCHBAR_BUTTON_OPEN("aichat_searchbar_button_open"),
5256
DUCK_CHAT_EXPERIMENT_SEARCHBAR_BUTTON_OPEN("aichat_experiment_searchbar_button_open"),
57+
DUCK_CHAT_IS_ENABLED_DAILY("aichat_is_enabled_daily"),
58+
DUCK_CHAT_BROWSER_MENU_IS_ENABLED_DAILY("aichat_browser_menu_is_enabled_daily"),
59+
DUCK_CHAT_ADDRESS_BAR_IS_ENABLED_DAILY("aichat_address_bar_is_enabled_daily"),
60+
DUCK_CHAT_SEARCH_ASSIST_SETTINGS_BUTTON_CLICKED("aichat_search_assist_settings_button_clicked"),
5361
}
5462

5563
@ContributesMultibinding(AppScope::class)
@@ -69,6 +77,10 @@ class DuckChatParamRemovalPlugin @Inject constructor() : PixelParamRemovalPlugin
6977
DUCK_CHAT_SETTINGS_DISPLAYED.pixelName to PixelParameter.removeAtb(),
7078
DUCK_CHAT_SEARCHBAR_BUTTON_OPEN.pixelName to PixelParameter.removeAtb(),
7179
DUCK_CHAT_EXPERIMENT_SEARCHBAR_BUTTON_OPEN.pixelName to PixelParameter.removeAtb(),
80+
DUCK_CHAT_IS_ENABLED_DAILY.pixelName to PixelParameter.removeAtb(),
81+
DUCK_CHAT_BROWSER_MENU_IS_ENABLED_DAILY.pixelName to PixelParameter.removeAtb(),
82+
DUCK_CHAT_ADDRESS_BAR_IS_ENABLED_DAILY.pixelName to PixelParameter.removeAtb(),
83+
DUCK_CHAT_SEARCH_ASSIST_SETTINGS_BUTTON_CLICKED.pixelName to PixelParameter.removeAtb(),
7284
)
7385
}
7486
}

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import androidx.lifecycle.lifecycleScope
2626
import com.duckduckgo.anvil.annotations.ContributeToActivityStarter
2727
import com.duckduckgo.anvil.annotations.InjectWith
2828
import com.duckduckgo.app.statistics.pixels.Pixel
29+
import com.duckduckgo.app.tabs.BrowserNav
2930
import com.duckduckgo.browser.api.ui.BrowserScreens.WebViewActivityWithParams
3031
import com.duckduckgo.common.ui.DuckDuckGoActivity
3132
import com.duckduckgo.common.ui.spans.DuckDuckGoClickableSpan
@@ -67,6 +68,9 @@ class DuckChatSettingsActivity : DuckDuckGoActivity() {
6768
@Inject
6869
lateinit var globalActivityStarter: GlobalActivityStarter
6970

71+
@Inject
72+
lateinit var browserNav: BrowserNav
73+
7074
@Inject
7175
lateinit var pixel: Pixel
7276

@@ -118,19 +122,25 @@ class DuckChatSettingsActivity : DuckDuckGoActivity() {
118122
isVisible = viewState.shouldShowAddressBarToggle
119123
quietlySetIsChecked(viewState.showInAddressBar, addressBarToggleListener)
120124
}
125+
binding.showDuckChatSearchSettingsLink.setOnClickListener {
126+
viewModel.duckChatSearchAISettingsClicked()
127+
}
121128
}
122129

123130
private fun processCommand(command: DuckChatSettingsViewModel.Command) {
124131
when (command) {
125-
is DuckChatSettingsViewModel.Command.OpenLearnMore -> {
132+
is DuckChatSettingsViewModel.Command.OpenLink -> {
126133
globalActivityStarter.start(
127134
this,
128135
WebViewActivityWithParams(
129-
url = command.learnMoreLink,
136+
url = command.link,
130137
screenTitle = getString(R.string.duck_chat_title),
131138
),
132139
)
133140
}
141+
is DuckChatSettingsViewModel.Command.OpenLinkInNewTab -> {
142+
startActivity(browserNav.openInNewTab(this@DuckChatSettingsActivity, command.link))
143+
}
134144
}
135145
}
136146
}

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ package com.duckduckgo.duckchat.impl.ui
1919
import androidx.lifecycle.ViewModel
2020
import androidx.lifecycle.viewModelScope
2121
import com.duckduckgo.anvil.annotations.ContributesViewModel
22+
import com.duckduckgo.app.statistics.pixels.Pixel
2223
import com.duckduckgo.di.scopes.ActivityScope
2324
import com.duckduckgo.duckchat.impl.DuckChatInternal
24-
import com.duckduckgo.duckchat.impl.ui.DuckChatSettingsViewModel.Command.OpenLearnMore
25+
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName
26+
import com.duckduckgo.duckchat.impl.ui.DuckChatSettingsViewModel.Command.OpenLink
27+
import com.duckduckgo.duckchat.impl.ui.DuckChatSettingsViewModel.Command.OpenLinkInNewTab
2528
import javax.inject.Inject
2629
import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST
2730
import kotlinx.coroutines.channels.Channel
@@ -34,6 +37,7 @@ import kotlinx.coroutines.launch
3437
@ContributesViewModel(ActivityScope::class)
3538
class DuckChatSettingsViewModel @Inject constructor(
3639
private val duckChat: DuckChatInternal,
40+
private val pixel: Pixel,
3741
) : ViewModel() {
3842

3943
private val commandChannel = Channel<Command>(capacity = 1, onBufferOverflow = DROP_OLDEST)
@@ -62,7 +66,8 @@ class DuckChatSettingsViewModel @Inject constructor(
6266
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), ViewState())
6367

6468
sealed class Command {
65-
data class OpenLearnMore(val learnMoreLink: String) : Command()
69+
data class OpenLink(val link: String) : Command()
70+
data class OpenLinkInNewTab(val link: String) : Command()
6671
}
6772

6873
fun onDuckChatUserEnabledToggled(checked: Boolean) {
@@ -85,7 +90,19 @@ class DuckChatSettingsViewModel @Inject constructor(
8590

8691
fun duckChatLearnMoreClicked() {
8792
viewModelScope.launch {
88-
commandChannel.send(OpenLearnMore("https://duckduckgo.com/duckduckgo-help-pages/aichat/"))
93+
commandChannel.send(OpenLink(DUCK_CHAT_LEARN_MORE_LINK))
8994
}
9095
}
96+
97+
fun duckChatSearchAISettingsClicked() {
98+
viewModelScope.launch {
99+
commandChannel.send(OpenLinkInNewTab(DUCK_CHAT_SEARCH_AI_SETTINGS_LINK))
100+
pixel.fire(DuckChatPixelName.DUCK_CHAT_SEARCH_ASSIST_SETTINGS_BUTTON_CLICKED)
101+
}
102+
}
103+
104+
companion object {
105+
const val DUCK_CHAT_LEARN_MORE_LINK = "https://duckduckgo.com/duckduckgo-help-pages/aichat/"
106+
const val DUCK_CHAT_SEARCH_AI_SETTINGS_LINK = "https://duckduckgo.com/settings?ko=-1#aifeatures"
107+
}
91108
}

duckchat/duckchat-impl/src/main/res/layout/activity_duck_chat_settings.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,23 @@
9797
android:layout_height="wrap_content"
9898
app:primaryText="@string/duck_chat_address_bar_visibility_setting"
9999
app:showSwitch="true"/>
100+
101+
<com.duckduckgo.common.ui.view.divider.HorizontalDivider
102+
android:layout_width="match_parent"
103+
android:layout_height="wrap_content"
104+
android:id="@+id/divider2"/>
105+
106+
<com.duckduckgo.common.ui.view.listitem.TwoLineListItem
107+
android:id="@+id/showDuckChatSearchSettingsLink"
108+
android:layout_width="match_parent"
109+
android:layout_height="wrap_content"
110+
app:primaryText="@string/duck_chat_assist_settings_title"
111+
app:secondaryText="@string/duck_chat_assist_settings_description"
112+
app:leadingIcon="@drawable/ic_assist_24"
113+
app:leadingIconBackground="circular"
114+
app:showSwitch="false"/>
115+
116+
100117
</LinearLayout>
101118
</ScrollView>
102119
</androidx.coordinatorlayout.widget.CoordinatorLayout>

duckchat/duckchat-impl/src/main/res/values-bg/strings-duckchat.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@
2727
<string name="duck_chat_show_in_heading">Бързи Връзки</string>
2828
<string name="duck_chat_visibility_setting">Меню на браузъра</string>
2929
<string name="duck_chat_address_bar_visibility_setting">Адресна лента</string>
30+
<string name="duck_chat_assist_settings_title">Настройки на Assist</string>
31+
<string name="duck_chat_assist_settings_description">Изберете колко често искате да се появяват отговори, подпомагани от изкуствен интелект, в търсенето</string>
3032
</resources>

duckchat/duckchat-impl/src/main/res/values-cs/strings-duckchat.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@
2727
<string name="duck_chat_show_in_heading">Zkratky</string>
2828
<string name="duck_chat_visibility_setting">Menu prohlížeče</string>
2929
<string name="duck_chat_address_bar_visibility_setting">Adresní řádek</string>
30+
<string name="duck_chat_assist_settings_title">Nastavení funkce Assist</string>
31+
<string name="duck_chat_assist_settings_description">Vyber, jak často chceš, aby se odpovědi s podporou umělé inteligence zobrazovaly ve tvých vyhledáváních</string>
3032
</resources>

duckchat/duckchat-impl/src/main/res/values-da/strings-duckchat.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@
2727
<string name="duck_chat_show_in_heading">Genveje</string>
2828
<string name="duck_chat_visibility_setting">Browsermenu</string>
2929
<string name="duck_chat_address_bar_visibility_setting">Adresselinje</string>
30+
<string name="duck_chat_assist_settings_title">Assist-indstillinger</string>
31+
<string name="duck_chat_assist_settings_description">Vælg, hvor ofte du vil have AI-assisterede svar til at optræde i dine søgninger</string>
3032
</resources>

duckchat/duckchat-impl/src/main/res/values-de/strings-duckchat.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@
2727
<string name="duck_chat_show_in_heading">Shortcuts</string>
2828
<string name="duck_chat_visibility_setting">Browsermenü</string>
2929
<string name="duck_chat_address_bar_visibility_setting">Adresszeile</string>
30+
<string name="duck_chat_assist_settings_title">Assist-Einstellungen</string>
31+
<string name="duck_chat_assist_settings_description">Du kannst auswählen, wie oft KI-gestützte Antworten in deinen Suchergebnissen angezeigt werden sollen.</string>
3032
</resources>

0 commit comments

Comments
 (0)