Skip to content
Open
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 @@ -19,6 +19,7 @@ package com.duckduckgo.app.browser.newaddressbaroption
import android.app.Activity
import com.duckduckgo.app.browser.omnibar.OmnibarType
import com.duckduckgo.app.onboarding.store.AppStage
import com.duckduckgo.app.onboarding.store.OnboardingStore
import com.duckduckgo.app.onboarding.store.UserStageStore
import com.duckduckgo.app.settings.db.SettingsDataStore
import com.duckduckgo.common.ui.DuckDuckGoActivity
Expand Down Expand Up @@ -55,6 +56,7 @@ class RealNewAddressBarOptionManager @Inject constructor(
private val remoteMessagingRepository: RemoteMessagingRepository,
private val newAddressBarOptionDataStore: NewAddressBarOptionDataStore,
private val settingsDataStore: SettingsDataStore,
private val onboardingStore: OnboardingStore,
private val dispatchers: DispatcherProvider = DefaultDispatcherProvider(),
) : NewAddressBarOptionManager {
private val showChoiceScreenMutex = Mutex()
Expand Down Expand Up @@ -97,7 +99,8 @@ class RealNewAddressBarOptionManager @Inject constructor(
hasNotInteractedWithSearchAndDuckAiRMF() &&
isNewAddressBarOptionChoiceScreenEnabled() &&
isNotLaunchedFromExternal(isLaunchedFromExternal) &&
isSubsequentLaunch()
isSubsequentLaunch() &&
hasNoInputScreenSelection()
}

private fun isActivityValid(activity: Activity): Boolean =
Expand Down Expand Up @@ -130,6 +133,11 @@ class RealNewAddressBarOptionManager @Inject constructor(
logcat(DEBUG) { "NewAddressBarOptionManager: $it isInputScreenDisabled" }
}

private fun hasNoInputScreenSelection(): Boolean =
(onboardingStore.getInputScreenSelection() == null).also {
logcat(DEBUG) { "NewAddressBarOptionManager: $it hasNoInputScreenSelection" }
}

private fun isBottomAddressBarDisabled(): Boolean =
(settingsDataStore.omnibarType != OmnibarType.SINGLE_BOTTOM).also {
logcat(DEBUG) { "NewAddressBarOptionManager: $it isBottomAddressBarDisabled" }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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.app.onboarding

import com.duckduckgo.app.di.AppCoroutineScope
import com.duckduckgo.app.lifecycle.MainProcessLifecycleObserver
import com.duckduckgo.app.onboarding.store.AppStage
import com.duckduckgo.app.onboarding.store.OnboardingStore
import com.duckduckgo.app.onboarding.store.UserStageStore
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.duckchat.api.DuckChat
import com.squareup.anvil.annotations.ContributesMultibinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import javax.inject.Inject

@ContributesMultibinding(
scope = AppScope::class,
boundType = MainProcessLifecycleObserver::class,
)
class OnboardingInputScreenSelectionObserver @Inject constructor(
@AppCoroutineScope private val appCoroutineScope: CoroutineScope,
dispatchers: DispatcherProvider,
private val userStageStore: UserStageStore,
private val onboardingStore: OnboardingStore,
private val duckChat: DuckChat,
) : MainProcessLifecycleObserver {

init {
appCoroutineScope.launch(dispatchers.io()) {
userStageStore.userAppStageFlow()
.distinctUntilChanged()
.filter { it == AppStage.ESTABLISHED }
.collect {
onboardingStore.getInputScreenSelection()?.let { selection ->
duckChat.setInputScreenUserSetting(selection)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ interface OnboardingStore {

fun getSearchOptions(): List<DaxDialogIntroOption>
fun getSitesOptions(): List<DaxDialogIntroOption>
fun storeInputScreenSelection(selected: Boolean)
fun getInputScreenSelection(): Boolean?
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,21 @@ class OnboardingStoreImpl @Inject constructor(
)
}

override fun storeInputScreenSelection(selected: Boolean) {
preferences.edit { putBoolean(KEY_INPUT_SCREEN_SELECTION, selected) }
}

override fun getInputScreenSelection(): Boolean? {
return if (preferences.contains(KEY_INPUT_SCREEN_SELECTION)) {
preferences.getBoolean(KEY_INPUT_SCREEN_SELECTION, false)
} else {
null
}
}

companion object {
const val FILENAME = "com.duckduckgo.app.onboarding.settings"
const val ONBOARDING_JOURNEY = "onboardingJourney"
private const val KEY_INPUT_SCREEN_SELECTION = "inputScreenSelection"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.ADDRESS_BAR
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.COMPARISON_CHART
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.INITIAL
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.INITIAL_REINSTALL_USER
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.INPUT_SCREEN
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.SKIP_ONBOARDING_OPTION
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.*
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.ShowInputScreenDialog
import com.duckduckgo.appbuildconfig.api.AppBuildConfig
import com.duckduckgo.common.ui.store.AppTheme
import com.duckduckgo.common.ui.view.TypeAnimationTextView
Expand Down Expand Up @@ -116,6 +118,7 @@ class BbWelcomePage : OnboardingPageFragment(R.layout.content_onboarding_welcome
is ShowSkipOnboardingOption -> configureDaxCta(SKIP_ONBOARDING_OPTION)
is ShowDefaultBrowserDialog -> showDefaultBrowserDialog(it.intent)
is ShowAddressBarPositionDialog -> configureDaxCta(ADDRESS_BAR_POSITION)
is ShowInputScreenDialog -> onContinuePressed()
is Finish -> onContinuePressed()
is OnboardingSkipped -> onSkipPressed()
is SetAddressBarPositionOptions -> setAddressBarPositionOptions(it.defaultOption)
Expand Down Expand Up @@ -436,6 +439,10 @@ class BbWelcomePage : OnboardingPageFragment(R.layout.content_onboarding_welcome
scheduleTypingAnimation(binding.daxDialogCta.addressBarPosition.dialogTitle, titleText) { afterTypingAnimation() }
backgroundSceneManager?.transitionToNextTile(expectedTile = TILE_04)
}

INPUT_SCREEN -> {
// Ignored
}
}
backgroundSceneManager?.setBackgroundClickListener(afterTypingAnimation)
binding.daxDialogCta.cardContainer.setOnClickListener { afterTypingAnimation() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.ADDRESS_BAR
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.COMPARISON_CHART
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.INITIAL
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.INITIAL_REINSTALL_USER
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.INPUT_SCREEN
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.SKIP_ONBOARDING_OPTION
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.Finish
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.OnboardingSkipped
Expand All @@ -62,6 +63,7 @@ import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.ShowCo
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.ShowDefaultBrowserDialog
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.ShowInitialDialog
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.ShowInitialReinstallUserDialog
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.ShowInputScreenDialog
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.ShowSkipOnboardingOption
import com.duckduckgo.appbuildconfig.api.AppBuildConfig
import com.duckduckgo.common.ui.store.AppTheme
Expand Down Expand Up @@ -116,6 +118,7 @@ class BuckWelcomePage : OnboardingPageFragment(R.layout.content_onboarding_welco
is ShowSkipOnboardingOption -> configureDaxCta(SKIP_ONBOARDING_OPTION)
is ShowDefaultBrowserDialog -> showDefaultBrowserDialog(it.intent)
is ShowAddressBarPositionDialog -> configureDaxCta(ADDRESS_BAR_POSITION)
is ShowInputScreenDialog -> onContinuePressed()
is Finish -> onContinuePressed()
is OnboardingSkipped -> onSkipPressed()
is SetAddressBarPositionOptions -> setAddressBarPositionOptions(it.defaultOption)
Expand Down Expand Up @@ -395,6 +398,10 @@ class BuckWelcomePage : OnboardingPageFragment(R.layout.content_onboarding_welco
},
)
}

INPUT_SCREEN -> {
// Ignored
}
}
binding.sceneBg.setOnClickListener { afterTypingAnimation() }
binding.daxDialogCta.cardContainer.setOnClickListener { afterTypingAnimation() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ enum class PreOnboardingDialogType {
SKIP_ONBOARDING_OPTION,
COMPARISON_CHART,
ADDRESS_BAR_POSITION,
INPUT_SCREEN,
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.ADDRESS_BAR
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.COMPARISON_CHART
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.INITIAL
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.INITIAL_REINSTALL_USER
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.INPUT_SCREEN
import com.duckduckgo.app.onboarding.ui.page.PreOnboardingDialogType.SKIP_ONBOARDING_OPTION
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.Finish
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.OnboardingSkipped
Expand All @@ -50,6 +51,7 @@ import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.ShowCo
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.ShowDefaultBrowserDialog
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.ShowInitialDialog
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.ShowInitialReinstallUserDialog
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.ShowInputScreenDialog
import com.duckduckgo.app.onboarding.ui.page.WelcomePageViewModel.Command.ShowSkipOnboardingOption
import com.duckduckgo.app.onboardingdesignexperiment.OnboardingDesignExperimentManager
import com.duckduckgo.appbuildconfig.api.AppBuildConfig
Expand All @@ -64,6 +66,7 @@ import com.duckduckgo.di.scopes.FragmentScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import javax.inject.Inject
import com.duckduckgo.mobile.android.R as CommonR

@InjectWith(FragmentScope::class)
class WelcomePage : OnboardingPageFragment(R.layout.content_onboarding_welcome_page) {
Expand Down Expand Up @@ -110,6 +113,7 @@ class WelcomePage : OnboardingPageFragment(R.layout.content_onboarding_welcome_p
is ShowSkipOnboardingOption -> configureDaxCta(SKIP_ONBOARDING_OPTION)
is ShowDefaultBrowserDialog -> showDefaultBrowserDialog(it.intent)
is ShowAddressBarPositionDialog -> configureDaxCta(ADDRESS_BAR_POSITION)
is ShowInputScreenDialog -> configureDaxCta(INPUT_SCREEN)
is Finish -> onContinuePressed()
is OnboardingSkipped -> onSkipPressed()
is SetAddressBarPositionOptions -> setAddressBarPositionOptions(it.defaultOption)
Expand Down Expand Up @@ -243,8 +247,10 @@ class WelcomePage : OnboardingPageFragment(R.layout.content_onboarding_welcome_p
binding.daxDialogCta.dialogTextCta.text = ""
TransitionManager.beginDelayedTransition(binding.daxDialogCta.cardView, AutoTransition())
binding.daxDialogCta.progressBarText.show()
binding.daxDialogCta.progressBarText.text = "1 / 2"
val maxPages = viewModel.getMaxPageCount()
binding.daxDialogCta.progressBarText.text = "1 / $maxPages"
binding.daxDialogCta.progressBar.show()
binding.daxDialogCta.progressBar.max = maxPages
binding.daxDialogCta.progressBar.progress = 1
val ctaText = it.getString(R.string.preOnboardingDaxDialog2Title)
binding.daxDialogCta.hiddenTextCta.text = ctaText.html(it)
Expand Down Expand Up @@ -296,8 +302,10 @@ class WelcomePage : OnboardingPageFragment(R.layout.content_onboarding_welcome_p
binding.daxDialogCta.comparisonChart.root.gone()
TransitionManager.beginDelayedTransition(binding.daxDialogCta.cardView, AutoTransition())
binding.daxDialogCta.progressBarText.show()
binding.daxDialogCta.progressBarText.text = "2 / 2"
val maxPages = viewModel.getMaxPageCount()
binding.daxDialogCta.progressBarText.text = "2 / $maxPages"
binding.daxDialogCta.progressBar.show()
binding.daxDialogCta.progressBar.max = maxPages
binding.daxDialogCta.progressBar.progress = 2
val ctaText = it.getString(R.string.preOnboardingAddressBarTitle).run {
if (onboardingDesignExperimentManager.isModifiedControlEnrolledAndEnabled()) {
Expand Down Expand Up @@ -328,6 +336,54 @@ class WelcomePage : OnboardingPageFragment(R.layout.content_onboarding_welcome_p

scheduleTypingAnimation(ctaText) { afterAnimation() }
}

INPUT_SCREEN -> {
binding.daxDialogCta.descriptionCta.gone()
binding.daxDialogCta.secondaryCta.gone()
binding.daxDialogCta.dialogTextCta.text = ""
binding.daxDialogCta.comparisonChart.root.gone()
binding.daxDialogCta.addressBarPosition.root.gone()
TransitionManager.beginDelayedTransition(binding.daxDialogCta.cardView, AutoTransition())
binding.daxDialogCta.progressBarText.show()
val maxPages = viewModel.getMaxPageCount()
binding.daxDialogCta.progressBarText.text = "3 / $maxPages"
binding.daxDialogCta.progressBar.show()
binding.daxDialogCta.progressBar.max = maxPages
binding.daxDialogCta.progressBar.progress = 3
val ctaText = it.getString(R.string.preOnboardingInputScreenTitle)
binding.daxDialogCta.hiddenTextCta.text = ctaText.html(it)
binding.daxDialogCta.primaryCta.alpha = MIN_ALPHA
binding.daxDialogCta.duckAiInputScreenToggleContainer.show()
binding.daxDialogCta.duckAiInputScreenToggleContainer.alpha = MIN_ALPHA

val isLightMode = appTheme.isLightModeEnabled()
updateAiChatToggleState(binding, isLightMode, withAi = true)
viewModel.onInputScreenOptionSelected(withAi = true)

binding.daxDialogCta.duckAiInputScreenWithoutAiContainer.setOnClickListener {
updateAiChatToggleState(binding, isLightMode, withAi = false)
viewModel.onInputScreenOptionSelected(withAi = false)
}
binding.daxDialogCta.duckAiInputScreenWithAiContainer.setOnClickListener {
updateAiChatToggleState(binding, isLightMode, withAi = true)
viewModel.onInputScreenOptionSelected(withAi = true)
}

val descriptionText = it.getString(R.string.preOnboardingInputScreenDescription)
binding.daxDialogCta.duckAiInputScreenToggleDescription.text = descriptionText.html(it)
binding.daxDialogCta.duckAiInputScreenToggleDescription.show()
binding.daxDialogCta.duckAiInputScreenToggleDescription.alpha = MIN_ALPHA

afterAnimation = {
binding.daxDialogCta.dialogTextCta.finishAnimation()
binding.daxDialogCta.primaryCta.text = it.getString(R.string.preOnboardingInputScreenButton)
binding.daxDialogCta.primaryCta.setOnClickListener { viewModel.onPrimaryCtaClicked(INPUT_SCREEN) }
binding.daxDialogCta.primaryCta.animate().alpha(MAX_ALPHA).duration = ANIMATION_DURATION
binding.daxDialogCta.duckAiInputScreenToggleContainer.animate().alpha(MAX_ALPHA).duration = ANIMATION_DURATION
binding.daxDialogCta.duckAiInputScreenToggleDescription.animate().alpha(MAX_ALPHA).duration = ANIMATION_DURATION
}
scheduleTypingAnimation(ctaText) { afterAnimation() }
}
}
binding.sceneBg.setOnClickListener { afterAnimation() }
binding.daxDialogCta.cardContainer.setOnClickListener { afterAnimation() }
Expand Down Expand Up @@ -397,6 +453,42 @@ class WelcomePage : OnboardingPageFragment(R.layout.content_onboarding_welcome_p
binding.sceneBg.setImageResource(backgroundRes)
}

private fun updateAiChatToggleState(
binding: ContentOnboardingWelcomePageBinding,
isLightMode: Boolean,
withAi: Boolean,
) {
val withoutAiImageRes = when {
!withAi && isLightMode -> com.duckduckgo.duckchat.impl.R.drawable.searchbox_withoutai_active
!withAi && !isLightMode -> com.duckduckgo.duckchat.impl.R.drawable.searchbox_withoutai_active_dark
withAi && isLightMode -> com.duckduckgo.duckchat.impl.R.drawable.searchbox_withoutai_inactive
else -> com.duckduckgo.duckchat.impl.R.drawable.searchbox_withoutai_inactive_dark
}
val withAiImageRes = when {
withAi && isLightMode -> com.duckduckgo.duckchat.impl.R.drawable.searchbox_withai_active
withAi && !isLightMode -> com.duckduckgo.duckchat.impl.R.drawable.searchbox_withai_active_dark
!withAi && isLightMode -> com.duckduckgo.duckchat.impl.R.drawable.searchbox_withai_inactive
else -> com.duckduckgo.duckchat.impl.R.drawable.searchbox_withai_inactive_dark
}

binding.daxDialogCta.duckAiInputScreenToggleWithoutAiImage.setImageResource(withoutAiImageRes)
binding.daxDialogCta.duckAiInputScreenToggleWithAiImage.setImageResource(withAiImageRes)

val withoutAiCheckRes = if (!withAi) {
CommonR.drawable.ic_check_accent_24
} else {
CommonR.drawable.ic_shape_circle_24
}
val withAiCheckRes = if (withAi) {
CommonR.drawable.ic_check_accent_24
} else {
CommonR.drawable.ic_shape_circle_24
}

binding.daxDialogCta.duckAiInputScreenToggleWithoutAiCheck.setImageResource(withoutAiCheckRes)
binding.daxDialogCta.duckAiInputScreenToggleWithAiCheck.setImageResource(withAiCheckRes)
}

companion object {
private const val MIN_ALPHA = 0f
private const val MAX_ALPHA = 1f
Expand Down
Loading
Loading