diff --git a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt index 8589e670d73f..6a63f6263c70 100644 --- a/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt +++ b/app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt @@ -222,8 +222,7 @@ import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggesti import com.duckduckgo.browser.api.autocomplete.AutoCompleteSettings import com.duckduckgo.browser.api.brokensite.BrokenSiteContext import com.duckduckgo.browser.api.webviewcompat.WebViewCompatWrapper -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.BOTTOM -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.TOP +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.common.test.InstantSchedulersRule import com.duckduckgo.common.ui.tabs.SwipingTabsFeature @@ -675,7 +674,7 @@ class BrowserTabViewModelTest { whenever(mockSavedSitesRepository.getBookmarks()).thenReturn(bookmarksListFlow.consumeAsFlow()) whenever(mockRemoteMessagingRepository.messageFlow()).thenReturn(remoteMessageFlow.consumeAsFlow()) whenever(mockSettingsDataStore.automaticFireproofSetting).thenReturn(AutomaticFireproofSetting.ASK_EVERY_TIME) - whenever(mockSettingsDataStore.omnibarPosition).thenReturn(TOP) + whenever(mockSettingsDataStore.omnibarType).thenReturn(OmnibarType.SINGLE_TOP) whenever(mockSettingsDataStore.isFullUrlEnabled).then { isFullSiteAddressEnabled } whenever(mockSSLCertificatesFeature.allowBypass()).thenReturn(mockEnabledToggle) whenever(subscriptions.shouldLaunchPrivacyProForUrl(any())).thenReturn(false) @@ -5337,7 +5336,7 @@ class BrowserTabViewModelTest { @Test fun whenRefreshIsTriggeredByUserThenPrivacyProtectionsPopupManagerIsNotifiedWithBottomPosition() = runTest { - whenever(mockSettingsDataStore.omnibarPosition).thenReturn(BOTTOM) + whenever(mockSettingsDataStore.omnibarType).thenReturn(OmnibarType.SINGLE_BOTTOM) testee.onRefreshRequested(triggeredByUser = false) verify(mockPrivacyProtectionsPopupManager, never()).onPageRefreshTriggeredByUser(isOmnibarAtTheTop = false) testee.onRefreshRequested(triggeredByUser = true) diff --git a/app/src/main/java/com/duckduckgo/app/appearance/AppearanceActivity.kt b/app/src/main/java/com/duckduckgo/app/appearance/AppearanceActivity.kt index 475e836af60a..9ec000cc548d 100644 --- a/app/src/main/java/com/duckduckgo/app/appearance/AppearanceActivity.kt +++ b/app/src/main/java/com/duckduckgo/app/appearance/AppearanceActivity.kt @@ -30,13 +30,13 @@ import com.duckduckgo.app.appearance.AppearanceScreen.Default import com.duckduckgo.app.appearance.AppearanceScreen.HighlightedItem import com.duckduckgo.app.appearance.AppearanceViewModel.Command import com.duckduckgo.app.appearance.AppearanceViewModel.Command.LaunchAppIcon -import com.duckduckgo.app.appearance.AppearanceViewModel.Command.LaunchOmnibarPositionSettings +import com.duckduckgo.app.appearance.AppearanceViewModel.Command.LaunchOmnibarTypeSettings import com.duckduckgo.app.appearance.AppearanceViewModel.Command.LaunchThemeSettings import com.duckduckgo.app.appearance.AppearanceViewModel.Command.UpdateTheme import com.duckduckgo.app.browser.R import com.duckduckgo.app.browser.databinding.ActivityAppearanceBinding import com.duckduckgo.app.fire.FireActivity -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.ui.DuckDuckGoActivity import com.duckduckgo.common.ui.DuckDuckGoTheme import com.duckduckgo.common.ui.DuckDuckGoTheme.DARK @@ -127,7 +127,7 @@ class AppearanceActivity : DuckDuckGoActivity() { binding.experimentalNightMode.quietlySetIsChecked(viewState.forceDarkModeEnabled, forceDarkModeToggleListener) binding.experimentalNightMode.isEnabled = viewState.canForceDarkMode binding.experimentalNightMode.isVisible = viewState.supportsForceDarkMode - updateSelectedOmnibarPosition(it.omnibarPosition) + updateSelectedOmnibarPosition(it.omnibarType) binding.showFullUrlSetting.quietlySetIsChecked(viewState.isFullUrlEnabled, showFullUrlToggleListener) binding.showTrackersCountInTabSwitcher.quietlySetIsChecked( viewState.isTrackersCountInTabSwitcherEnabled, @@ -155,12 +155,13 @@ class AppearanceActivity : DuckDuckGoActivity() { binding.selectedThemeSetting.setSecondaryText(subtitle) } - private fun updateSelectedOmnibarPosition(position: OmnibarPosition) { + private fun updateSelectedOmnibarPosition(omnibarType: OmnibarType) { val subtitle = getString( - when (position) { - OmnibarPosition.TOP -> R.string.settingsAddressBarPositionTop - OmnibarPosition.BOTTOM -> R.string.settingsAddressBarPositionBottom + when (omnibarType) { + OmnibarType.SINGLE_TOP -> R.string.settingsAddressBarPositionTop + OmnibarType.SINGLE_BOTTOM -> R.string.settingsAddressBarPositionBottom + OmnibarType.SPLIT -> TODO() }, ) binding.addressBarPositionSetting.setSecondaryText(subtitle) @@ -171,7 +172,7 @@ class AppearanceActivity : DuckDuckGoActivity() { is LaunchAppIcon -> launchAppIconChange() is UpdateTheme -> sendThemeChangedBroadcast() is LaunchThemeSettings -> launchThemeSelector(it.theme) - is LaunchOmnibarPositionSettings -> launchOmnibarPositionSelector(it.position) + is LaunchOmnibarTypeSettings -> launchOmnibarPositionSelector(it.omnibarType) } } @@ -207,7 +208,7 @@ class AppearanceActivity : DuckDuckGoActivity() { ).show() } - private fun launchOmnibarPositionSelector(position: OmnibarPosition) { + private fun launchOmnibarPositionSelector(type: OmnibarType) { RadioListAlertDialogBuilder(this) .setTitle(R.string.settingsAddressBarPositionTitle) .setOptions( @@ -215,15 +216,15 @@ class AppearanceActivity : DuckDuckGoActivity() { R.string.settingsAddressBarPositionTop, R.string.settingsAddressBarPositionBottom, ), - OmnibarPosition.entries.indexOf(position) + 1, + OmnibarType.entries.indexOf(type) + 1, ).setPositiveButton(com.duckduckgo.mobile.android.R.string.dialogSave) .setNegativeButton(R.string.cancel) .setCancelable(true) .addEventListener( object : RadioListAlertDialogBuilder.EventListener() { override fun onPositiveButtonClicked(selectedItem: Int) { - val newPosition = OmnibarPosition.entries[selectedItem - 1] - viewModel.onOmnibarPositionUpdated(newPosition) + val newType = OmnibarType.entries[selectedItem - 1] + viewModel.setOmnibarType(newType) } }, ).show() diff --git a/app/src/main/java/com/duckduckgo/app/appearance/AppearanceViewModel.kt b/app/src/main/java/com/duckduckgo/app/appearance/AppearanceViewModel.kt index c6f7e6e7c763..f07db53e5ddb 100644 --- a/app/src/main/java/com/duckduckgo/app/appearance/AppearanceViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/appearance/AppearanceViewModel.kt @@ -28,11 +28,8 @@ import com.duckduckgo.app.pixels.AppPixelName.SETTINGS_THEME_TOGGLED_SYSTEM_DEFA import com.duckduckgo.app.settings.db.SettingsDataStore import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.tabs.store.TabSwitcherDataStore -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.ui.DuckDuckGoTheme -import com.duckduckgo.common.ui.DuckDuckGoTheme.DARK -import com.duckduckgo.common.ui.DuckDuckGoTheme.LIGHT -import com.duckduckgo.common.ui.DuckDuckGoTheme.SYSTEM_DEFAULT import com.duckduckgo.common.ui.store.ThemingDataStore import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.ActivityScope @@ -40,9 +37,10 @@ import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -64,7 +62,7 @@ class AppearanceViewModel @Inject constructor( val forceDarkModeEnabled: Boolean = false, val canForceDarkMode: Boolean = false, val supportsForceDarkMode: Boolean = true, - val omnibarPosition: OmnibarPosition = OmnibarPosition.TOP, + val omnibarType: OmnibarType = OmnibarType.SINGLE_TOP, val isFullUrlEnabled: Boolean = true, val isTrackersCountInTabSwitcherEnabled: Boolean = true, ) @@ -78,32 +76,33 @@ class AppearanceViewModel @Inject constructor( data object UpdateTheme : Command() - data class LaunchOmnibarPositionSettings( - val position: OmnibarPosition, + data class LaunchOmnibarTypeSettings( + val omnibarType: OmnibarType, ) : Command() } - private val viewState = MutableStateFlow(ViewState()) - private val command = Channel(1, BufferOverflow.DROP_OLDEST) + private val viewState = MutableStateFlow( + ViewState( + theme = themingDataStore.theme, + appIcon = settingsDataStore.appIcon, + forceDarkModeEnabled = settingsDataStore.experimentalWebsiteDarkMode, + canForceDarkMode = canForceDarkMode(), + supportsForceDarkMode = WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING), + isFullUrlEnabled = settingsDataStore.isFullUrlEnabled, + omnibarType = settingsDataStore.omnibarType, + ), + ) - fun viewState(): Flow = - viewState.onStart { - viewModelScope.launch { - viewState.update { - currentViewState().copy( - theme = themingDataStore.theme, - appIcon = settingsDataStore.appIcon, - forceDarkModeEnabled = settingsDataStore.experimentalWebsiteDarkMode, - canForceDarkMode = canForceDarkMode(), - supportsForceDarkMode = WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING), - omnibarPosition = settingsDataStore.omnibarPosition, - isFullUrlEnabled = settingsDataStore.isFullUrlEnabled, - isTrackersCountInTabSwitcherEnabled = tabSwitcherDataStore.isTrackersAnimationInfoTileHidden().firstOrNull() != true, - ) - } - } - } + fun viewState() = combine( + viewState, + tabSwitcherDataStore.isTrackersAnimationInfoTileHidden(), + ) { currentViewState, isTrackersAnimationTileHidden -> + currentViewState.copy( + isTrackersCountInTabSwitcherEnabled = !isTrackersAnimationTileHidden, + ) + }.stateIn(viewModelScope, SharingStarted.Lazily, viewState.value) + private val command = Channel(1, BufferOverflow.DROP_OLDEST) fun commands(): Flow = command.receiveAsFlow() private fun canForceDarkMode(): Boolean = themingDataStore.theme != DuckDuckGoTheme.LIGHT @@ -119,7 +118,7 @@ class AppearanceViewModel @Inject constructor( } fun userRequestedToChangeAddressBarPosition() { - viewModelScope.launch { command.send(Command.LaunchOmnibarPositionSettings(viewState.value.omnibarPosition)) } + viewModelScope.launch { command.send(Command.LaunchOmnibarTypeSettings(viewState.value.omnibarType)) } pixel.fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_PRESSED) } @@ -132,34 +131,33 @@ class AppearanceViewModel @Inject constructor( viewModelScope.launch(dispatcherProvider.io()) { themingDataStore.theme = selectedTheme withContext(dispatcherProvider.main()) { - viewState.update { currentViewState().copy(theme = selectedTheme, forceDarkModeEnabled = canForceDarkMode()) } + viewState.update { it.copy(theme = selectedTheme, forceDarkModeEnabled = canForceDarkMode()) } command.send(Command.UpdateTheme) } } val pixelName = when (selectedTheme) { - LIGHT -> SETTINGS_THEME_TOGGLED_LIGHT - DARK -> SETTINGS_THEME_TOGGLED_DARK - SYSTEM_DEFAULT -> SETTINGS_THEME_TOGGLED_SYSTEM_DEFAULT + DuckDuckGoTheme.LIGHT -> SETTINGS_THEME_TOGGLED_LIGHT + DuckDuckGoTheme.DARK -> SETTINGS_THEME_TOGGLED_DARK + DuckDuckGoTheme.SYSTEM_DEFAULT -> SETTINGS_THEME_TOGGLED_SYSTEM_DEFAULT } pixel.fire(pixelName) } - fun onOmnibarPositionUpdated(position: OmnibarPosition) { + fun setOmnibarType(type: OmnibarType) { viewModelScope.launch(dispatcherProvider.io()) { - settingsDataStore.omnibarPosition = position - viewState.update { currentViewState().copy(omnibarPosition = position) } + settingsDataStore.omnibarType = type + viewState.update { it.copy(omnibarType = type) } - when (position) { - OmnibarPosition.TOP -> pixel.fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_SELECTED_TOP) - OmnibarPosition.BOTTOM -> pixel.fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_SELECTED_BOTTOM) + when (type) { + OmnibarType.SINGLE_TOP -> pixel.fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_SELECTED_TOP) + OmnibarType.SINGLE_BOTTOM -> pixel.fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_SELECTED_BOTTOM) + OmnibarType.SPLIT -> pixel.fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_SELECTED_SPLIT_TOP) } } } - private fun currentViewState(): ViewState = viewState.value - fun onForceDarkModeSettingChanged(checked: Boolean) { viewModelScope.launch(dispatcherProvider.io()) { if (checked) { diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserActivity.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserActivity.kt index 8e8eb28ad697..d861b09654fb 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserActivity.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserActivity.kt @@ -94,8 +94,7 @@ import com.duckduckgo.appbuildconfig.api.AppBuildConfig import com.duckduckgo.autofill.api.emailprotection.EmailProtectionLinkVerifier import com.duckduckgo.browser.api.ui.BrowserScreens.BookmarksScreenNoParams import com.duckduckgo.browser.api.ui.BrowserScreens.SettingsScreenNoParams -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.BOTTOM -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.TOP +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.ui.DuckDuckGoActivity import com.duckduckgo.common.ui.tabs.SwipingTabsFeatureProvider import com.duckduckgo.common.ui.view.addBottomShadow @@ -765,9 +764,9 @@ open class BrowserActivity : DuckDuckGoActivity() { delay(500) val anchorView = - when (settingsDataStore.omnibarPosition) { - TOP -> null - BOTTOM -> currentTab?.getOmnibar()?.omnibarView?.toolbar ?: binding.fragmentContainer + when (settingsDataStore.omnibarType) { + OmnibarType.SINGLE_TOP, OmnibarType.SPLIT -> null + OmnibarType.SINGLE_BOTTOM -> currentTab?.getOmnibar()?.omnibarView?.toolbar ?: binding.fragmentContainer } DefaultSnackbar( parentView = binding.fragmentContainer, @@ -1397,8 +1396,8 @@ open class BrowserActivity : DuckDuckGoActivity() { } private fun bindMockupToolbars() { - when (settingsDataStore.omnibarPosition) { - TOP -> { + when (settingsDataStore.omnibarType) { + OmnibarType.SINGLE_TOP, OmnibarType.SPLIT -> { if (Build.VERSION.SDK_INT < 28) { binding.topMockupSingleToolbar.mockOmniBarContainerShadow.cardElevation = 2f.toPx(this) } @@ -1415,7 +1414,7 @@ open class BrowserActivity : DuckDuckGoActivity() { } } - BOTTOM -> { + OmnibarType.SINGLE_BOTTOM -> { if (Build.VERSION.SDK_INT < 28) { binding.bottomMockupSingleToolbar.mockOmniBarContainerShadow.cardElevation = 0.5f.toPx(this) } diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index 28a86a610195..4baab72cec59 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -149,6 +149,7 @@ import com.duckduckgo.app.browser.omnibar.Omnibar.LogoClickListener import com.duckduckgo.app.browser.omnibar.Omnibar.OmnibarTextState import com.duckduckgo.app.browser.omnibar.Omnibar.TextListener import com.duckduckgo.app.browser.omnibar.Omnibar.ViewMode +import com.duckduckgo.app.browser.omnibar.OmnibarFeatureRepository import com.duckduckgo.app.browser.omnibar.OmnibarItemPressedListener import com.duckduckgo.app.browser.omnibar.QueryOrigin import com.duckduckgo.app.browser.print.PrintDocumentAdapterFactory @@ -250,8 +251,7 @@ import com.duckduckgo.browser.api.ui.BrowserScreens.PrivateSearchScreenNoParams import com.duckduckgo.browser.api.ui.BrowserScreens.WebViewActivityWithParams import com.duckduckgo.browser.api.webviewcompat.WebViewCompatWrapper import com.duckduckgo.browser.ui.autocomplete.BrowserAutoCompleteSuggestionsAdapter -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.BOTTOM -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.TOP +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.ui.DuckDuckGoActivity import com.duckduckgo.common.ui.DuckDuckGoFragment import com.duckduckgo.common.ui.store.BrowserAppTheme @@ -596,6 +596,9 @@ class BrowserTabFragment : @Inject lateinit var webViewCompatWrapper: WebViewCompatWrapper + @Inject + lateinit var omnibarFeatureRepository: OmnibarFeatureRepository + /** * We use this to monitor whether the user was seeing the in-context Email Protection signup prompt * This is needed because the activity stack will be cleared if an external link is opened in our browser @@ -1017,9 +1020,9 @@ class BrowserTabFragment : super.onActivityCreated(savedInstanceState) omnibar = Omnibar( - omnibarPosition = settingsDataStore.omnibarPosition, + omnibarType = settingsDataStore.omnibarType, binding = binding, - isUnifiedOmnibarEnabled = androidBrowserConfigFeature.useUnifiedOmnibarLayout().isEnabled(), + isUnifiedOmnibarEnabled = omnibarFeatureRepository.isUnifiedOmnibarEnabled, ) webViewContainer = binding.webViewContainer @@ -1123,17 +1126,18 @@ class BrowserTabFragment : } private fun launchInputScreen(query: String) { + val isTopOmnibar = omnibar.omnibarType != OmnibarType.SINGLE_BOTTOM val intent = globalActivityStarter.startIntent( requireContext(), InputScreenActivityParams( query = query, - isTopOmnibar = omnibar.omnibarPosition == TOP, + isTopOmnibar = isTopOmnibar, browserButtonsConfig = InputScreenBrowserButtonsConfig.Enabled(tabs = viewModel.tabs.value?.size ?: 0), ), ) - val enterTransition = browserAndInputScreenTransitionProvider.getInputScreenEnterAnimation(omnibar.omnibarPosition == TOP) - val exitTransition = browserAndInputScreenTransitionProvider.getBrowserExitAnimation(omnibar.omnibarPosition == TOP) + val enterTransition = browserAndInputScreenTransitionProvider.getInputScreenEnterAnimation(isTopOmnibar) + val exitTransition = browserAndInputScreenTransitionProvider.getBrowserExitAnimation(isTopOmnibar) val options = ActivityOptionsCompat.makeCustomAnimation( requireActivity(), @@ -1286,9 +1290,9 @@ class BrowserTabFragment : private fun createPopupMenu() { val popupMenuResourceType = - when (settingsDataStore.omnibarPosition) { - TOP -> BrowserPopupMenu.ResourceType.TOP - BOTTOM -> BrowserPopupMenu.ResourceType.BOTTOM + when (omnibar.omnibarType) { + OmnibarType.SINGLE_TOP, OmnibarType.SPLIT -> BrowserPopupMenu.ResourceType.TOP + OmnibarType.SINGLE_BOTTOM -> BrowserPopupMenu.ResourceType.BOTTOM } popupMenu = @@ -1483,8 +1487,7 @@ class BrowserTabFragment : return } - val hasOmnibarPositionChanged = viewModel.hasOmnibarPositionChanged(omnibar.omnibarPosition) - if (hasOmnibarPositionChanged) { + if (viewModel.hasOmnibarTypeChanged(omnibar.omnibarType)) { viewModel.resetTrackersCount() if (swipingTabsFeature.isEnabled && requireActivity() is BrowserActivity) { (requireActivity() as BrowserActivity).clearTabsAndRecreate() @@ -2945,7 +2948,7 @@ class BrowserTabFragment : autoCompleteLongPressClickListener = { viewModel.userLongPressedAutocomplete(it) }, - omnibarPosition = settingsDataStore.omnibarPosition, + omnibarType = settingsDataStore.omnibarType, ) binding.autoCompleteSuggestionsList.adapter = autoCompleteSuggestionsAdapter } diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt index 7bb2a2358bff..50afef13eb61 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt @@ -293,8 +293,7 @@ import com.duckduckgo.browser.api.brokensite.BrokenSiteData import com.duckduckgo.browser.api.brokensite.BrokenSiteData.ReportFlow.MENU import com.duckduckgo.browser.api.brokensite.BrokenSiteData.ReportFlow.RELOAD_THREE_TIMES_WITHIN_20_SECONDS import com.duckduckgo.browser.api.webviewcompat.WebViewCompatWrapper -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.TOP +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.ui.tabs.SwipingTabsFeatureProvider import com.duckduckgo.common.utils.AppUrl import com.duckduckgo.common.utils.AppUrl.ParamKey.QUERY @@ -1380,7 +1379,9 @@ class BrowserTabViewModel @Inject constructor( site?.uri?.let { brokenSitePrompt.pageRefreshed(it) } - privacyProtectionsPopupManager.onPageRefreshTriggeredByUser(isOmnibarAtTheTop = settingsDataStore.omnibarPosition == TOP) + privacyProtectionsPopupManager.onPageRefreshTriggeredByUser( + isOmnibarAtTheTop = settingsDataStore.omnibarType != OmnibarType.SINGLE_BOTTOM, + ) } } @@ -4389,7 +4390,7 @@ class BrowserTabViewModel @Inject constructor( fun isPrinting(): Boolean = currentBrowserViewState().isPrinting - fun hasOmnibarPositionChanged(currentPosition: OmnibarPosition): Boolean = settingsDataStore.omnibarPosition != currentPosition + fun hasOmnibarTypeChanged(omnibarType: OmnibarType): Boolean = settingsDataStore.omnibarType != omnibarType private fun firePixelBasedOnCurrentUrl( emptyUrlPixel: AppPixelName, diff --git a/app/src/main/java/com/duckduckgo/app/browser/newaddressbaroption/NewAddressBarOptionManager.kt b/app/src/main/java/com/duckduckgo/app/browser/newaddressbaroption/NewAddressBarOptionManager.kt index fcf517d73190..2691c03025ea 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/newaddressbaroption/NewAddressBarOptionManager.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/newaddressbaroption/NewAddressBarOptionManager.kt @@ -20,7 +20,7 @@ import android.app.Activity import com.duckduckgo.app.onboarding.store.AppStage import com.duckduckgo.app.onboarding.store.UserStageStore import com.duckduckgo.app.settings.db.SettingsDataStore -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.ui.DuckDuckGoActivity import com.duckduckgo.common.utils.DefaultDispatcherProvider import com.duckduckgo.common.utils.DispatcherProvider @@ -131,7 +131,7 @@ class RealNewAddressBarOptionManager @Inject constructor( } private fun isBottomAddressBarDisabled(): Boolean = - (settingsDataStore.omnibarPosition != OmnibarPosition.BOTTOM).also { + (settingsDataStore.omnibarType != OmnibarType.SINGLE_BOTTOM).also { logcat(DEBUG) { "NewAddressBarOptionManager: $it isBottomAddressBarDisabled" } } diff --git a/app/src/main/java/com/duckduckgo/app/browser/omnibar/LegacyOmnibarLayout.kt b/app/src/main/java/com/duckduckgo/app/browser/omnibar/LegacyOmnibarLayout.kt index d8aa47a62843..7da2dc2469b9 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/omnibar/LegacyOmnibarLayout.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/omnibar/LegacyOmnibarLayout.kt @@ -89,7 +89,7 @@ import com.duckduckgo.app.global.view.renderIfChanged import com.duckduckgo.app.onboardingdesignexperiment.OnboardingDesignExperimentManager import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.trackerdetection.model.Entity -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.browser.ui.tabs.TabSwitcherButton import com.duckduckgo.common.ui.DuckDuckGoActivity import com.duckduckgo.common.ui.view.KeyboardAwareEditText @@ -114,7 +114,6 @@ import kotlinx.coroutines.launch import logcat.logcat import javax.inject.Inject import kotlin.collections.isNotEmpty -import kotlin.jvm.java import com.duckduckgo.app.global.model.PrivacyShield as PrivacyShieldState import com.duckduckgo.mobile.android.R as CommonR @@ -294,7 +293,7 @@ open class LegacyOmnibarLayout @JvmOverloads constructor( } } - override val omnibarPosition: OmnibarPosition = OmnibarPosition.TOP + override val omnibarType: OmnibarType = OmnibarType.SINGLE_TOP private val smoothProgressAnimator by lazy { SmoothProgressAnimator(pageLoadingIndicator) } @@ -951,15 +950,17 @@ open class LegacyOmnibarLayout @JvmOverloads constructor( override fun isBottomNavEnabled(): Boolean = false override fun getBehavior(): CoordinatorLayout.Behavior = - when (omnibarPosition) { - OmnibarPosition.TOP -> TopAppBarBehavior(context, this) - OmnibarPosition.BOTTOM -> BottomAppBarBehavior(context, this) + when (omnibarType) { + OmnibarType.SINGLE_TOP -> TopAppBarBehavior(context, this) + OmnibarType.SINGLE_BOTTOM -> BottomAppBarBehavior(context, this) + else -> throw IllegalStateException("OmnibarType $omnibarType not supported in OmnibarLayout") } override fun setExpanded(expanded: Boolean) { - when (omnibarPosition) { - OmnibarPosition.TOP -> super.setExpanded(expanded) - OmnibarPosition.BOTTOM -> (behavior as BottomAppBarBehavior).setExpanded(expanded) + when (omnibarType) { + OmnibarType.SINGLE_TOP -> super.setExpanded(expanded) + OmnibarType.SINGLE_BOTTOM -> (behavior as BottomAppBarBehavior).setExpanded(expanded) + else -> throw IllegalStateException("OmnibarType $omnibarType not supported in OmnibarLayout") } } @@ -967,9 +968,10 @@ open class LegacyOmnibarLayout @JvmOverloads constructor( expanded: Boolean, animate: Boolean, ) { - when (omnibarPosition) { - OmnibarPosition.TOP -> super.setExpanded(expanded, animate) - OmnibarPosition.BOTTOM -> (behavior as BottomAppBarBehavior).setExpanded(expanded) + when (omnibarType) { + OmnibarType.SINGLE_TOP -> super.setExpanded(expanded, animate) + OmnibarType.SINGLE_BOTTOM -> (behavior as BottomAppBarBehavior).setExpanded(expanded) + else -> throw IllegalStateException("OmnibarType $omnibarType not supported in OmnibarLayout") } } diff --git a/app/src/main/java/com/duckduckgo/app/browser/omnibar/Omnibar.kt b/app/src/main/java/com/duckduckgo/app/browser/omnibar/Omnibar.kt index 6feea615a191..abde3854b824 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/omnibar/Omnibar.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/omnibar/Omnibar.kt @@ -42,7 +42,7 @@ import com.duckduckgo.app.browser.viewstate.OmnibarViewState import com.duckduckgo.app.browser.webview.BottomOmnibarBrowserContainerLayoutBehavior import com.duckduckgo.app.global.model.PrivacyShield import com.duckduckgo.app.trackerdetection.model.Entity -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.ui.view.KeyboardAwareEditText import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.hide @@ -59,7 +59,7 @@ import logcat.logcat @SuppressLint("ClickableViewAccessibility") class Omnibar( - val omnibarPosition: OmnibarPosition, + val omnibarType: OmnibarType, private val binding: FragmentBrowserTabBinding, isUnifiedOmnibarEnabled: Boolean, ) { @@ -164,17 +164,18 @@ class Omnibar( binding.rootView.removeView(binding.omnibarLayoutTop) binding.rootView.removeView(binding.omnibarLayoutBottom) - return when (omnibarPosition) { - OmnibarPosition.TOP -> { + return when (omnibarType) { + OmnibarType.SINGLE_TOP -> { binding.rootView.removeView(binding.singleOmnibarLayoutBottom) binding.singleOmnibarLayoutTop } - OmnibarPosition.BOTTOM -> { + OmnibarType.SINGLE_BOTTOM -> { binding.rootView.removeView(binding.singleOmnibarLayoutTop) adjustCoordinatorLayoutBehaviorForBottomOmnibar() binding.singleOmnibarLayoutBottom } + else -> throw IllegalStateException("Invalid omnibar type for single omnibar setup: $omnibarType") } } @@ -182,13 +183,13 @@ class Omnibar( binding.rootView.removeView(binding.singleOmnibarLayoutTop) binding.rootView.removeView(binding.singleOmnibarLayoutBottom) - return when (omnibarPosition) { - OmnibarPosition.TOP -> { + return when (omnibarType) { + OmnibarType.SINGLE_TOP, OmnibarType.SPLIT -> { binding.rootView.removeView(binding.omnibarLayoutBottom) binding.omnibarLayoutTop } - OmnibarPosition.BOTTOM -> { + OmnibarType.SINGLE_BOTTOM -> { binding.rootView.removeView(binding.omnibarLayoutTop) adjustCoordinatorLayoutBehaviorForBottomOmnibar() binding.omnibarLayoutBottom diff --git a/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarLayout.kt b/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarLayout.kt index ba88594b4733..6985a46169c1 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarLayout.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarLayout.kt @@ -92,9 +92,10 @@ import com.duckduckgo.app.browser.omnibar.model.Decoration.QueueCookiesAnimation import com.duckduckgo.app.browser.omnibar.model.StateChange import com.duckduckgo.app.global.view.renderIfChanged import com.duckduckgo.app.onboardingdesignexperiment.OnboardingDesignExperimentManager +import com.duckduckgo.app.settings.db.SettingsDataStore import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.trackerdetection.model.Entity -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.browser.ui.tabs.TabSwitcherButton import com.duckduckgo.common.ui.DuckDuckGoActivity import com.duckduckgo.common.ui.view.KeyboardAwareEditText @@ -188,6 +189,9 @@ class OmnibarLayout @JvmOverloads constructor( @Inject lateinit var addressBarTrackersAnimationFeatureToggle: AddressBarTrackersAnimationFeatureToggle + @Inject + lateinit var settingsDataStore: SettingsDataStore + private var previousTransitionState: TransitionState? = null private val lifecycleOwner: LifecycleOwner by lazy { @@ -238,17 +242,20 @@ class OmnibarLayout @JvmOverloads constructor( private var focusAnimator: ValueAnimator? = null - override val omnibarPosition: OmnibarPosition + override val omnibarType: OmnibarType init { inflate(context, R.layout.view_omnibar, this) - val attr = context.theme.obtainStyledAttributes(attrs, R.styleable.OmnibarLayout, defStyle, 0) - omnibarPosition = OmnibarPosition.entries[attr.getInt(R.styleable.OmnibarLayout_omnibarPosition, 0)] - AndroidSupportInjection.inject(this) - renderPosition() + omnibarType = settingsDataStore.omnibarType + + val attr = context.theme.obtainStyledAttributes(attrs, R.styleable.OmnibarLayout, defStyle, 0) + val omnibarType = OmnibarType.entries[attr.getInt(R.styleable.OmnibarLayout_omnibarPosition, 0)] + val isTopPosition = omnibarType == OmnibarType.SINGLE_TOP || omnibarType == OmnibarType.SPLIT + + renderPosition(isTopPosition) if (Build.VERSION.SDK_INT >= 28) { omnibarCardShadow.addBottomShadow() @@ -358,7 +365,7 @@ class OmnibarLayout @JvmOverloads constructor( private val smoothProgressAnimator by lazy { SmoothProgressAnimator(pageLoadingIndicator) } - protected val viewModel: OmnibarLayoutViewModel by lazy { + private val viewModel: OmnibarLayoutViewModel by lazy { ViewModelProvider( findViewTreeViewModelStoreOwner()!!, viewModelFactory, @@ -589,61 +596,57 @@ class OmnibarLayout @JvmOverloads constructor( } } - private fun renderPosition() { - when (omnibarPosition) { - OmnibarPosition.TOP -> { - if (Build.VERSION.SDK_INT < 28) { - omnibarCardShadow.cardElevation = 2f.toPx(context) - } + private fun renderPosition(isTopPosition: Boolean) { + if (isTopPosition) { + if (Build.VERSION.SDK_INT < 28) { + omnibarCardShadow.cardElevation = 2f.toPx(context) + } - shieldIconPulseAnimationContainer.updateLayoutParams { - (this as MarginLayoutParams).apply { - if (addressBarTrackersAnimationFeatureToggle.feature().isEnabled()) { - // TODO when the animation is made permanent we should add this adjustment to the actual layout - marginStart = 1.toPx() - } + shieldIconPulseAnimationContainer.updateLayoutParams { + (this as MarginLayoutParams).apply { + if (addressBarTrackersAnimationFeatureToggle.feature().isEnabled()) { + // TODO when the animation is made permanent we should add this adjustment to the actual layout + marginStart = 1.toPx() } } } - - OmnibarPosition.BOTTOM -> { - // When omnibar is at the bottom, we're adding an additional space at the top - omnibarCardShadow.updateLayoutParams { - (this as MarginLayoutParams).apply { - topMargin = experimentalOmnibarCardMarginBottom - bottomMargin = experimentalOmnibarCardMarginTop - } + } else { + // When omnibar is at the bottom, we're adding an additional space at the top + omnibarCardShadow.updateLayoutParams { + (this as MarginLayoutParams).apply { + topMargin = experimentalOmnibarCardMarginBottom + bottomMargin = experimentalOmnibarCardMarginTop } + } - iconsContainer.updateLayoutParams { - (this as MarginLayoutParams).apply { - topMargin = experimentalOmnibarCardMarginBottom - bottomMargin = experimentalOmnibarCardMarginTop - } + iconsContainer.updateLayoutParams { + (this as MarginLayoutParams).apply { + topMargin = experimentalOmnibarCardMarginBottom + bottomMargin = experimentalOmnibarCardMarginTop } + } - shieldIconPulseAnimationContainer.updateLayoutParams { - (this as MarginLayoutParams).apply { - topMargin = experimentalOmnibarCardMarginBottom - bottomMargin = experimentalOmnibarCardMarginTop - if (addressBarTrackersAnimationFeatureToggle.feature().isEnabled()) { - // TODO when the animation is made permanent we should add this adjustment to the actual layout - marginStart = 1.toPx() - } + shieldIconPulseAnimationContainer.updateLayoutParams { + (this as MarginLayoutParams).apply { + topMargin = experimentalOmnibarCardMarginBottom + bottomMargin = experimentalOmnibarCardMarginTop + if (addressBarTrackersAnimationFeatureToggle.feature().isEnabled()) { + // TODO when the animation is made permanent we should add this adjustment to the actual layout + marginStart = 1.toPx() } } + } - shieldIconPulseAnimationContainer.setPadding( - shieldIconPulseAnimationContainer.paddingLeft, - shieldIconPulseAnimationContainer.paddingTop, - shieldIconPulseAnimationContainer.paddingRight, - 6.toPx(), - ) + shieldIconPulseAnimationContainer.setPadding( + shieldIconPulseAnimationContainer.paddingLeft, + shieldIconPulseAnimationContainer.paddingTop, + shieldIconPulseAnimationContainer.paddingRight, + 6.toPx(), + ) - // Try to reduce the bottom omnibar material shadow when not using the custom shadow - if (Build.VERSION.SDK_INT < 28) { - omnibarCardShadow.cardElevation = 0.5f.toPx(context) - } + // Try to reduce the bottom omnibar material shadow when not using the custom shadow + if (Build.VERSION.SDK_INT < 28) { + omnibarCardShadow.cardElevation = 0.5f.toPx(context) } } } @@ -1099,15 +1102,15 @@ class OmnibarLayout @JvmOverloads constructor( override fun isBottomNavEnabled(): Boolean = false override fun getBehavior(): CoordinatorLayout.Behavior = - when (omnibarPosition) { - OmnibarPosition.TOP -> TopAppBarBehavior(context, this) - OmnibarPosition.BOTTOM -> BottomAppBarBehavior(context, this) + when (omnibarType) { + OmnibarType.SINGLE_TOP, OmnibarType.SPLIT -> TopAppBarBehavior(context, this) + OmnibarType.SINGLE_BOTTOM -> BottomAppBarBehavior(context, this) } override fun setExpanded(expanded: Boolean) { - when (omnibarPosition) { - OmnibarPosition.TOP -> super.setExpanded(expanded) - OmnibarPosition.BOTTOM -> (behavior as BottomAppBarBehavior).setExpanded(expanded) + when (omnibarType) { + OmnibarType.SINGLE_TOP, OmnibarType.SPLIT -> super.setExpanded(expanded) + OmnibarType.SINGLE_BOTTOM -> (behavior as BottomAppBarBehavior).setExpanded(expanded) } } @@ -1115,9 +1118,9 @@ class OmnibarLayout @JvmOverloads constructor( expanded: Boolean, animate: Boolean, ) { - when (omnibarPosition) { - OmnibarPosition.TOP -> super.setExpanded(expanded, animate) - OmnibarPosition.BOTTOM -> (behavior as BottomAppBarBehavior).setExpanded(expanded) + when (omnibarType) { + OmnibarType.SINGLE_TOP, OmnibarType.SPLIT -> super.setExpanded(expanded, animate) + OmnibarType.SINGLE_BOTTOM -> (behavior as BottomAppBarBehavior).setExpanded(expanded) } } diff --git a/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarRepository.kt b/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarRepository.kt new file mode 100644 index 000000000000..73f0d0fa9787 --- /dev/null +++ b/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarRepository.kt @@ -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.app.browser.omnibar + +import androidx.lifecycle.LifecycleOwner +import com.duckduckgo.app.di.AppCoroutineScope +import com.duckduckgo.app.lifecycle.MainProcessLifecycleObserver +import com.duckduckgo.app.pixels.remoteconfig.AndroidBrowserConfigFeature +import com.duckduckgo.app.settings.db.SettingsDataStore +import com.duckduckgo.browser.ui.omnibar.OmnibarType +import com.duckduckgo.common.utils.DispatcherProvider +import com.duckduckgo.di.scopes.AppScope +import com.squareup.anvil.annotations.ContributesMultibinding +import dagger.SingleInstanceIn +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import javax.inject.Inject + +@ContributesMultibinding( + scope = AppScope::class, + boundType = MainProcessLifecycleObserver::class, +) +@SingleInstanceIn(AppScope::class) +class OmnibarFeatureRepository @Inject constructor( + private val settingsDataStore: SettingsDataStore, + private val browserFeatures: AndroidBrowserConfigFeature, + private val dispatcherProvider: DispatcherProvider, + @AppCoroutineScope private val coroutineScope: CoroutineScope, +) : MainProcessLifecycleObserver { + var isUnifiedOmnibarEnabled: Boolean = false + private set + + var isSplitOmnibarEnabled: Boolean = false + private set + + val isSplitOmnibarAvailable: Boolean + get() = isUnifiedOmnibarEnabled && isSplitOmnibarEnabled + + override fun onStart(owner: LifecycleOwner) { + updateFeatureFlags() + } + + fun updateFeatureFlags() { + coroutineScope.launch(dispatcherProvider.io()) { + isUnifiedOmnibarEnabled = browserFeatures.useUnifiedOmnibarLayout().isEnabled() + isSplitOmnibarEnabled = browserFeatures.splitOmnibar().isEnabled() + + resetOmnibarTypeIfNecessary() + } + } + + private fun resetOmnibarTypeIfNecessary() { + if (settingsDataStore.omnibarType == OmnibarType.SPLIT && !isSplitOmnibarAvailable) { + settingsDataStore.isSplitOmnibarSelected = true + settingsDataStore.omnibarType = OmnibarType.SINGLE_TOP + } else if (settingsDataStore.isSplitOmnibarSelected && isSplitOmnibarAvailable) { + // Restore user's choice if the feature is re-enabled + settingsDataStore.omnibarType = OmnibarType.SPLIT + settingsDataStore.isSplitOmnibarSelected = false + } + } +} diff --git a/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarPositionDetector.kt b/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarTypeDetector.kt similarity index 86% rename from app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarPositionDetector.kt rename to app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarTypeDetector.kt index df332394c22f..9b0a696d849f 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarPositionDetector.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarTypeDetector.kt @@ -25,15 +25,15 @@ import com.squareup.anvil.annotations.ContributesMultibinding import dagger.SingleInstanceIn import javax.inject.Inject -interface OmnibarPositionReporterPlugin +interface OmnibarTypeReporterPlugin @ContributesMultibinding(scope = AppScope::class, boundType = BrowserFeatureStateReporterPlugin::class) -@ContributesBinding(scope = AppScope::class, boundType = OmnibarPositionReporterPlugin::class) +@ContributesBinding(scope = AppScope::class, boundType = OmnibarTypeReporterPlugin::class) @SingleInstanceIn(AppScope::class) -class OmnibarPositionDetector @Inject constructor( +class OmnibarTypeDetector @Inject constructor( private val settingsDataStore: SettingsDataStore, -) : OmnibarPositionReporterPlugin, BrowserFeatureStateReporterPlugin { +) : OmnibarTypeReporterPlugin, BrowserFeatureStateReporterPlugin { override fun featureStateParams(): Map { - return mapOf(PixelParameter.ADDRESS_BAR to settingsDataStore.omnibarPosition.name) + return mapOf(PixelParameter.ADDRESS_BAR to settingsDataStore.omnibarType.typeName) } } diff --git a/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarView.kt b/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarView.kt index e00f9364b3f3..c680ef43503d 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarView.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarView.kt @@ -27,12 +27,12 @@ import com.duckduckgo.app.browser.omnibar.Omnibar.LogoClickListener import com.duckduckgo.app.browser.omnibar.Omnibar.TextListener import com.duckduckgo.app.browser.omnibar.model.Decoration import com.duckduckgo.app.browser.omnibar.model.StateChange -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.ui.view.KeyboardAwareEditText import kotlinx.coroutines.flow.Flow interface OmnibarView { - val omnibarPosition: OmnibarPosition + val omnibarType: OmnibarType var isScrollingEnabled: Boolean val isEditing: Boolean val isEditingFlow: Flow diff --git a/app/src/main/java/com/duckduckgo/app/browser/omnibar/SingleOmnibarLayout.kt b/app/src/main/java/com/duckduckgo/app/browser/omnibar/SingleOmnibarLayout.kt index 9b5667c83828..7f2d4729eb81 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/omnibar/SingleOmnibarLayout.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/omnibar/SingleOmnibarLayout.kt @@ -31,7 +31,7 @@ import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.app.browser.R import com.duckduckgo.app.browser.omnibar.Omnibar.ViewMode import com.duckduckgo.app.browser.omnibar.OmnibarLayoutViewModel.ViewState -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.ui.view.addBottomShadow import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.hide @@ -89,11 +89,11 @@ class SingleOmnibarLayout @JvmOverloads constructor( private var singleOmnibarItemPressedListener: OmnibarItemPressedListener? = null - override val omnibarPosition: OmnibarPosition + override val omnibarType: OmnibarType init { val attr = context.theme.obtainStyledAttributes(attrs, R.styleable.OmnibarLayout, defStyle, 0) - omnibarPosition = OmnibarPosition.entries[attr.getInt(R.styleable.OmnibarLayout_omnibarPosition, 0)] + omnibarType = OmnibarType.entries[attr.getInt(R.styleable.OmnibarLayout_omnibarPosition, 0)] inflate(context, R.layout.view_single_omnibar, this) @@ -103,8 +103,8 @@ class SingleOmnibarLayout @JvmOverloads constructor( omnibarCardShadow.addBottomShadow() } - when (omnibarPosition) { - OmnibarPosition.TOP -> { + when (omnibarType) { + OmnibarType.SINGLE_TOP -> { if (Build.VERSION.SDK_INT < 28) { omnibarCardShadow.cardElevation = 2f.toPx(context) } @@ -118,7 +118,7 @@ class SingleOmnibarLayout @JvmOverloads constructor( } } } - OmnibarPosition.BOTTOM -> { + OmnibarType.SINGLE_BOTTOM -> { // When omnibar is at the bottom, we're adding an additional space at the top omnibarCardShadow.updateLayoutParams { (this as MarginLayoutParams).apply { @@ -157,6 +157,7 @@ class SingleOmnibarLayout @JvmOverloads constructor( omnibarCardShadow.cardElevation = 0.5f.toPx(context) } } + else -> throw IllegalStateException("OmnibarType $omnibarType not supported in SingleOmnibarLayout") } } diff --git a/app/src/main/java/com/duckduckgo/app/browser/webview/BrowserContainerLayoutBehavior.kt b/app/src/main/java/com/duckduckgo/app/browser/webview/BrowserContainerLayoutBehavior.kt index 3156018ee34f..7e6164e28136 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/webview/BrowserContainerLayoutBehavior.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/webview/BrowserContainerLayoutBehavior.kt @@ -25,7 +25,7 @@ import androidx.core.view.isGone import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarView import com.duckduckgo.app.browser.omnibar.OmnibarLayout import com.duckduckgo.app.browser.omnibar.OmnibarView -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior @@ -38,7 +38,7 @@ import com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior * * This prevents the omnibar and the navigation bar from overlapping with, for example, content found in the web view. * - * Note: If bottom [OmnibarLayout] is used ([OmnibarPosition.BOTTOM]), [BottomOmnibarBrowserContainerLayoutBehavior] should be set to the target child. + * Note: If bottom [OmnibarLayout] is used ([OmnibarType.SINGLE_BOTTOM]), [BottomOmnibarBrowserContainerLayoutBehavior] should be set to the target child. */ class TopOmnibarBrowserContainerLayoutBehavior( context: Context, @@ -63,7 +63,7 @@ class TopOmnibarBrowserContainerLayoutBehavior( } /** - * A behavior that observes the position of the bottom [OmnibarLayout] ([OmnibarPosition.BOTTOM]), if present, + * A behavior that observes the position of the bottom [OmnibarLayout] ([OmnibarType.SINGLE_BOTTOM]), if present, * and applies bottom padding to the target view equal to the visible height of the omnibar. * * This prevents the omnibar from overlapping with, for example, content found in the web view. @@ -73,7 +73,7 @@ class TopOmnibarBrowserContainerLayoutBehavior( * * We don't need to additionally observe the position of the [BrowserNavigationBarView] when bottom [OmnibarLayout] is used because it comes pre-embedded with the navigation bar. * - * Note: If top [OmnibarLayout] is used ([OmnibarPosition.TOP]), [TopOmnibarBrowserContainerLayoutBehavior] should be set to the target child. + * Note: If top [OmnibarLayout] is used ([OmnibarType.SINGLE_TOP] or [OmnibarType.SPLIT]), [TopOmnibarBrowserContainerLayoutBehavior] should be set to the target child. */ class BottomOmnibarBrowserContainerLayoutBehavior : Behavior() { override fun layoutDependsOn( @@ -119,4 +119,4 @@ private fun offsetByBottomElementVisibleHeight( private fun View.isBrowserNavigationBar(): Boolean = this is BrowserNavigationBarView -private fun View.isBottomOmnibar(): Boolean = this is OmnibarView && this.omnibarPosition == OmnibarPosition.BOTTOM +private fun View.isBottomOmnibar(): Boolean = this is OmnibarView && this.omnibarType == OmnibarType.SINGLE_BOTTOM diff --git a/app/src/main/java/com/duckduckgo/app/cta/ui/Cta.kt b/app/src/main/java/com/duckduckgo/app/cta/ui/Cta.kt index 632f7578eadc..7e20b9dc6024 100644 --- a/app/src/main/java/com/duckduckgo/app/cta/ui/Cta.kt +++ b/app/src/main/java/com/duckduckgo/app/cta/ui/Cta.kt @@ -53,7 +53,7 @@ import com.duckduckgo.app.settings.db.SettingsDataStore import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.statistics.pixels.Pixel.PixelValues.DAX_FIRE_DIALOG_CTA import com.duckduckgo.app.trackerdetection.model.Entity -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.ui.view.TypeAnimationTextView import com.duckduckgo.common.ui.view.button.DaxButton import com.duckduckgo.common.ui.view.gone @@ -498,7 +498,7 @@ sealed class OnboardingDaxDialogCta( title = getTrackersDescription(context, trackers), description = context .getString(R.string.bbOnboardingTrackersBlockedDialogDescription) - .getStringForOmnibarPosition(settingsDataStore.omnibarPosition), + .getStringForOmnibarPosition(settingsDataStore.omnibarType), primaryCtaText = buttonText?.let { context.getString(it) }, binding = binding, onTypingAnimationFinished = onTypingAnimationFinished, @@ -538,21 +538,21 @@ sealed class OnboardingDaxDialogCta( if (onboardingDesignExperimentManager.isBbEnrolledAndEnabled()) { context.resources .getQuantityString(R.plurals.bbOnboardingTrackersBlockedZeroDialogTitle, trackersFiltered.size) - .getStringForOmnibarPosition(settingsDataStore.omnibarPosition) + .getStringForOmnibarPosition(settingsDataStore.omnibarType) } else { context.resources .getQuantityString(R.plurals.onboardingTrackersBlockedZeroDialogDescription, trackersFiltered.size) - .getStringForOmnibarPosition(settingsDataStore.omnibarPosition) + .getStringForOmnibarPosition(settingsDataStore.omnibarType) } } else { if (onboardingDesignExperimentManager.isBbEnrolledAndEnabled()) { context.resources .getQuantityString(R.plurals.bbOnboardingTrackersBlockedDialogTitle, size, size) - .getStringForOmnibarPosition(settingsDataStore.omnibarPosition) + .getStringForOmnibarPosition(settingsDataStore.omnibarType) } else { context.resources .getQuantityString(R.plurals.onboardingTrackersBlockedDialogDescription, size, size) - .getStringForOmnibarPosition(settingsDataStore.omnibarPosition) + .getStringForOmnibarPosition(settingsDataStore.omnibarType) } } return if (onboardingDesignExperimentManager.isBbEnrolledAndEnabled()) { @@ -1865,10 +1865,10 @@ fun DaxCta.canSendShownPixel(): Boolean { return !(param.isNotEmpty() && param.any { it.split(":").firstOrNull().orEmpty() == ctaPixelParam }) } -fun String.getStringForOmnibarPosition(position: OmnibarPosition): String = +fun String.getStringForOmnibarPosition(position: OmnibarType): String = when (position) { - OmnibarPosition.TOP -> this - OmnibarPosition.BOTTOM -> replace("☝", "\uD83D\uDC47") + OmnibarType.SINGLE_TOP, OmnibarType.SPLIT -> this + OmnibarType.SINGLE_BOTTOM -> replace("☝", "\uD83D\uDC47") } private fun View.fadeIn(duration: Duration = 500.milliseconds): ViewPropertyAnimator = animate().alpha(1f).setDuration(duration.inWholeMilliseconds) diff --git a/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/WelcomePageViewModel.kt b/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/WelcomePageViewModel.kt index 652099cc5371..99ef403cef12 100644 --- a/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/WelcomePageViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/onboarding/ui/page/WelcomePageViewModel.kt @@ -57,7 +57,7 @@ import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.statistics.pixels.Pixel.PixelParameter import com.duckduckgo.app.statistics.pixels.Pixel.PixelType.Unique import com.duckduckgo.appbuildconfig.api.AppBuildConfig -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.FragmentScope import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST @@ -156,12 +156,11 @@ class WelcomePageViewModel @Inject constructor( } ADDRESS_BAR_POSITION -> { - if (!defaultAddressBarPosition) { - settingsDataStore.omnibarPosition = OmnibarPosition.BOTTOM - pixel.fire(PREONBOARDING_BOTTOM_ADDRESS_BAR_SELECTED_UNIQUE) - } viewModelScope.launch { if (!defaultAddressBarPosition) { + settingsDataStore.omnibarType = OmnibarType.SINGLE_BOTTOM + pixel.fire(PREONBOARDING_BOTTOM_ADDRESS_BAR_SELECTED_UNIQUE) + onboardingDesignExperimentManager.fireAddressBarSetBottomPixel() } else { onboardingDesignExperimentManager.fireAddressBarSetTopPixel() diff --git a/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt b/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt index 78d38a2e5ca8..b17070edbafd 100644 --- a/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt +++ b/app/src/main/java/com/duckduckgo/app/pixels/AppPixelName.kt @@ -144,6 +144,7 @@ enum class AppPixelName(override val pixelName: String) : Pixel.PixelName { SETTINGS_ADDRESS_BAR_POSITION_PRESSED("ms_address_bar_position_setting_pressed"), SETTINGS_ADDRESS_BAR_POSITION_SELECTED_TOP("ms_address_bar_position_setting_selected_top"), SETTINGS_ADDRESS_BAR_POSITION_SELECTED_BOTTOM("ms_address_bar_position_setting_selected_bottom"), + SETTINGS_ADDRESS_BAR_POSITION_SELECTED_SPLIT_TOP("ms_address_bar_position_setting_selected_split_top"), SETTINGS_NEXT_STEPS_ADDRESS_BAR("m_settings_next_steps_set_address_bar"), SETTINGS_NEXT_STEPS_VOICE_SEARCH("m_settings_next_steps_enable_voice_search"), SETTINGS_MAC_APP_PRESSED("ms_mac_app_setting_pressed"), diff --git a/app/src/main/java/com/duckduckgo/app/pixels/remoteconfig/AndroidBrowserConfigFeature.kt b/app/src/main/java/com/duckduckgo/app/pixels/remoteconfig/AndroidBrowserConfigFeature.kt index e70d522d821b..6150eee153be 100644 --- a/app/src/main/java/com/duckduckgo/app/pixels/remoteconfig/AndroidBrowserConfigFeature.kt +++ b/app/src/main/java/com/duckduckgo/app/pixels/remoteconfig/AndroidBrowserConfigFeature.kt @@ -192,4 +192,8 @@ interface AndroidBrowserConfigFeature { @Toggle.DefaultValue(TRUE) fun useUnifiedOmnibarLayout(): Toggle + + @Toggle.DefaultValue(FALSE) + @Toggle.InternalAlwaysEnabled + fun splitOmnibar(): Toggle } diff --git a/app/src/main/java/com/duckduckgo/app/settings/db/SettingsDataStore.kt b/app/src/main/java/com/duckduckgo/app/settings/db/SettingsDataStore.kt index e955e8bc275c..53f19d2a6995 100644 --- a/app/src/main/java/com/duckduckgo/app/settings/db/SettingsDataStore.kt +++ b/app/src/main/java/com/duckduckgo/app/settings/db/SettingsDataStore.kt @@ -28,7 +28,7 @@ import com.duckduckgo.app.settings.clear.ClearWhenOption import com.duckduckgo.app.settings.clear.FireAnimation import com.duckduckgo.appbuildconfig.api.AppBuildConfig import com.duckduckgo.browser.api.autocomplete.AutoCompleteSettings -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.di.scopes.AppScope import com.squareup.anvil.annotations.ContributesBinding import dagger.SingleInstanceIn @@ -66,7 +66,10 @@ interface SettingsDataStore { var appLinksEnabled: Boolean var showAppLinksPrompt: Boolean var showAutomaticFireproofDialog: Boolean - var omnibarPosition: OmnibarPosition + var omnibarType: OmnibarType + + // Temporary cache value for split omnibar feature, in case the flag is temporarily disabled + var isSplitOmnibarSelected: Boolean /** * This will be checked upon app startup and used to decide whether it should perform a clear or not. @@ -213,9 +216,15 @@ class SettingsSharedPreferences @Inject constructor( get() = preferences.getBoolean(SHOW_AUTOMATIC_FIREPROOF_DIALOG, true) set(enabled) = preferences.edit { putBoolean(SHOW_AUTOMATIC_FIREPROOF_DIALOG, enabled) } - override var omnibarPosition: OmnibarPosition - get() = OmnibarPosition.valueOf(preferences.getString(KEY_OMNIBAR_POSITION, OmnibarPosition.TOP.name) ?: OmnibarPosition.TOP.name) - set(value) = preferences.edit { putString(KEY_OMNIBAR_POSITION, value.name) } + override var omnibarType: OmnibarType + get() = OmnibarType.fromString( + preferences.getString(KEY_OMNIBAR_TYPE, OmnibarType.SINGLE_TOP.typeName) ?: OmnibarType.SINGLE_TOP.typeName, + ) + set(value) = preferences.edit { putString(KEY_OMNIBAR_TYPE, value.typeName) } + + override var isSplitOmnibarSelected: Boolean + get() = preferences.getBoolean(KEY_SPLIT_OMNIBAR, false) + set(value) = preferences.edit { putBoolean(KEY_SPLIT_OMNIBAR, value) } override var isFullUrlEnabled: Boolean get() = preferences.getBoolean(KEY_IS_FULL_URL_ENABLED, true) @@ -297,7 +306,8 @@ class SettingsSharedPreferences @Inject constructor( const val SHOW_AUTOMATIC_FIREPROOF_DIALOG = "SHOW_AUTOMATIC_FIREPROOF_DIALOG" const val KEY_NOTIFY_ME_IN_DOWNLOADS_DISMISSED = "KEY_NOTIFY_ME_IN_DOWNLOADS_DISMISSED" const val KEY_EXPERIMENTAL_SITE_DARK_MODE = "KEY_EXPERIMENTAL_SITE_DARK_MODE" - const val KEY_OMNIBAR_POSITION = "KEY_OMNIBAR_POSITION" + const val KEY_OMNIBAR_TYPE = "KEY_OMNIBAR_POSITION" + const val KEY_SPLIT_OMNIBAR = "KEY_SPLIT_OMNIBAR" const val KEY_IS_FULL_URL_ENABLED = "KEY_IS_FULL_URL_ENABLED" const val KEY_CLEAR_DUCK_AI_DATA = "KEY_CLEAR_DUCK_AI_DATA" } diff --git a/app/src/main/java/com/duckduckgo/app/systemsearch/SystemSearchActivity.kt b/app/src/main/java/com/duckduckgo/app/systemsearch/SystemSearchActivity.kt index 29dbe7cae570..e2f992e9435e 100644 --- a/app/src/main/java/com/duckduckgo/app/systemsearch/SystemSearchActivity.kt +++ b/app/src/main/java/com/duckduckgo/app/systemsearch/SystemSearchActivity.kt @@ -73,7 +73,6 @@ import com.duckduckgo.app.tabs.ui.GridViewColumnCalculator import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggestion import com.duckduckgo.browser.api.ui.BrowserScreens.PrivateSearchScreenNoParams import com.duckduckgo.browser.ui.autocomplete.BrowserAutoCompleteSuggestionsAdapter -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition import com.duckduckgo.common.ui.DuckDuckGoActivity import com.duckduckgo.common.ui.view.KeyboardAwareEditText import com.duckduckgo.common.ui.view.addBottomShadow @@ -202,10 +201,8 @@ class SystemSearchActivity : DuckDuckGoActivity() { dataClearerForegroundAppRestartPixel.registerIntent(intent) setContentView(binding.root) - val isOmnibarAtTop = settingsDataStore.omnibarPosition == OmnibarPosition.TOP - - configureViewReferences(isOmnibarAtTop) - configureOmnibar(isOmnibarAtTop) + configureViewReferences(viewModel.isOmnibarAtTop) + configureOmnibar(viewModel.isOmnibarAtTop) configureObservers() configureFlowCollectors() configureOnboarding() @@ -219,7 +216,7 @@ class SystemSearchActivity : DuckDuckGoActivity() { if (savedInstanceState == null) { intent?.let { sendLaunchPixels(it) - val inputScreenLaunched = launchInputScreen(isTopOmnibar = isOmnibarAtTop, intent = it) + val inputScreenLaunched = launchInputScreen(isTopOmnibar = viewModel.isOmnibarAtTop, intent = it) if (!inputScreenLaunched) { handleVoiceSearchLaunch(it) } @@ -243,7 +240,7 @@ class SystemSearchActivity : DuckDuckGoActivity() { override fun onResume() { super.onResume() - if (viewModel.hasOmnibarPositionChanged) { + if (viewModel.hasOmnibarTypeChanged) { recreate() } } @@ -254,8 +251,7 @@ class SystemSearchActivity : DuckDuckGoActivity() { viewModel.resetViewState() viewModel.setLaunchedFromSearchOnlyWidget(launchedFromSearchOnlyWidget(intent)) sendLaunchPixels(intent) - val isOmnibarAtTop = settingsDataStore.omnibarPosition == OmnibarPosition.TOP - val inputScreenLaunched = launchInputScreen(isTopOmnibar = isOmnibarAtTop, intent = intent) + val inputScreenLaunched = launchInputScreen(isTopOmnibar = viewModel.isOmnibarAtTop, intent = intent) if (!inputScreenLaunched) { handleVoiceSearchLaunch(intent) } @@ -363,7 +359,7 @@ class SystemSearchActivity : DuckDuckGoActivity() { autoCompleteLongPressClickListener = { viewModel.userLongPressedAutocomplete(it) }, - omnibarPosition = settingsDataStore.omnibarPosition, + omnibarType = settingsDataStore.omnibarType, ) binding.autocompleteSuggestions.adapter = autocompleteSuggestionsAdapter @@ -417,7 +413,7 @@ class SystemSearchActivity : DuckDuckGoActivity() { } else { binding.rootView.removeView(binding.appBarLayout) } - viewModel.onOmnibarConfigured(settingsDataStore.omnibarPosition) + viewModel.onOmnibarConfigured(settingsDataStore.omnibarType) } private fun configureVoiceSearch() { diff --git a/app/src/main/java/com/duckduckgo/app/systemsearch/SystemSearchViewModel.kt b/app/src/main/java/com/duckduckgo/app/systemsearch/SystemSearchViewModel.kt index cd95d6def64a..9d2f58f14649 100644 --- a/app/src/main/java/com/duckduckgo/app/systemsearch/SystemSearchViewModel.kt +++ b/app/src/main/java/com/duckduckgo/app/systemsearch/SystemSearchViewModel.kt @@ -39,7 +39,7 @@ import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggesti import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion.AutoCompleteSwitchToTabSuggestion import com.duckduckgo.browser.api.autocomplete.AutoCompleteFactory import com.duckduckgo.browser.api.autocomplete.AutoCompleteSettings -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.SingleLiveEvent import com.duckduckgo.di.scopes.ActivityScope @@ -183,7 +183,10 @@ class SystemSearchViewModel @Inject constructor( private val hiddenIds = MutableStateFlow(HiddenBookmarksIds()) private var hasUserSeenHistory = false - private var omnibarPosition: OmnibarPosition = appSettingsPreferencesStore.omnibarPosition + private var omnibarType: OmnibarType = appSettingsPreferencesStore.omnibarType + + val isOmnibarAtTop: Boolean + get() = omnibarType == OmnibarType.SINGLE_TOP || omnibarType == OmnibarType.SPLIT private val autoComplete: AutoComplete = autoCompleteFactory.create( AutoComplete.Config(showInstalledApps = true), @@ -273,12 +276,12 @@ class SystemSearchViewModel @Inject constructor( } } - fun onOmnibarConfigured(position: OmnibarPosition) { - omnibarPosition = position + fun onOmnibarConfigured(type: OmnibarType) { + omnibarType = type } - val hasOmnibarPositionChanged: Boolean - get() = omnibarPosition != appSettingsPreferencesStore.omnibarPosition + val hasOmnibarTypeChanged: Boolean + get() = omnibarType != appSettingsPreferencesStore.omnibarType fun userTappedOnboardingToggle() { onboardingViewState.value = currentOnboardingState().copy(expanded = !currentOnboardingState().expanded) diff --git a/app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherActivity.kt b/app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherActivity.kt index 0d211f8fa132..495c7848857f 100644 --- a/app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherActivity.kt +++ b/app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherActivity.kt @@ -77,7 +77,7 @@ import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.Command.ShowUndoDeleteTab import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.SelectionViewState.Mode import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.SelectionViewState.Mode.Selection import com.duckduckgo.appbuildconfig.api.AppBuildConfig -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.ui.DuckDuckGoActivity import com.duckduckgo.common.ui.menu.PopupMenu import com.duckduckgo.common.ui.view.button.ButtonType @@ -211,7 +211,7 @@ class TabSwitcherActivity : private val binding: ActivityTabSwitcherBinding by viewBinding() private val popupMenu by lazy { - if (settingsDataStore.omnibarPosition == OmnibarPosition.BOTTOM && viewModel.isNewToolbarEnabled) { + if (settingsDataStore.omnibarType == OmnibarType.SINGLE_BOTTOM && viewModel.isNewToolbarEnabled) { PopupMenu(layoutInflater, R.layout.popup_tabs_menu_bottom) } else { PopupMenu(layoutInflater, R.layout.popup_tabs_menu) @@ -219,7 +219,7 @@ class TabSwitcherActivity : } private val snackbarAnchorView by lazy { - if (settingsDataStore.omnibarPosition == OmnibarPosition.BOTTOM) { + if (settingsDataStore.omnibarType == OmnibarType.SINGLE_BOTTOM) { toolbar } else { null @@ -277,10 +277,13 @@ class TabSwitcherActivity : tabsRecycler = findViewById(R.id.tabsRecycler) if (viewModel.isNewToolbarEnabled) { - if (settingsDataStore.omnibarPosition == OmnibarPosition.BOTTOM) { - binding.root.removeView(binding.tabSwitcherExperimentToolbarTop.root) - } else { - binding.root.removeView(binding.tabSwitcherToolbarBottom.root) + when (settingsDataStore.omnibarType) { + OmnibarType.SINGLE_TOP, OmnibarType.SPLIT -> { + binding.root.removeView(binding.tabSwitcherToolbarBottom.root) + } + OmnibarType.SINGLE_BOTTOM -> { + binding.root.removeView(binding.tabSwitcherExperimentToolbarTop.root) + } } binding.root.removeView(binding.tabSwitcherToolbarTop.root) } else { @@ -328,10 +331,13 @@ class TabSwitcherActivity : tabsContainer.updateLayoutParams { this as CoordinatorLayout.LayoutParams this.behavior = null - if (settingsDataStore.omnibarPosition == OmnibarPosition.TOP) { - this.topMargin = TABS_CONTENT_PADDING_DP.toPx() - } else { - this.bottomMargin = TABS_CONTENT_PADDING_DP.toPx() + when (settingsDataStore.omnibarType) { + OmnibarType.SINGLE_TOP, OmnibarType.SPLIT -> { + this.topMargin = TABS_CONTENT_PADDING_DP.toPx() + } + OmnibarType.SINGLE_BOTTOM -> { + this.bottomMargin = TABS_CONTENT_PADDING_DP.toPx() + } } } } diff --git a/app/src/test/java/com/duckduckgo/app/Fakes.kt b/app/src/test/java/com/duckduckgo/app/Fakes.kt index 4e350ac7d6fc..9b223464d088 100644 --- a/app/src/test/java/com/duckduckgo/app/Fakes.kt +++ b/app/src/test/java/com/duckduckgo/app/Fakes.kt @@ -24,7 +24,7 @@ import com.duckduckgo.app.settings.clear.ClearWhenOption import com.duckduckgo.app.settings.clear.FireAnimation import com.duckduckgo.app.settings.db.SettingsDataStore import com.duckduckgo.browser.api.autocomplete.AutoCompleteSettings -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType class FakeSettingsDataStore : SettingsDataStore, @@ -162,12 +162,18 @@ class FakeSettingsDataStore : store["showAutomaticFireproofDialog"] = value } - override var omnibarPosition: OmnibarPosition - get() = OmnibarPosition.valueOf(store["omnibarPosition"] as String) + override var omnibarType: OmnibarType + get() = OmnibarType.fromString(store["omnibarPosition"] as String) set(value) { store["omnibarPosition"] = value.name } + override var isSplitOmnibarSelected: Boolean + get() = store["isSplitOmnibarSelected"] as Boolean? ?: false + set(value) { + store["isSplitOmnibarSelected"] = value + } + override var notifyMeInDownloadsDismissed: Boolean get() = store["notifyMeInDownloadsDismissed"] as Boolean? ?: false set(value) { diff --git a/app/src/test/java/com/duckduckgo/app/appearance/AppearanceViewModelTest.kt b/app/src/test/java/com/duckduckgo/app/appearance/AppearanceViewModelTest.kt index 9714a9583368..447fb7713515 100644 --- a/app/src/test/java/com/duckduckgo/app/appearance/AppearanceViewModelTest.kt +++ b/app/src/test/java/com/duckduckgo/app/appearance/AppearanceViewModelTest.kt @@ -27,8 +27,7 @@ import com.duckduckgo.app.settings.clear.FireAnimation import com.duckduckgo.app.settings.db.SettingsDataStore import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.tabs.store.TabSwitcherDataStore -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.BOTTOM -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.TOP +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.common.ui.DuckDuckGoTheme import com.duckduckgo.common.ui.store.AppTheme @@ -80,9 +79,13 @@ internal class AppearanceViewModelTest { whenever(mockAppSettingsDataStore.appIcon).thenReturn(AppIcon.DEFAULT) whenever(mockThemeSettingsDataStore.theme).thenReturn(DuckDuckGoTheme.SYSTEM_DEFAULT) whenever(mockAppSettingsDataStore.selectedFireAnimation).thenReturn(FireAnimation.HeroFire) - whenever(mockAppSettingsDataStore.omnibarPosition).thenReturn(TOP) + whenever(mockAppSettingsDataStore.omnibarType).thenReturn(OmnibarType.SINGLE_TOP) whenever(mockTabSwitcherDataStore.isTrackersAnimationInfoTileHidden()).thenReturn(flowOf(false)) + initializeViewModel() + } + + private fun initializeViewModel() { testee = AppearanceViewModel( mockThemeSettingsDataStore, @@ -118,7 +121,7 @@ internal class AppearanceViewModelTest { testee.commands().test { testee.userRequestedToChangeTheme() - assertEquals(Command.LaunchThemeSettings(DuckDuckGoTheme.LIGHT), awaitItem()) + assertEquals(Command.LaunchThemeSettings(DuckDuckGoTheme.SYSTEM_DEFAULT), awaitItem()) cancelAndIgnoreRemainingEvents() } @@ -140,8 +143,8 @@ internal class AppearanceViewModelTest { @Test fun whenThemeChangedThenDataStoreIsUpdatedAndUpdateThemeCommandIsSent() = runTest { + givenThemeSelected(DuckDuckGoTheme.LIGHT) testee.commands().test { - givenThemeSelected(DuckDuckGoTheme.LIGHT) testee.onThemeSelected(DuckDuckGoTheme.DARK) verify(mockThemeSettingsDataStore).theme = DuckDuckGoTheme.DARK @@ -210,7 +213,7 @@ internal class AppearanceViewModelTest { runTest { testee.commands().test { testee.userRequestedToChangeAddressBarPosition() - assertEquals(Command.LaunchOmnibarPositionSettings(TOP), awaitItem()) + assertEquals(Command.LaunchOmnibarTypeSettings(OmnibarType.SINGLE_TOP), awaitItem()) verify(mockPixel).fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_PRESSED) } } @@ -218,16 +221,16 @@ internal class AppearanceViewModelTest { @Test fun whenOmnibarPositionUpdatedToBottom() = runTest { - testee.onOmnibarPositionUpdated(BOTTOM) - verify(mockAppSettingsDataStore).omnibarPosition = BOTTOM + testee.setOmnibarType(OmnibarType.SINGLE_BOTTOM) + verify(mockAppSettingsDataStore).omnibarType = OmnibarType.SINGLE_BOTTOM verify(mockPixel).fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_SELECTED_BOTTOM) } @Test fun whenOmnibarPositionUpdatedToTop() = runTest { - testee.onOmnibarPositionUpdated(TOP) - verify(mockAppSettingsDataStore).omnibarPosition = TOP + testee.setOmnibarType(OmnibarType.SINGLE_TOP) + verify(mockAppSettingsDataStore).omnibarType = OmnibarType.SINGLE_TOP verify(mockPixel).fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_SELECTED_TOP) } @@ -297,6 +300,8 @@ internal class AppearanceViewModelTest { whenever(mockThemeSettingsDataStore.theme).thenReturn(DuckDuckGoTheme.LIGHT) whenever(mockAppTheme.isLightModeEnabled()).thenReturn(true) + initializeViewModel() + testee.viewState().test { val value = expectMostRecentItem() @@ -311,5 +316,6 @@ internal class AppearanceViewModelTest { private fun givenThemeSelected(theme: DuckDuckGoTheme) { whenever(mockThemeSettingsDataStore.theme).thenReturn(theme) whenever(mockThemeSettingsDataStore.isCurrentlySelected(theme)).thenReturn(true) + initializeViewModel() } } diff --git a/app/src/test/java/com/duckduckgo/app/browser/newaddressbaroption/NewAddressBarOptionManagerTest.kt b/app/src/test/java/com/duckduckgo/app/browser/newaddressbaroption/NewAddressBarOptionManagerTest.kt index f55a02334681..dbbaeac6744c 100644 --- a/app/src/test/java/com/duckduckgo/app/browser/newaddressbaroption/NewAddressBarOptionManagerTest.kt +++ b/app/src/test/java/com/duckduckgo/app/browser/newaddressbaroption/NewAddressBarOptionManagerTest.kt @@ -19,7 +19,7 @@ package com.duckduckgo.app.browser.newaddressbaroption import com.duckduckgo.app.onboarding.store.AppStage import com.duckduckgo.app.onboarding.store.UserStageStore import com.duckduckgo.app.settings.db.SettingsDataStore -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.common.ui.DuckDuckGoActivity import com.duckduckgo.duckchat.api.DuckAiFeatureState @@ -190,7 +190,7 @@ class NewAddressBarOptionManagerTest { fun `when bottom address bar is enabled then showChoiceScreen does not show dialog`() = runTest { setupAllConditionsMet() - whenever(settingsDataStoreMock.omnibarPosition).thenReturn(OmnibarPosition.BOTTOM) + whenever(settingsDataStoreMock.omnibarType).thenReturn(OmnibarType.SINGLE_BOTTOM) testee.showChoiceScreen(mock(), isLaunchedFromExternal = false) @@ -302,7 +302,7 @@ class NewAddressBarOptionManagerTest { showInputScreenFlow.value = false whenever(newAddressBarOptionDataStoreMock.wasShown()).thenReturn(false) whenever(remoteMessagingRepositoryMock.dismissedMessages()).thenReturn(emptyList()) - whenever(settingsDataStoreMock.omnibarPosition).thenReturn(OmnibarPosition.TOP) + whenever(settingsDataStoreMock.omnibarType).thenReturn(OmnibarType.SINGLE_TOP) whenever(newAddressBarOptionDataStoreMock.wasValidated()).thenReturn(true) } } diff --git a/app/src/test/java/com/duckduckgo/app/cta/ui/CtaTest.kt b/app/src/test/java/com/duckduckgo/app/cta/ui/CtaTest.kt index f79c6cc0c810..a9052e6856de 100644 --- a/app/src/test/java/com/duckduckgo/app/cta/ui/CtaTest.kt +++ b/app/src/test/java/com/duckduckgo/app/cta/ui/CtaTest.kt @@ -32,8 +32,7 @@ import com.duckduckgo.app.trackerdetection.model.Entity import com.duckduckgo.app.trackerdetection.model.TrackerStatus import com.duckduckgo.app.trackerdetection.model.TrackerType import com.duckduckgo.app.trackerdetection.model.TrackingEvent -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.BOTTOM -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.TOP +import com.duckduckgo.browser.ui.omnibar.OmnibarType import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -74,7 +73,7 @@ class CtaTest { whenever(mockActivity.resources).thenReturn(mockResources) whenever(mockResources.getQuantityString(any(), any())).thenReturn("withZero") whenever(mockResources.getQuantityString(any(), any(), any())).thenReturn("withMultiple") - whenever(mockSettingsDataStore.omnibarPosition).thenReturn(TOP) + whenever(mockSettingsDataStore.omnibarType).thenReturn(OmnibarType.SINGLE_TOP) } @Test @@ -212,14 +211,14 @@ class CtaTest { @Test fun whenOmnibarPositionIsTopKeepTopPointingEmoji() { val inputString = "
☝️ Tap the shield for more info.️]]" - assertEquals(inputString.getStringForOmnibarPosition(TOP), inputString) + assertEquals(inputString.getStringForOmnibarPosition(OmnibarType.SINGLE_TOP), inputString) } @Test fun whenOmnibarPositionIsBottomUpdateHandEmojiToPointDown() { val inputString = "
☝️ Tap the shield for more info.️]]" val expectedString = "
\uD83D\uDC47️ Tap the shield for more info.️]]" - assertEquals(inputString.getStringForOmnibarPosition(BOTTOM), expectedString) + assertEquals(inputString.getStringForOmnibarPosition(OmnibarType.SINGLE_BOTTOM), expectedString) } @Test diff --git a/app/src/test/java/com/duckduckgo/app/onboarding/ui/page/WelcomePageViewModelTest.kt b/app/src/test/java/com/duckduckgo/app/onboarding/ui/page/WelcomePageViewModelTest.kt index 5b1289eef67e..c047b0a9a3d0 100644 --- a/app/src/test/java/com/duckduckgo/app/onboarding/ui/page/WelcomePageViewModelTest.kt +++ b/app/src/test/java/com/duckduckgo/app/onboarding/ui/page/WelcomePageViewModelTest.kt @@ -47,7 +47,7 @@ import com.duckduckgo.app.statistics.pixels.Pixel import com.duckduckgo.app.statistics.pixels.Pixel.PixelParameter import com.duckduckgo.app.statistics.pixels.Pixel.PixelType.Unique import com.duckduckgo.appbuildconfig.api.AppBuildConfig -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.test.CoroutineTestRule import kotlinx.coroutines.test.runTest import org.junit.Assert @@ -309,7 +309,7 @@ class WelcomePageViewModelTest { testee.onAddressBarPositionOptionSelected(false) testee.onPrimaryCtaClicked(PreOnboardingDialogType.ADDRESS_BAR_POSITION) - verify(mockSettingsDataStore).omnibarPosition = OmnibarPosition.BOTTOM + verify(mockSettingsDataStore).omnibarType = OmnibarType.SINGLE_BOTTOM } @Test diff --git a/browser/browser-ui/src/main/java/com/duckduckgo/browser/ui/autocomplete/BrowserAutoCompleteSuggestionsAdapter.kt b/browser/browser-ui/src/main/java/com/duckduckgo/browser/ui/autocomplete/BrowserAutoCompleteSuggestionsAdapter.kt index 12a88f5084bf..282e54671a83 100644 --- a/browser/browser-ui/src/main/java/com/duckduckgo/browser/ui/autocomplete/BrowserAutoCompleteSuggestionsAdapter.kt +++ b/browser/browser-ui/src/main/java/com/duckduckgo/browser/ui/autocomplete/BrowserAutoCompleteSuggestionsAdapter.kt @@ -32,7 +32,7 @@ import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggesti import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion.AutoCompleteBookmarkSuggestion import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggestion.AutoCompleteUrlSuggestion.AutoCompleteSwitchToTabSuggestion import com.duckduckgo.browser.ui.autocomplete.AutoCompleteViewHolder.EmptySuggestionViewHolder -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType private sealed interface AutoCompleteItem { data class Suggestion( @@ -51,7 +51,7 @@ class BrowserAutoCompleteSuggestionsAdapter( private val autoCompleteInAppMessageDismissedListener: () -> Unit, private val autoCompleteOpenSettingsClickListener: () -> Unit, private val autoCompleteLongPressClickListener: (AutoCompleteSuggestion) -> Unit, - omnibarPosition: OmnibarPosition, + omnibarType: OmnibarType, ) : RecyclerView.Adapter() { private val deleteClickListener: (AutoCompleteSuggestion) -> Unit = { val suggestions = getSuggestions().filter { suggestion -> suggestion != it } @@ -93,12 +93,12 @@ class BrowserAutoCompleteSuggestionsAdapter( private val viewHolderFactoryMap: Map = mapOf( Type.EMPTY_TYPE to EmptySuggestionViewHolderFactory(), - Type.SUGGESTION_TYPE to SearchSuggestionViewHolderFactory(omnibarPosition), + Type.SUGGESTION_TYPE to SearchSuggestionViewHolderFactory(omnibarType), Type.BOOKMARK_TYPE to BookmarkSuggestionViewHolderFactory(), Type.HISTORY_TYPE to HistorySuggestionViewHolderFactory(), Type.HISTORY_SEARCH_TYPE to HistorySearchSuggestionViewHolderFactory(), Type.IN_APP_MESSAGE_TYPE to InAppMessageViewHolderFactory(), - Type.DEFAULT_TYPE to DefaultSuggestionViewHolderFactory(omnibarPosition), + Type.DEFAULT_TYPE to DefaultSuggestionViewHolderFactory(omnibarType), Type.SWITCH_TO_TAB_TYPE to SwitchToTabSuggestionViewHolderFactory(), Type.DIVIDER_TYPE to DividerViewHolderFactory(), Type.DUCK_AI_PROMPT_TYPE to DuckAIPromptSuggestionViewHolderFactory(), diff --git a/browser/browser-ui/src/main/java/com/duckduckgo/browser/ui/autocomplete/SuggestionViewHolderFactory.kt b/browser/browser-ui/src/main/java/com/duckduckgo/browser/ui/autocomplete/SuggestionViewHolderFactory.kt index a41940783b48..07a1766b383a 100644 --- a/browser/browser-ui/src/main/java/com/duckduckgo/browser/ui/autocomplete/SuggestionViewHolderFactory.kt +++ b/browser/browser-ui/src/main/java/com/duckduckgo/browser/ui/autocomplete/SuggestionViewHolderFactory.kt @@ -40,7 +40,7 @@ import com.duckduckgo.browser.ui.databinding.ItemAutocompleteHistorySuggestionBi import com.duckduckgo.browser.ui.databinding.ItemAutocompleteInAppMessageBinding import com.duckduckgo.browser.ui.databinding.ItemAutocompleteSearchSuggestionBinding import com.duckduckgo.browser.ui.databinding.ItemAutocompleteSwitchToTabSuggestionBinding -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.ui.view.MessageCta.Message import com.duckduckgo.mobile.android.R as CommonR @@ -59,7 +59,7 @@ interface SuggestionViewHolderFactory { } class SearchSuggestionViewHolderFactory( - private val omnibarPosition: OmnibarPosition, + private val omnibarType: OmnibarType, ) : SuggestionViewHolderFactory { override fun onCreateViewHolder(parent: ViewGroup): AutoCompleteViewHolder { val inflater = LayoutInflater.from(parent.context) @@ -81,7 +81,7 @@ class SearchSuggestionViewHolderFactory( suggestion as AutoCompleteSearchSuggestion, immediateSearchClickListener, editableSearchClickListener, - omnibarPosition, + omnibarType, ) } } @@ -209,7 +209,7 @@ class EmptySuggestionViewHolderFactory : SuggestionViewHolderFactory { } class DefaultSuggestionViewHolderFactory( - private val omnibarPosition: OmnibarPosition, + private val omnibarType: OmnibarType, ) : SuggestionViewHolderFactory { override fun onCreateViewHolder(parent: ViewGroup): AutoCompleteViewHolder { val inflater = LayoutInflater.from(parent.context) @@ -232,7 +232,7 @@ class DefaultSuggestionViewHolderFactory( longPressClickListener: (AutoCompleteSuggestion) -> Unit, ) { val viewholder = holder as AutoCompleteViewHolder.DefaultSuggestionViewHolder - viewholder.bind(suggestion as AutoCompleteDefaultSuggestion, immediateSearchClickListener, omnibarPosition) + viewholder.bind(suggestion as AutoCompleteDefaultSuggestion, immediateSearchClickListener, omnibarType) } } @@ -334,7 +334,7 @@ sealed class AutoCompleteViewHolder( item: AutoCompleteSearchSuggestion, immediateSearchListener: (AutoCompleteSuggestion) -> Unit, editableSearchClickListener: (AutoCompleteSuggestion) -> Unit, - omnibarPosition: OmnibarPosition, + omnibarType: OmnibarType, ) = with(binding) { phrase.text = item.phrase @@ -345,7 +345,7 @@ sealed class AutoCompleteViewHolder( editQueryImage.setOnClickListener { editableSearchClickListener(item) } root.setOnClickListener { immediateSearchListener(item) } - if (omnibarPosition == OmnibarPosition.BOTTOM) { + if (omnibarType == OmnibarType.SINGLE_BOTTOM) { editQueryImage.setImageResource(R.drawable.ic_arrow_circle_down_left_16) } } @@ -431,12 +431,12 @@ sealed class AutoCompleteViewHolder( fun bind( item: AutoCompleteDefaultSuggestion, immediateSearchListener: (AutoCompleteSuggestion) -> Unit, - omnibarPosition: OmnibarPosition, + omnibarType: OmnibarType, ) { binding.phrase.text = item.phrase binding.root.setOnClickListener { immediateSearchListener(item) } - if (omnibarPosition == OmnibarPosition.BOTTOM) { + if (omnibarType == OmnibarType.SINGLE_BOTTOM) { binding.editQueryImage.setImageResource(R.drawable.ic_arrow_circle_down_left_16) } } diff --git a/browser/browser-ui/src/main/java/com/duckduckgo/browser/ui/omnibar/OmnibarPosition.kt b/browser/browser-ui/src/main/java/com/duckduckgo/browser/ui/omnibar/OmnibarType.kt similarity index 58% rename from browser/browser-ui/src/main/java/com/duckduckgo/browser/ui/omnibar/OmnibarPosition.kt rename to browser/browser-ui/src/main/java/com/duckduckgo/browser/ui/omnibar/OmnibarType.kt index 927dab2c2e4b..fe8aac2e5cb2 100644 --- a/browser/browser-ui/src/main/java/com/duckduckgo/browser/ui/omnibar/OmnibarPosition.kt +++ b/browser/browser-ui/src/main/java/com/duckduckgo/browser/ui/omnibar/OmnibarType.kt @@ -16,7 +16,20 @@ package com.duckduckgo.browser.ui.omnibar -enum class OmnibarPosition { - TOP, - BOTTOM, +enum class OmnibarType(val typeName: String) { + SINGLE_TOP("TOP"), + SINGLE_BOTTOM("BOTTOM"), + SPLIT("SPLIT_TOP"), + ; + + companion object { + fun fromString(name: String): OmnibarType { + return when (name) { + "TOP" -> SINGLE_TOP + "BOTTOM" -> SINGLE_BOTTOM + "SPLIT_TOP" -> SPLIT + else -> throw IllegalStateException("Unknown OmnibarType: $name") + } + } + } } diff --git a/duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/inputscreen/ui/tabs/SearchTabFragment.kt b/duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/inputscreen/ui/tabs/SearchTabFragment.kt index d4916e6b2a48..0ac5c134f14a 100644 --- a/duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/inputscreen/ui/tabs/SearchTabFragment.kt +++ b/duckchat/duckchat-impl/src/main/java/com/duckduckgo/duckchat/impl/inputscreen/ui/tabs/SearchTabFragment.kt @@ -30,7 +30,7 @@ import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggestion import com.duckduckgo.browser.api.ui.BrowserScreens.PrivateSearchScreenNoParams import com.duckduckgo.browser.ui.autocomplete.BrowserAutoCompleteSuggestionsAdapter -import com.duckduckgo.browser.ui.omnibar.OmnibarPosition +import com.duckduckgo.browser.ui.omnibar.OmnibarType import com.duckduckgo.common.ui.DuckDuckGoFragment import com.duckduckgo.common.ui.view.dialog.TextAlertDialogBuilder import com.duckduckgo.common.ui.view.toPx @@ -147,11 +147,11 @@ class SearchTabFragment : DuckDuckGoFragment(R.layout.fragment_search_tab) { autoCompleteLongPressClickListener = { viewModel.userLongPressedAutocomplete(it) }, - omnibarPosition = + omnibarType = if (inputScreenConfigResolver.useTopBar()) { - OmnibarPosition.TOP + OmnibarType.SINGLE_TOP } else { - OmnibarPosition.BOTTOM + OmnibarType.SINGLE_BOTTOM }, ) binding.autoCompleteSuggestionsList.adapter = autoCompleteSuggestionsAdapter