Skip to content
4 changes: 1 addition & 3 deletions .maestro/input_screen/input_screen_chat_mode.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ tags:
# enable the Input Screen
- runFlow: ../shared/open_ai_settings_screen.yaml
- tapOn:
id: "trailingSwitch"
childOf:
id: "duckAiInputScreenEnabledToggle"
id: "duckAiInputScreenToggleWithAiImage"
- action: back
- action: back

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ tags:
# enable the Input Screen
- runFlow: ../shared/open_ai_settings_screen.yaml
- tapOn:
id: "trailingSwitch"
childOf:
id: "duckAiInputScreenEnabledToggle"
id: "duckAiInputScreenToggleWithAiImage"
- action: back
- action: back

Expand Down
4 changes: 1 addition & 3 deletions .maestro/input_screen/input_screen_search_mode.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ tags:
# enable the Input Screen
- runFlow: ../shared/open_ai_settings_screen.yaml
- tapOn:
id: "trailingSwitch"
childOf:
id: "duckAiInputScreenEnabledToggle"
id: "duckAiInputScreenToggleWithAiImage"
- action: back
- action: back

Expand Down
28 changes: 28 additions & 0 deletions common/common-ui/src/main/res/drawable/ic_check_accent_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!--
~ Copyright (c) 2025 DuckDuckGo
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,2C6.477,2 2,6.477 2,12s4.477,10 10,10 10,-4.477 10,-10S17.523,2 12,2Z"
android:fillColor="?attr/daxColorAccentBlue"/>
<path
android:pathData="M16.775,9.214c0.296,0.29 0.3,0.765 0.011,1.06l-4.494,4.589c-0.785,0.8 -2.074,0.8 -2.859,0l-2.219,-2.265a0.75,0.75 0,0 1,1.072 -1.05l2.219,2.265c0.196,0.2 0.519,0.2 0.715,0l4.494,-4.588a0.75,0.75 0,0 1,1.061 -0.01Z"
android:fillColor="?attr/daxColorAccentContentPrimary"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,2C17.523,2 22,6.477 22,12C22,17.523 17.523,22 12,22C6.477,22 2,17.523 2,12C2,6.477 6.477,2 12,2ZM12,3.5C7.306,3.5 3.5,7.306 3.5,12C3.5,16.694 7.306,20.5 12,20.5C16.694,20.5 20.5,16.694 20.5,12C20.5,7.306 16.694,3.5 12,3.5Z"
android:fillColor="?attr/daxColorIconDisabled"
android:fillType="evenOdd"/>
</vector>
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
<!-- Inside each category there are different colors, which define where are applied -->

<!-- Dark Palette -->
<!-- Accents -->
<color name="accent_content_primary_dark">@color/blue80</color>

<!-- Backgrounds -->
<color name="background_background_dark">#27282A</color>
<color name="background_surface_dark">#333538</color>
Expand All @@ -47,6 +50,9 @@
<color name="controls_decoration_tertiary_dark">#B8F9F9F9</color>

<!-- Light Palette -->
<!-- Accents -->
<color name="accent_content_primary_light">@color/white</color>

<!-- Backgrounds -->
<color name="background_background_light">#F2F2F2</color>
<color name="background_surface_light">#F9F9F9</color>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<item name="daxColorControlFillPrimary">@color/controls_fill_primary_dark</item>
<item name="daxColorControlFillSecondary">@color/controls_fill_secondary_dark</item>
<item name="daxColorControlFillTertiary">@color/controls_fill_tertiary_dark</item>
<item name="daxColorControlDecorationTertiary">@color/controls_decoration_tertiary_dark</item>

<!-- Widgets -->
<item name="daxColorToolbar">?attr/daxColorBackground</item>
Expand Down Expand Up @@ -83,6 +84,7 @@
<item name="daxColorControlFillPrimary">@color/controls_fill_primary_light</item>
<item name="daxColorControlFillSecondary">@color/controls_fill_secondary_light</item>
<item name="daxColorControlFillTertiary">@color/controls_fill_tertiary_light</item>
<item name="daxColorControlDecorationTertiary">@color/controls_decoration_tertiary_light</item>

<!-- Widgets -->
<item name="daxColorToolbar">?attr/daxColorBackground</item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
<attr name="daxColorLines" format="color"/>
<attr name="daxColorAccentBlue" format="color"/>
<attr name="daxColorAccentYellow" format="color"/>
<attr name="daxColorAccentContentPrimary" format="color"/>
<attr name="daxColorContainerDisabled" format="color"/>
<attr name="daxColorTextDisabled" format="color"/>
<attr name="daxColorRipple" format="color"/>
Expand All @@ -74,6 +75,7 @@
<attr name="daxColorControlFillPrimary" format="color"/>
<attr name="daxColorControlFillSecondary" format="color"/>
<attr name="daxColorControlFillTertiary" format="color"/>
<attr name="daxColorControlDecorationTertiary" format="color"/>

<attr name="daxColorButtonPrimaryContainer" format="color"/>
<attr name="daxColorButtonPrimaryContainerPressed" format="color"/>
Expand Down Expand Up @@ -191,6 +193,7 @@
<color name="blue50_14">#243969EF</color>
<color name="blue60">#2B55CA</color>
<color name="blue70">#1E42A4</color>
<color name="blue80">#14307E</color>

