Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ import com.duckduckgo.savedsites.api.models.SavedSite.Favorite
import com.duckduckgo.savedsites.impl.SavedSitesPixelName
import com.duckduckgo.serp.logos.api.SerpEasterEggLogosToggles
import com.duckduckgo.serp.logos.api.SerpLogo
import com.duckduckgo.settings.api.SettingsPageFeature
import com.duckduckgo.settings.api.SerpSettingsFeature
import com.duckduckgo.site.permissions.api.SitePermissionsManager
import com.duckduckgo.site.permissions.api.SitePermissionsManager.LocationPermissionRequest
import com.duckduckgo.site.permissions.api.SitePermissionsManager.SitePermissionQueryResponse
Expand Down Expand Up @@ -623,10 +623,9 @@ class BrowserTabViewModelTest {
private val mockDeviceAppLookup: DeviceAppLookup = mock()

private val mockDuckAiFullScreenMode = MutableStateFlow(false)
private val mockDuckAiFullScreenModeEnabled = MutableStateFlow(false)

private lateinit var fakeContentScopeScriptsSubscriptionEventPluginPoint: FakeContentScopeScriptsSubscriptionEventPluginPoint
private var fakeSettingsPageFeature = FakeFeatureToggleFactory.create(SettingsPageFeature::class.java)
private var serpSettingsFeature = FakeFeatureToggleFactory.create(SerpSettingsFeature::class.java)

@Before
fun before() =
Expand Down Expand Up @@ -864,7 +863,7 @@ class BrowserTabViewModelTest {
autoconsentPixelManager = mockAutoconsentPixelManager,
omnibarRepository = mockOmnibarFeatureRepository,
contentScopeScriptsSubscriptionEventPluginPoint = fakeContentScopeScriptsSubscriptionEventPluginPoint,
settingsPageFeature = fakeSettingsPageFeature,
serpSettingsFeature = serpSettingsFeature,
)

testee.loadData("abc", null, false, false)
Expand Down Expand Up @@ -7924,7 +7923,7 @@ class BrowserTabViewModelTest {

@Test
fun whenOnViewResumedWithNoPluginsThenNoSubscriptionEventsSent() = runTest {
fakeSettingsPageFeature.serpSettingsSync().setRawStoredState(State(enable = true))
serpSettingsFeature.storeSerpSettings().setRawStoredState(State(enable = true))

testee.onViewResumed()

Expand All @@ -7936,7 +7935,7 @@ class BrowserTabViewModelTest {

@Test
fun whenOnViewResumedWithPluginsThenSubscriptionEventsSent() = runTest {
fakeSettingsPageFeature.serpSettingsSync().setRawStoredState(State(enable = true))
serpSettingsFeature.storeSerpSettings().setRawStoredState(State(enable = true))
val events = mutableListOf<SubscriptionEventData>().apply {
add(
SubscriptionEventData(
Expand Down Expand Up @@ -7973,7 +7972,7 @@ class BrowserTabViewModelTest {

@Test
fun whenOnViewResumedWithPluginsAndSerpSettingsFeatureFlagOffThenNoEventsSent() = runTest {
fakeSettingsPageFeature.serpSettingsSync().setRawStoredState(State(enable = false))
serpSettingsFeature.storeSerpSettings().setRawStoredState(State(enable = false))
val events = mutableListOf<SubscriptionEventData>().apply {
add(
SubscriptionEventData(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ import com.duckduckgo.savedsites.impl.dialogs.EditSavedSiteDialogFragment.Delete
import com.duckduckgo.savedsites.impl.dialogs.EditSavedSiteDialogFragment.EditSavedSiteListener
import com.duckduckgo.serp.logos.api.SerpEasterEggLogosToggles
import com.duckduckgo.serp.logos.api.SerpLogo
import com.duckduckgo.settings.api.SettingsPageFeature
import com.duckduckgo.settings.api.SerpSettingsFeature
import com.duckduckgo.site.permissions.api.SitePermissionsManager
import com.duckduckgo.site.permissions.api.SitePermissionsManager.LocationPermissionRequest
import com.duckduckgo.site.permissions.api.SitePermissionsManager.SitePermissionQueryResponse
Expand Down Expand Up @@ -495,7 +495,7 @@ class BrowserTabViewModel @Inject constructor(
private val autoconsentPixelManager: AutoconsentPixelManager,
private val omnibarRepository: OmnibarRepository,
private val contentScopeScriptsSubscriptionEventPluginPoint: PluginPoint<ContentScopeScriptsSubscriptionEventPlugin>,
private val settingsPageFeature: SettingsPageFeature,
private val serpSettingsFeature: SerpSettingsFeature,
) : ViewModel(),
WebViewClientListener,
EditSavedSiteListener,
Expand Down Expand Up @@ -943,7 +943,7 @@ class BrowserTabViewModel @Inject constructor(
command.value = Command.RefreshOmnibar
}

if (settingsPageFeature.serpSettingsSync().isEnabled()) {
if (serpSettingsFeature.storeSerpSettings().isEnabled()) {
viewModelScope.launch {
contentScopeScriptsSubscriptionEventPluginPoint.getPlugins().forEach { plugin ->
_subscriptionEventDataChannel.send(plugin.getSubscriptionEventData())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import com.duckduckgo.common.utils.AppUrl.ParamKey
import com.duckduckgo.common.utils.AppUrl.ParamValue
import com.duckduckgo.duckchat.api.DuckChat
import com.duckduckgo.experiments.api.VariantManager
import com.duckduckgo.settings.api.SettingsPageFeature
import com.duckduckgo.settings.api.SerpSettingsFeature
import logcat.logcat

interface RequestRewriter {
Expand All @@ -40,7 +40,7 @@ class DuckDuckGoRequestRewriter(
private val appReferrerDataStore: AppReferrerDataStore,
private val duckChat: DuckChat,
private val androidConfigFeatures: AndroidBrowserConfigFeature,
private val settingsPageFeature: SettingsPageFeature,
private val serpSettingsFeature: SerpSettingsFeature,
) : RequestRewriter {

private val hideDuckAiSerpKillSwitch by lazy { androidConfigFeatures.hideDuckAiInSerpKillSwitch().isEnabled() }
Expand Down Expand Up @@ -82,7 +82,7 @@ class DuckDuckGoRequestRewriter(
val sourceValue = if (appReferrerDataStore.installedFromEuAuction) ParamValue.SOURCE_EU_AUCTION else ParamValue.SOURCE

builder.appendQueryParameter(ParamKey.HIDE_SERP, ParamValue.HIDE_SERP)
if (!settingsPageFeature.serpSettingsSync().isEnabled()) {
if (!serpSettingsFeature.storeSerpSettings().isEnabled()) {
// Once serpSettingsSync feature is permanently enabled this can be removed.
if (!duckChat.isEnabled() && hideDuckAiSerpKillSwitch) {
builder.appendQueryParameter(ParamKey.HIDE_DUCK_AI, ParamValue.HIDE_DUCK_AI)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ import com.duckduckgo.privacy.config.api.AmpLinks
import com.duckduckgo.privacy.config.api.Gpc
import com.duckduckgo.privacy.config.api.TrackingParameters
import com.duckduckgo.request.filterer.api.RequestFilterer
import com.duckduckgo.settings.api.SettingsPageFeature
import com.duckduckgo.settings.api.SerpSettingsFeature
import com.duckduckgo.subscriptions.api.Subscriptions
import com.duckduckgo.user.agent.api.UserAgentProvider
import dagger.Module
Expand All @@ -115,7 +115,7 @@ class BrowserModule {
appReferrerDataStore: AppReferrerDataStore,
duckChat: DuckChat,
androidBrowserConfigFeature: AndroidBrowserConfigFeature,
settingsPageFeature: SettingsPageFeature,
serpSettingsFeature: SerpSettingsFeature,
): RequestRewriter {
return DuckDuckGoRequestRewriter(
urlDetector,
Expand All @@ -124,7 +124,7 @@ class BrowserModule {
appReferrerDataStore,
duckChat,
androidBrowserConfigFeature,
settingsPageFeature,
serpSettingsFeature,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ import com.duckduckgo.duckchat.api.DuckChat
import com.duckduckgo.experiments.api.VariantManager
import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory
import com.duckduckgo.feature.toggles.api.Toggle.State
import com.duckduckgo.settings.api.SettingsPageFeature
import org.junit.Assert.*
import com.duckduckgo.settings.api.SerpSettingsFeature
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
Expand All @@ -47,7 +49,7 @@ class DuckDuckGoRequestRewriterTest {
private val mockVariantManager: VariantManager = mock()
private val mockAppReferrerDataStore: AppReferrerDataStore = mock()
private val duckChat: DuckChat = mock()
private val settingsPageFeature: SettingsPageFeature = FakeFeatureToggleFactory.create(SettingsPageFeature::class.java)
private val serpSettingsFeature: SerpSettingsFeature = FakeFeatureToggleFactory.create(SerpSettingsFeature::class.java)
private val androidBrowserConfigFeature: AndroidBrowserConfigFeature = FakeFeatureToggleFactory.create(AndroidBrowserConfigFeature::class.java)
private lateinit var builder: Uri.Builder

Expand All @@ -66,7 +68,7 @@ class DuckDuckGoRequestRewriterTest {
mockAppReferrerDataStore,
duckChat,
androidBrowserConfigFeature,
settingsPageFeature,
serpSettingsFeature,
)
builder = Uri.Builder()
}
Expand Down Expand Up @@ -157,7 +159,7 @@ class DuckDuckGoRequestRewriterTest {

@Test
fun whenSerpSettingsSyncIsEnabledThenDoNotHideDuckAi() {
settingsPageFeature.serpSettingsSync().setRawStoredState(State(true))
serpSettingsFeature.storeSerpSettings().setRawStoredState(State(true))
whenever(duckChat.isEnabled()).thenReturn(false)
androidBrowserConfigFeature.hideDuckAiInSerpKillSwitch().setRawStoredState(State(true))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ import com.duckduckgo.duckchat.api.DuckChat
import com.duckduckgo.experiments.api.VariantManager
import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory
import com.duckduckgo.feature.toggles.api.Toggle.State
import com.duckduckgo.settings.api.SettingsPageFeature
import org.junit.Assert.*
import com.duckduckgo.settings.api.SerpSettingsFeature
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
Expand All @@ -44,7 +47,7 @@ class QueryUrlConverterTest {
private val mockAppReferrerDataStore: AppReferrerDataStore = mock()
private val duckChat: DuckChat = mock()
private val androidBrowserConfigFeature: AndroidBrowserConfigFeature = FakeFeatureToggleFactory.create(AndroidBrowserConfigFeature::class.java)
private val settingsPageFeature: SettingsPageFeature = FakeFeatureToggleFactory.create(SettingsPageFeature::class.java)
private val serpSettingsFeature: SerpSettingsFeature = FakeFeatureToggleFactory.create(SerpSettingsFeature::class.java)
private val requestRewriter =
DuckDuckGoRequestRewriter(
DuckDuckGoUrlDetectorImpl(),
Expand All @@ -53,7 +56,7 @@ class QueryUrlConverterTest {
mockAppReferrerDataStore,
duckChat,
androidBrowserConfigFeature,
settingsPageFeature,
serpSettingsFeature,
)
private val testee: QueryUrlConverter = QueryUrlConverter(requestRewriter)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,10 @@ interface DuckChatFeature {
*/
@Toggle.DefaultValue(DefaultFeatureValue.INTERNAL)
fun duckAiVoiceSearch(): Toggle

/**
* @return `true` when the "Hide AI Generated Images" option should be visible in AI Features Settings.
*/
@Toggle.DefaultValue(DefaultFeatureValue.INTERNAL)
fun showHideAiGeneratedImages(): Toggle
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import com.duckduckgo.js.messaging.api.JsMessage
import com.duckduckgo.js.messaging.api.JsMessageCallback
import com.duckduckgo.js.messaging.api.JsMessageHandler
import com.duckduckgo.js.messaging.api.JsMessaging
import com.duckduckgo.settings.api.SettingsPageFeature
import com.duckduckgo.settings.api.SerpSettingsFeature
import com.squareup.anvil.annotations.ContributesMultibinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
Expand All @@ -45,7 +45,7 @@ import javax.inject.Inject
class IsNativeDuckAiEnabledHandler @Inject constructor(
private val dispatcherProvider: DispatcherProvider,
@AppCoroutineScope private val appScope: CoroutineScope,
private val settingsPageFeature: SettingsPageFeature,
private val serpSettingsFeature: SerpSettingsFeature,
private val duckChat: DuckChat,
) : ContentScopeJsMessageHandlersPlugin {

Expand All @@ -57,7 +57,7 @@ class IsNativeDuckAiEnabledHandler @Inject constructor(
jsMessageCallback: JsMessageCallback?,
) {
appScope.launch(dispatcherProvider.main()) {
if (settingsPageFeature.serpSettingsSync().isEnabled()) {
if (serpSettingsFeature.storeSerpSettings().isEnabled()) {
logcat { "SERP-SETTINGS: IsNativeDuckAiEnabledHandler processing message" }
val response = JSONObject().apply {
put("enabled", duckChat.isEnabled())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.duckduckgo.duckchat.api.DuckChatNativeSettingsNoParams
import com.duckduckgo.duckchat.api.DuckChatSettingsNoParams
import com.duckduckgo.duckchat.impl.DuckChatInternal
import com.duckduckgo.duckchat.impl.R
import com.duckduckgo.duckchat.impl.feature.DuckChatFeature
import com.duckduckgo.duckchat.impl.inputscreen.ui.metrics.discovery.InputScreenDiscoveryFunnel
import com.duckduckgo.duckchat.impl.pixel.DuckChatPixelName
import com.duckduckgo.duckchat.impl.ui.settings.DuckChatSettingsViewModel.Command.OpenLink
Expand Down Expand Up @@ -53,6 +54,7 @@ class DuckChatSettingsViewModel @AssistedInject constructor(
private val inputScreenDiscoveryFunnel: InputScreenDiscoveryFunnel,
private val settingsPageFeature: SettingsPageFeature,
private val dispatcherProvider: DispatcherProvider,
private val duckChatFeature: DuckChatFeature,
) : ViewModel() {
private val commandChannel = Channel<Command>(capacity = 1, onBufferOverflow = DROP_OLDEST)
val commands = commandChannel.receiveAsFlow()
Expand All @@ -73,15 +75,15 @@ class DuckChatSettingsViewModel @AssistedInject constructor(
duckChat.observeEnableDuckChatUserSetting(),
duckChat.observeInputScreenUserSettingEnabled(),
duckChat.observeFullscreenModeUserSetting(),
flowOf(settingsPageFeature.hideAiGeneratedImagesOption().isEnabled()).flowOn(dispatcherProvider.io()),
) { isDuckChatUserEnabled, isInputScreenEnabled, isFullScreenModeEnabled, isHideAiGeneratedImagesOptionVisible ->
flowOf(duckChatFeature.showHideAiGeneratedImages().isEnabled()).flowOn(dispatcherProvider.io()),
) { isDuckChatUserEnabled, isInputScreenEnabled, isFullScreenModeEnabled, showHideAiGeneratedImagesOption ->
ViewState(
isDuckChatUserEnabled = isDuckChatUserEnabled,
isInputScreenEnabled = isInputScreenEnabled,
shouldShowShortcuts = isDuckChatUserEnabled,
shouldShowInputScreenToggle = isDuckChatUserEnabled && duckChat.isInputScreenFeatureAvailable(),
isSearchSectionVisible = isSearchSectionVisible(duckChatActivityParams),
isHideGeneratedImagesOptionVisible = isHideAiGeneratedImagesOptionVisible,
isHideGeneratedImagesOptionVisible = showHideAiGeneratedImagesOption,
shouldShowFullScreenModeToggle = duckChat.isDuckChatFullScreenModeFeatureAvailable(),
isFullScreenModeEnabled = isFullScreenModeEnabled,
)
Expand Down Expand Up @@ -143,19 +145,19 @@ class DuckChatSettingsViewModel @AssistedInject constructor(

fun duckChatSearchAISettingsClicked() {
viewModelScope.launch {
val hideAiGeneratedImagesOptionEnabled = withContext(dispatcherProvider.io()) {
settingsPageFeature.hideAiGeneratedImagesOption().isEnabled()
val showHideAiGeneratedImages = withContext(dispatcherProvider.io()) {
duckChatFeature.showHideAiGeneratedImages().isEnabled()
}

if (settingsPageFeature.embeddedSettingsWebView().isEnabled()) {
commandChannel.send(
OpenLink(
link = if (hideAiGeneratedImagesOptionEnabled) {
link = if (showHideAiGeneratedImages) {
DUCK_CHAT_SEARCH_AI_SETTINGS_LINK_EMBEDDED
} else {
LEGACY_DUCK_CHAT_SEARCH_AI_SETTINGS_LINK_EMBEDDED
},
titleRes = if (hideAiGeneratedImagesOptionEnabled) {
titleRes = if (showHideAiGeneratedImages) {
R.string.duckAiSerpSettingsTitle
} else {
R.string.duck_chat_assist_settings_title
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import com.duckduckgo.duckchat.impl.messaging.fakes.FakeJsMessaging
import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory
import com.duckduckgo.feature.toggles.api.Toggle
import com.duckduckgo.js.messaging.api.JsMessage
import com.duckduckgo.settings.api.SettingsPageFeature
import com.duckduckgo.settings.api.SerpSettingsFeature
import kotlinx.coroutines.test.runTest
import org.json.JSONObject
import org.junit.Assert.assertEquals
Expand All @@ -39,15 +39,15 @@ class IsNativeDuckAiEnabledHandlerTest {
val coroutineTestRule: CoroutineTestRule = CoroutineTestRule()

private val fakeDuckChat = FakeDuckChat(enabled = true)
private val settingsPageFeature = FakeFeatureToggleFactory.create(SettingsPageFeature::class.java)
private val serpSettingsFeature = FakeFeatureToggleFactory.create(SerpSettingsFeature::class.java)
private lateinit var handler: IsNativeDuckAiEnabledHandler

@Before
fun setUp() {
handler = IsNativeDuckAiEnabledHandler(
dispatcherProvider = coroutineTestRule.testDispatcherProvider,
appScope = coroutineTestRule.testScope,
settingsPageFeature = settingsPageFeature,
serpSettingsFeature = serpSettingsFeature,
duckChat = fakeDuckChat,
)
}
Expand All @@ -74,7 +74,7 @@ class IsNativeDuckAiEnabledHandlerTest {
@Test
fun `when id is null then no response is sent`() = runTest {
@Suppress("DenyListedApi")
settingsPageFeature.serpSettingsSync().setRawStoredState(Toggle.State(enable = true))
serpSettingsFeature.storeSerpSettings().setRawStoredState(Toggle.State(enable = true))
fakeDuckChat.setEnabled(true)

val fakeJsMessaging = FakeJsMessaging()
Expand All @@ -96,7 +96,7 @@ class IsNativeDuckAiEnabledHandlerTest {
@Test
fun `when id is not null then response is sent`() = runTest {
@Suppress("DenyListedApi")
settingsPageFeature.serpSettingsSync().setRawStoredState(Toggle.State(enable = true))
serpSettingsFeature.storeSerpSettings().setRawStoredState(Toggle.State(enable = true))
fakeDuckChat.setEnabled(true)

val fakeJsMessaging = FakeJsMessaging()
Expand Down
Loading
Loading