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 @@ -88,6 +88,11 @@ interface DuckChatInternal : DuckChat {
*/
suspend fun setShowInAddressBarUserSetting(showDuckChat: Boolean)

/**
* Set user setting to determine whether DuckChat should be shown in fullscreen mode.
*/
suspend fun setFullScreenModeUserSetting(enabled: Boolean)

/**
* Observes whether DuckChat is user enabled or disabled.
*/
Expand All @@ -108,6 +113,11 @@ interface DuckChatInternal : DuckChat {
*/
fun observeShowInAddressBarUserSetting(): Flow<Boolean>

/**
* Observes whether Duck.ai full screen mode is enabled or disabled.
*/
fun observeFullscreenModeUserSetting(): Flow<Boolean>

/**
* Opens DuckChat settings.
*/
Expand Down Expand Up @@ -166,6 +176,11 @@ interface DuckChatInternal : DuckChat {
*/
fun isInputScreenFeatureAvailable(): Boolean

/**
* Returns whether dedicated Duck.ai full screen mode feature is available (its feature flag is enabled).
*/
fun isDuckChatFullScreenModeFeatureAvailable(): Boolean

/**
* Checks whether DuckChat is enabled based on remote config flag.
*/
Expand Down Expand Up @@ -257,6 +272,7 @@ class RealDuckChat @Inject constructor(
private val _showNewAddressBarOptionChoiceScreen = MutableStateFlow(false)
private val _showClearDuckAIChatHistory = MutableStateFlow(true)
private val _showMainButtonsInInputScreen = MutableStateFlow(false)
private val _fullscreenModeEnabled = MutableStateFlow(false)

private val _chatState = MutableStateFlow(ChatState.HIDE)
private val keepSession = MutableStateFlow(false)
Expand All @@ -281,6 +297,7 @@ class RealDuckChat @Inject constructor(
private var clearChatHistory: Boolean = true
private var inputScreenMainButtonsEnabled = false
private var showInputScreenOnSystemSearchLaunchEnabled: Boolean = true
private var isFullscreenModeEnabled: Boolean = false

init {
if (isMainProcess) {
Expand Down Expand Up @@ -314,12 +331,21 @@ class RealDuckChat @Inject constructor(
cacheUserSettings()
}

override suspend fun setFullScreenModeUserSetting(enabled: Boolean) {
withContext(dispatchers.io()) {
duckChatFeatureRepository.setFullScreenModeUserSetting(enabled)
cacheUserSettings()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs tests

}

override fun isEnabled(): Boolean = isDuckChatFeatureEnabled && isDuckChatUserEnabled

override fun isInputScreenFeatureAvailable(): Boolean = duckAiInputScreen

override fun isDuckChatFeatureEnabled(): Boolean = isDuckChatFeatureEnabled

override fun isDuckChatFullScreenModeFeatureAvailable(): Boolean = duckChatFeature.fullscreenMode().isEnabled()

override fun observeEnableDuckChatUserSetting(): Flow<Boolean> = duckChatFeatureRepository.observeDuckChatUserEnabled()

override fun observeInputScreenUserSettingEnabled(): Flow<Boolean> = duckChatFeatureRepository.observeInputScreenUserSettingEnabled()
Expand All @@ -328,6 +354,8 @@ class RealDuckChat @Inject constructor(

override fun observeShowInAddressBarUserSetting(): Flow<Boolean> = duckChatFeatureRepository.observeShowInAddressBar()

override fun observeFullscreenModeUserSetting(): Flow<Boolean> = duckChatFeatureRepository.observeFullscreenModeEnabled()

override fun openDuckChatSettings() {
val intent = globalActivityStarter.startIntent(context, DuckChatSettingsNoParams)
intent?.flags = Intent.FLAG_ACTIVITY_NEW_TASK
Expand Down Expand Up @@ -607,6 +635,8 @@ class RealDuckChat @Inject constructor(
showInputScreenOnSystemSearchLaunchEnabled = duckChatFeature.showInputScreenOnSystemSearchLaunch().isEnabled()
inputScreenMainButtonsEnabled = duckChatFeature.showMainButtonsInInputScreen().isEnabled()

isFullscreenModeEnabled = duckChatFeature.fullscreenMode().isEnabled()

val showMainButtons = duckChatFeature.showMainButtonsInInputScreen().isEnabled()
_showMainButtonsInInputScreen.emit(showMainButtons)

Expand Down Expand Up @@ -666,6 +696,9 @@ class RealDuckChat @Inject constructor(

val showClearChatHistory = clearChatHistory
_showClearDuckAIChatHistory.emit(showClearChatHistory)

val fullscreenModeEnabled = isFullscreenModeEnabled
_fullscreenModeEnabled.emit(fullscreenModeEnabled)
}

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

/**
* @return `true` when the new fullscreen mode is enabled
*/
@Toggle.DefaultValue(DefaultFeatureValue.INTERNAL)
fun fullscreenMode(): Toggle
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ interface DuckChatFeatureRepository {

suspend fun setShowInAddressBar(showDuckChat: Boolean)

suspend fun setFullScreenModeUserSetting(enabled: Boolean)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs tests


fun observeDuckChatUserEnabled(): Flow<Boolean>

fun observeInputScreenUserSettingEnabled(): Flow<Boolean>
Expand All @@ -43,6 +45,8 @@ interface DuckChatFeatureRepository {

fun observeShowInAddressBar(): Flow<Boolean>

fun observeFullscreenModeEnabled(): Flow<Boolean>

suspend fun isDuckChatUserEnabled(): Boolean

suspend fun isInputScreenUserSettingEnabled(): Boolean
Expand All @@ -51,6 +55,8 @@ interface DuckChatFeatureRepository {

suspend fun shouldShowInAddressBar(): Boolean

suspend fun isFullScreenModeUserSettingEnabled(): Boolean

suspend fun registerOpened()

suspend fun wasOpenedBefore(): Boolean
Expand Down Expand Up @@ -82,6 +88,10 @@ class RealDuckChatFeatureRepository @Inject constructor(
duckChatDataStore.setShowInAddressBar(showDuckChat)
}

override suspend fun setFullScreenModeUserSetting(enabled: Boolean) {
duckChatDataStore.setFullScreenModeUserSetting(enabled)
}

override fun observeDuckChatUserEnabled(): Flow<Boolean> = duckChatDataStore.observeDuckChatUserEnabled()

override fun observeInputScreenUserSettingEnabled(): Flow<Boolean> = duckChatDataStore.observeInputScreenUserSettingEnabled()
Expand All @@ -90,6 +100,8 @@ class RealDuckChatFeatureRepository @Inject constructor(

override fun observeShowInAddressBar(): Flow<Boolean> = duckChatDataStore.observeShowInAddressBar()

override fun observeFullscreenModeEnabled(): Flow<Boolean> = duckChatDataStore.observeFullscreenMode()

override suspend fun isDuckChatUserEnabled(): Boolean = duckChatDataStore.isDuckChatUserEnabled()

override suspend fun isInputScreenUserSettingEnabled(): Boolean = duckChatDataStore.isInputScreenUserSettingEnabled()
Expand All @@ -98,6 +110,8 @@ class RealDuckChatFeatureRepository @Inject constructor(

override suspend fun shouldShowInAddressBar(): Boolean = duckChatDataStore.getShowInAddressBar()

override suspend fun isFullScreenModeUserSettingEnabled(): Boolean = duckChatDataStore.isFullScreenUserSettingEnabled()

override suspend fun registerOpened() {
if (!duckChatDataStore.wasOpenedBefore()) {
updateWidgets()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.duckchat.impl.di.DuckChat
import com.duckduckgo.duckchat.impl.store.SharedPreferencesDuckChatDataStore.Keys.DUCK_AI_INPUT_SCREEN_USER_SETTING
import com.duckduckgo.duckchat.impl.store.SharedPreferencesDuckChatDataStore.Keys.DUCK_CHAT_FULLSCREEN_MODE_SETTING
import com.duckduckgo.duckchat.impl.store.SharedPreferencesDuckChatDataStore.Keys.DUCK_CHAT_LAST_SESSION_TIMESTAMP
import com.duckduckgo.duckchat.impl.store.SharedPreferencesDuckChatDataStore.Keys.DUCK_CHAT_OPENED
import com.duckduckgo.duckchat.impl.store.SharedPreferencesDuckChatDataStore.Keys.DUCK_CHAT_SESSION_DELTA_TIMESTAMP
Expand Down Expand Up @@ -57,6 +58,8 @@ interface DuckChatDataStore {

suspend fun setShowInAddressBar(showDuckChat: Boolean)

suspend fun setFullScreenModeUserSetting(enabled: Boolean)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs tests


fun observeDuckChatUserEnabled(): Flow<Boolean>

fun observeInputScreenUserSettingEnabled(): Flow<Boolean>
Expand All @@ -65,6 +68,8 @@ interface DuckChatDataStore {

fun observeShowInAddressBar(): Flow<Boolean>

fun observeFullscreenMode(): Flow<Boolean>

suspend fun isDuckChatUserEnabled(): Boolean

suspend fun isInputScreenUserSettingEnabled(): Boolean
Expand All @@ -73,6 +78,8 @@ interface DuckChatDataStore {

suspend fun getShowInAddressBar(): Boolean

suspend fun isFullScreenUserSettingEnabled(): Boolean

suspend fun fetchAndClearUserPreferences(): String?

suspend fun updateUserPreferences(userPreferences: String?)
Expand Down Expand Up @@ -103,6 +110,7 @@ class SharedPreferencesDuckChatDataStore @Inject constructor(
val DUCK_CHAT_USER_PREFERENCES = stringPreferencesKey("DUCK_CHAT_USER_PREFERENCES")
val DUCK_CHAT_LAST_SESSION_TIMESTAMP = longPreferencesKey(name = "DUCK_CHAT_LAST_SESSION_TIMESTAMP")
val DUCK_CHAT_SESSION_DELTA_TIMESTAMP = longPreferencesKey(name = "DUCK_CHAT_SESSION_DELTA_TIMESTAMP")
val DUCK_CHAT_FULLSCREEN_MODE_SETTING = booleanPreferencesKey(name = "DUCK_CHAT_FULLSCREEN_MODE_SETTING")
}

private fun Preferences.defaultShowInAddressBar(): Boolean =
Expand Down Expand Up @@ -158,6 +166,10 @@ class SharedPreferencesDuckChatDataStore @Inject constructor(
store.edit { prefs -> prefs[DUCK_AI_INPUT_SCREEN_USER_SETTING] = enabled }
}

override suspend fun setFullScreenModeUserSetting(enabled: Boolean) {
store.edit { prefs -> prefs[DUCK_CHAT_FULLSCREEN_MODE_SETTING] = enabled }
}

override suspend fun setShowInBrowserMenu(showDuckChat: Boolean) {
store.edit { prefs -> prefs[DUCK_CHAT_SHOW_IN_MENU] = showDuckChat }
}
Expand All @@ -174,6 +186,8 @@ class SharedPreferencesDuckChatDataStore @Inject constructor(

override fun observeShowInAddressBar(): Flow<Boolean> = duckChatShowInAddressBar

override fun observeFullscreenMode(): Flow<Boolean> = store.data.map { it[DUCK_CHAT_FULLSCREEN_MODE_SETTING] ?: false }

override suspend fun isDuckChatUserEnabled(): Boolean = store.data.firstOrNull()?.let { it[DUCK_CHAT_USER_ENABLED] } ?: true

override suspend fun isInputScreenUserSettingEnabled(): Boolean = store.data.firstOrNull()?.let { it[DUCK_AI_INPUT_SCREEN_USER_SETTING] } ?: false
Expand All @@ -182,6 +196,8 @@ class SharedPreferencesDuckChatDataStore @Inject constructor(

override suspend fun getShowInAddressBar(): Boolean = store.data.firstOrNull()?.defaultShowInAddressBar() ?: true

override suspend fun isFullScreenUserSettingEnabled(): Boolean = store.data.firstOrNull()?.let { it[DUCK_CHAT_FULLSCREEN_MODE_SETTING] } ?: false

override suspend fun fetchAndClearUserPreferences(): String? {
val userPreferences = store.data.map { it[DUCK_CHAT_USER_PREFERENCES] }.firstOrNull()
store.edit { prefs -> prefs.remove(DUCK_CHAT_USER_PREFERENCES) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ class DuckChatSettingsActivity : DuckDuckGoActivity() {
updateWidgets()
}

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

@Inject
lateinit var globalActivityStarter: GlobalActivityStarter

Expand Down Expand Up @@ -189,6 +194,10 @@ class DuckChatSettingsActivity : DuckDuckGoActivity() {
binding.duckAiInputScreenWithAiContainer.setOnClickListener {
viewModel.onDuckAiInputScreenWithAiSelected()
}

binding.duckAiFullScreenMode.isVisible = viewState.shouldShowFullScreenModeToggle
binding.duckAiFullScreenMode.updatePadding(left = offset)
binding.duckAiFullScreenMode.quietlySetIsChecked(viewState.isFullScreenModeEnabled, userEnabledDuckChatFullScreenModeListener)
}

private fun renderSearchSettingsSection(viewState: ViewState) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,26 @@ class DuckChatSettingsViewModel @AssistedInject constructor(
val shouldShowInputScreenToggle: Boolean = false,
val isSearchSectionVisible: Boolean = true,
val isHideGeneratedImagesOptionVisible: Boolean = false,
val shouldShowFullScreenModeToggle: Boolean = false,
val isFullScreenModeEnabled: Boolean = false,
)

val viewState =
combine(
duckChat.observeEnableDuckChatUserSetting(),
duckChat.observeInputScreenUserSettingEnabled(),
duckChat.observeFullscreenModeUserSetting(),
flowOf(settingsPageFeature.hideAiGeneratedImagesOption().isEnabled()).flowOn(dispatcherProvider.io()),
) { isDuckChatUserEnabled, isInputScreenEnabled, isHideAiGeneratedImagesOptionVisible ->
) { isDuckChatUserEnabled, isInputScreenEnabled, isFullScreenModeEnabled, isHideAiGeneratedImagesOptionVisible ->
ViewState(
isDuckChatUserEnabled = isDuckChatUserEnabled,
isInputScreenEnabled = isInputScreenEnabled,
shouldShowShortcuts = isDuckChatUserEnabled,
shouldShowInputScreenToggle = isDuckChatUserEnabled && duckChat.isInputScreenFeatureAvailable(),
isSearchSectionVisible = isSearchSectionVisible(duckChatActivityParams),
isHideGeneratedImagesOptionVisible = isHideAiGeneratedImagesOptionVisible,
shouldShowFullScreenModeToggle = duckChat.isDuckChatFullScreenModeFeatureAvailable(),
isFullScreenModeEnabled = isFullScreenModeEnabled,
)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), ViewState())

Expand Down Expand Up @@ -195,6 +200,12 @@ class DuckChatSettingsViewModel @AssistedInject constructor(
}
}

fun onDuckChatFullscreenModeToggled(checked: Boolean) {
viewModelScope.launch {
duckChat.setFullScreenModeUserSetting(checked)
}
}

private fun isSearchSectionVisible(duckChatActivityParams: GlobalActivityStarter.ActivityParams): Boolean = when (duckChatActivityParams) {
is DuckChatSettingsNoParams -> true
is DuckChatNativeSettingsNoParams -> false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,15 @@
app:textType="secondary"
app:typography="body2" />

<com.duckduckgo.common.ui.view.listitem.TwoLineListItem
android:id="@+id/duckAiFullScreenMode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:showSwitch="true"
app:primaryText="@string/duckAiFullScreenModeTitle"
app:secondaryText="@string/duckAiFullScreenModeDescription" />

<com.duckduckgo.common.ui.view.listitem.TwoLineListItem
android:id="@+id/duckAiShortcuts"
android:layout_width="match_parent"
Expand Down
4 changes: 4 additions & 0 deletions duckchat/duckchat-impl/src/main/res/values/donottranslate.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@
<!-- Duck AI Hide Generated Images Settings Option -->
<string name="duckAiHideAiGeneratedImagesTitle">Hide AI-Generated Images</string>
<string name="duckAiHideAiGeneratedImagesDescription">Filters out AI-generated images from image search results</string>

<!-- Duck AI Fullscreen Mode -->
<string name="duckAiFullScreenModeTitle">Full Screen Mode</string>
<string name="duckAiFullScreenModeDescription">Internal setting for O-J work</string>
</resources>
Loading
Loading