<color name="black84">#D6000000</color>
<color name="black60">#99000000</color>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@
<item name="daxColorLines">@color/white9</item>
<item name="daxColorAccentBlue">@color/blue30</item>
<item name="daxColorAccentYellow">@color/yellow50</item>
<item name="daxColorAccentContentPrimary">@color/accent_content_primary_dark</item>
<item name="daxColorRipple">@color/white12</item>
<item name="daxLogoTitleText">@color/white</item>
<item name="daxOmnibarTextColorHighlight">@color/blue30_20</item>
Expand Down Expand Up @@ -289,6 +290,7 @@
<item name="daxColorLines">@color/black9</item>
<item name="daxColorAccentBlue">@color/blue50</item>
<item name="daxColorAccentYellow">@color/yellow50</item>
<item name="daxColorAccentContentPrimary">@color/accent_content_primary_light</item>
<item name="daxColorRipple">@color/black6</item>
<item name="daxLogoTitleText">@color/gray85</item>
<item name="daxOmnibarTextColorHighlight">@color/blue50_20</item>
Expand Down
7 changes: 6 additions & 1 deletion duckchat/duckchat-impl/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@

<application>
<activity
android:name="com.duckduckgo.duckchat.impl.ui.DuckChatSettingsActivity"
android:name="com.duckduckgo.duckchat.impl.ui.settings.DuckChatSettingsActivity"
android:exported="false"
android:label="@string/duck_chat_title"
android:parentActivityName="com.duckduckgo.app.settings.SettingsActivity" />
<activity
android:name="com.duckduckgo.duckchat.impl.ui.settings.DuckAiShortcutSettingsActivity"
android:exported="false"
android:label="@string/duck_ai_shortcut_settings_title"
android:parentActivityName="com.duckduckgo.duckchat.impl.ui.settings.DuckChatSettingsActivity" />
<activity
android:name="com.duckduckgo.duckchat.impl.ui.DuckChatWebViewActivity"
android:exported="false"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2025 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.duckchat.impl.ui.settings

import android.os.Bundle
import android.widget.CompoundButton
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import com.duckduckgo.anvil.annotations.InjectWith
import com.duckduckgo.common.ui.DuckDuckGoActivity
import com.duckduckgo.common.ui.viewbinding.viewBinding
import com.duckduckgo.di.scopes.ActivityScope
import com.duckduckgo.duckchat.impl.databinding.ActivityDuckAiShortcutSettingsBinding
import com.duckduckgo.duckchat.impl.ui.settings.DuckAiShortcutSettingsViewModel.ViewState
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

@InjectWith(ActivityScope::class)
class DuckAiShortcutSettingsActivity : DuckDuckGoActivity() {

private val viewModel: DuckAiShortcutSettingsViewModel by bindViewModel()
private val binding: ActivityDuckAiShortcutSettingsBinding by viewBinding()

private val menuToggleListener =
CompoundButton.OnCheckedChangeListener { _, isChecked ->
viewModel.onShowDuckChatInMenuToggled(isChecked)
}

private val addressBarToggleListener =
CompoundButton.OnCheckedChangeListener { _, isChecked ->
viewModel.onShowDuckChatInAddressBarToggled(isChecked)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContentView(binding.root)

setupToolbar(binding.includeToolbar.toolbar)

observeViewModel()
}

private fun observeViewModel() {
viewModel.viewState
.flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED)
.onEach { renderViewState(it) }
.launchIn(lifecycleScope)
}

private fun renderViewState(viewState: ViewState) {
binding.showDuckAiInMenuToggle.apply {
quietlySetIsChecked(viewState.showInBrowserMenu, menuToggleListener)
}
binding.showDuckAiInAddressBarToggle.apply {
isVisible = viewState.shouldShowAddressBarToggle
quietlySetIsChecked(viewState.showInAddressBar, addressBarToggleListener)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2025 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.duckchat.impl.ui.settings

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.duckduckgo.anvil.annotations.ContributesViewModel
import com.duckduckgo.di.scopes.ActivityScope
import com.duckduckgo.duckchat.impl.DuckChatInternal
import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

@ContributesViewModel(ActivityScope::class)
class DuckAiShortcutSettingsViewModel @Inject constructor(
private val duckChat: DuckChatInternal,
) : ViewModel() {

data class ViewState(
val showInBrowserMenu: Boolean = false,
val showInAddressBar: Boolean = false,
val shouldShowAddressBarToggle: Boolean = false,
)

val viewState = combine(
duckChat.observeShowInBrowserMenuUserSetting(),
duckChat.observeShowInAddressBarUserSetting(),
) { showInBrowserMenu, showInAddressBar ->
ViewState(
showInBrowserMenu = showInBrowserMenu,
showInAddressBar = showInAddressBar,
shouldShowAddressBarToggle = duckChat.isAddressBarEntryPointEnabled(),
)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), ViewState())

fun onShowDuckChatInMenuToggled(checked: Boolean) {
viewModelScope.launch {
duckChat.setShowInBrowserMenuUserSetting(checked)
}
}

fun onShowDuckChatInAddressBarToggled(checked: Boolean) {
viewModelScope.launch {
duckChat.setShowInAddressBarUserSetting(checked)
}
}
}
Loading
Loading