Skip to content

Commit 8ce9307

Browse files
0nkoLukasPaczos
andauthored
Split Omnibar: Data type & storage update (#6991)
Task/Issue URL: https://app.asana.com/1/137249556945/task/1211565479656353?focus=true ### Description This PR updates the storage layer and replaces the `OmnibarPosition` -> `OmnibarType`. New `OmnibarType` enum has the following values: - `SINGLE_TOP` - `SINGLE_BOTTOM` - `SPLIT_TOP` There's also a new `splitOmnibar` feature flag, although it's unused at the moment. ### Steps to test this PR _Change position_ - [x] Go to Settings -> Appearance - [x] Verify the position option shows the correct value - [x] Tap on the omnibar position option - [x] Change the position - [x] Go back to the browser and verify the omnibar position changed _Omnibar behavior_ - [x] Test that the omnibar hiding works on scroll both when position at the top - [x] Test thatthe correct browser menu shown when position at the top (navigation buttons are at the top of menu) - [x] Test that the omnibar hiding works on scroll both when position at the bottom - [x] Test that the correct browser menu shown when position at the bottom (navigation buttons are at the bottom of menu) --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211565479656353 --------- Co-authored-by: Łukasz Paczos <lpaczos@duckduckgo.com>
1 parent 436abfe commit 8ce9307

File tree

32 files changed

+399
-272
lines changed

32 files changed

+399
-272
lines changed

app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,7 @@ import com.duckduckgo.browser.api.autocomplete.AutoComplete.AutoCompleteSuggesti
221221
import com.duckduckgo.browser.api.autocomplete.AutoCompleteSettings
222222
import com.duckduckgo.browser.api.brokensite.BrokenSiteContext
223223
import com.duckduckgo.browser.api.webviewcompat.WebViewCompatWrapper
224-
import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.BOTTOM
225-
import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.TOP
224+
import com.duckduckgo.browser.ui.omnibar.OmnibarType
226225
import com.duckduckgo.common.test.CoroutineTestRule
227226
import com.duckduckgo.common.test.InstantSchedulersRule
228227
import com.duckduckgo.common.ui.tabs.SwipingTabsFeature
@@ -670,7 +669,7 @@ class BrowserTabViewModelTest {
670669
whenever(mockSavedSitesRepository.getBookmarks()).thenReturn(bookmarksListFlow.consumeAsFlow())
671670
whenever(mockRemoteMessagingRepository.messageFlow()).thenReturn(remoteMessageFlow.consumeAsFlow())
672671
whenever(mockSettingsDataStore.automaticFireproofSetting).thenReturn(AutomaticFireproofSetting.ASK_EVERY_TIME)
673-
whenever(mockSettingsDataStore.omnibarPosition).thenReturn(TOP)
672+
whenever(mockSettingsDataStore.omnibarType).thenReturn(OmnibarType.SINGLE_TOP)
674673
whenever(mockSettingsDataStore.isFullUrlEnabled).then { isFullSiteAddressEnabled }
675674
whenever(mockSSLCertificatesFeature.allowBypass()).thenReturn(mockEnabledToggle)
676675
whenever(subscriptions.shouldLaunchPrivacyProForUrl(any())).thenReturn(false)
@@ -5329,7 +5328,7 @@ class BrowserTabViewModelTest {
53295328
@Test
53305329
fun whenRefreshIsTriggeredByUserThenPrivacyProtectionsPopupManagerIsNotifiedWithBottomPosition() =
53315330
runTest {
5332-
whenever(mockSettingsDataStore.omnibarPosition).thenReturn(BOTTOM)
5331+
whenever(mockSettingsDataStore.omnibarType).thenReturn(OmnibarType.SINGLE_BOTTOM)
53335332
testee.onRefreshRequested(triggeredByUser = false)
53345333
verify(mockPrivacyProtectionsPopupManager, never()).onPageRefreshTriggeredByUser(isOmnibarAtTheTop = false)
53355334
testee.onRefreshRequested(triggeredByUser = true)

app/src/main/java/com/duckduckgo/app/appearance/AppearanceActivity.kt

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ import com.duckduckgo.app.appearance.AppearanceScreen.Default
3030
import com.duckduckgo.app.appearance.AppearanceScreen.HighlightedItem
3131
import com.duckduckgo.app.appearance.AppearanceViewModel.Command
3232
import com.duckduckgo.app.appearance.AppearanceViewModel.Command.LaunchAppIcon
33-
import com.duckduckgo.app.appearance.AppearanceViewModel.Command.LaunchOmnibarPositionSettings
33+
import com.duckduckgo.app.appearance.AppearanceViewModel.Command.LaunchOmnibarTypeSettings
3434
import com.duckduckgo.app.appearance.AppearanceViewModel.Command.LaunchThemeSettings
3535
import com.duckduckgo.app.appearance.AppearanceViewModel.Command.UpdateTheme
3636
import com.duckduckgo.app.browser.R
3737
import com.duckduckgo.app.browser.databinding.ActivityAppearanceBinding
3838
import com.duckduckgo.app.fire.FireActivity
39-
import com.duckduckgo.browser.ui.omnibar.OmnibarPosition
39+
import com.duckduckgo.browser.ui.omnibar.OmnibarType
4040
import com.duckduckgo.common.ui.DuckDuckGoActivity
4141
import com.duckduckgo.common.ui.DuckDuckGoTheme
4242
import com.duckduckgo.common.ui.DuckDuckGoTheme.DARK
@@ -127,7 +127,7 @@ class AppearanceActivity : DuckDuckGoActivity() {
127127
binding.experimentalNightMode.quietlySetIsChecked(viewState.forceDarkModeEnabled, forceDarkModeToggleListener)
128128
binding.experimentalNightMode.isEnabled = viewState.canForceDarkMode
129129
binding.experimentalNightMode.isVisible = viewState.supportsForceDarkMode
130-
updateSelectedOmnibarPosition(it.omnibarPosition)
130+
updateSelectedOmnibarPosition(it.omnibarType)
131131
binding.showFullUrlSetting.quietlySetIsChecked(viewState.isFullUrlEnabled, showFullUrlToggleListener)
132132
binding.showTrackersCountInTabSwitcher.quietlySetIsChecked(
133133
viewState.isTrackersCountInTabSwitcherEnabled,
@@ -155,12 +155,13 @@ class AppearanceActivity : DuckDuckGoActivity() {
155155
binding.selectedThemeSetting.setSecondaryText(subtitle)
156156
}
157157

158-
private fun updateSelectedOmnibarPosition(position: OmnibarPosition) {
158+
private fun updateSelectedOmnibarPosition(omnibarType: OmnibarType) {
159159
val subtitle =
160160
getString(
161-
when (position) {
162-
OmnibarPosition.TOP -> R.string.settingsAddressBarPositionTop
163-
OmnibarPosition.BOTTOM -> R.string.settingsAddressBarPositionBottom
161+
when (omnibarType) {
162+
OmnibarType.SINGLE_TOP -> R.string.settingsAddressBarPositionTop
163+
OmnibarType.SINGLE_BOTTOM -> R.string.settingsAddressBarPositionBottom
164+
OmnibarType.SPLIT -> TODO()
164165
},
165166
)
166167
binding.addressBarPositionSetting.setSecondaryText(subtitle)
@@ -171,7 +172,7 @@ class AppearanceActivity : DuckDuckGoActivity() {
171172
is LaunchAppIcon -> launchAppIconChange()
172173
is UpdateTheme -> sendThemeChangedBroadcast()
173174
is LaunchThemeSettings -> launchThemeSelector(it.theme)
174-
is LaunchOmnibarPositionSettings -> launchOmnibarPositionSelector(it.position)
175+
is LaunchOmnibarTypeSettings -> launchOmnibarPositionSelector(it.omnibarType)
175176
}
176177
}
177178

@@ -207,23 +208,23 @@ class AppearanceActivity : DuckDuckGoActivity() {
207208
).show()
208209
}
209210

210-
private fun launchOmnibarPositionSelector(position: OmnibarPosition) {
211+
private fun launchOmnibarPositionSelector(type: OmnibarType) {
211212
RadioListAlertDialogBuilder(this)
212213
.setTitle(R.string.settingsAddressBarPositionTitle)
213214
.setOptions(
214215
listOf(
215216
R.string.settingsAddressBarPositionTop,
216217
R.string.settingsAddressBarPositionBottom,
217218
),
218-
OmnibarPosition.entries.indexOf(position) + 1,
219+
OmnibarType.entries.indexOf(type) + 1,
219220
).setPositiveButton(com.duckduckgo.mobile.android.R.string.dialogSave)
220221
.setNegativeButton(R.string.cancel)
221222
.setCancelable(true)
222223
.addEventListener(
223224
object : RadioListAlertDialogBuilder.EventListener() {
224225
override fun onPositiveButtonClicked(selectedItem: Int) {
225-
val newPosition = OmnibarPosition.entries[selectedItem - 1]
226-
viewModel.onOmnibarPositionUpdated(newPosition)
226+
val newType = OmnibarType.entries[selectedItem - 1]
227+
viewModel.setOmnibarType(newType)
227228
}
228229
},
229230
).show()

app/src/main/java/com/duckduckgo/app/appearance/AppearanceViewModel.kt

Lines changed: 39 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,19 @@ import com.duckduckgo.app.pixels.AppPixelName.SETTINGS_THEME_TOGGLED_SYSTEM_DEFA
2828
import com.duckduckgo.app.settings.db.SettingsDataStore
2929
import com.duckduckgo.app.statistics.pixels.Pixel
3030
import com.duckduckgo.app.tabs.store.TabSwitcherDataStore
31-
import com.duckduckgo.browser.ui.omnibar.OmnibarPosition
31+
import com.duckduckgo.browser.ui.omnibar.OmnibarType
3232
import com.duckduckgo.common.ui.DuckDuckGoTheme
33-
import com.duckduckgo.common.ui.DuckDuckGoTheme.DARK
34-
import com.duckduckgo.common.ui.DuckDuckGoTheme.LIGHT
35-
import com.duckduckgo.common.ui.DuckDuckGoTheme.SYSTEM_DEFAULT
3633
import com.duckduckgo.common.ui.store.ThemingDataStore
3734
import com.duckduckgo.common.utils.DispatcherProvider
3835
import com.duckduckgo.di.scopes.ActivityScope
3936
import kotlinx.coroutines.channels.BufferOverflow
4037
import kotlinx.coroutines.channels.Channel
4138
import kotlinx.coroutines.flow.Flow
4239
import kotlinx.coroutines.flow.MutableStateFlow
43-
import kotlinx.coroutines.flow.firstOrNull
44-
import kotlinx.coroutines.flow.onStart
40+
import kotlinx.coroutines.flow.SharingStarted
41+
import kotlinx.coroutines.flow.combine
4542
import kotlinx.coroutines.flow.receiveAsFlow
43+
import kotlinx.coroutines.flow.stateIn
4644
import kotlinx.coroutines.flow.update
4745
import kotlinx.coroutines.launch
4846
import kotlinx.coroutines.withContext
@@ -64,7 +62,7 @@ class AppearanceViewModel @Inject constructor(
6462
val forceDarkModeEnabled: Boolean = false,
6563
val canForceDarkMode: Boolean = false,
6664
val supportsForceDarkMode: Boolean = true,
67-
val omnibarPosition: OmnibarPosition = OmnibarPosition.TOP,
65+
val omnibarType: OmnibarType = OmnibarType.SINGLE_TOP,
6866
val isFullUrlEnabled: Boolean = true,
6967
val isTrackersCountInTabSwitcherEnabled: Boolean = true,
7068
)
@@ -78,32 +76,33 @@ class AppearanceViewModel @Inject constructor(
7876

7977
data object UpdateTheme : Command()
8078

81-
data class LaunchOmnibarPositionSettings(
82-
val position: OmnibarPosition,
79+
data class LaunchOmnibarTypeSettings(
80+
val omnibarType: OmnibarType,
8381
) : Command()
8482
}
8583

86-
private val viewState = MutableStateFlow(ViewState())
87-
private val command = Channel<Command>(1, BufferOverflow.DROP_OLDEST)
84+
private val viewState = MutableStateFlow(
85+
ViewState(
86+
theme = themingDataStore.theme,
87+
appIcon = settingsDataStore.appIcon,
88+
forceDarkModeEnabled = settingsDataStore.experimentalWebsiteDarkMode,
89+
canForceDarkMode = canForceDarkMode(),
90+
supportsForceDarkMode = WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING),
91+
isFullUrlEnabled = settingsDataStore.isFullUrlEnabled,
92+
omnibarType = settingsDataStore.omnibarType,
93+
),
94+
)
8895

89-
fun viewState(): Flow<ViewState> =
90-
viewState.onStart {
91-
viewModelScope.launch {
92-
viewState.update {
93-
currentViewState().copy(
94-
theme = themingDataStore.theme,
95-
appIcon = settingsDataStore.appIcon,
96-
forceDarkModeEnabled = settingsDataStore.experimentalWebsiteDarkMode,
97-
canForceDarkMode = canForceDarkMode(),
98-
supportsForceDarkMode = WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING),
99-
omnibarPosition = settingsDataStore.omnibarPosition,
100-
isFullUrlEnabled = settingsDataStore.isFullUrlEnabled,
101-
isTrackersCountInTabSwitcherEnabled = tabSwitcherDataStore.isTrackersAnimationInfoTileHidden().firstOrNull() != true,
102-
)
103-
}
104-
}
105-
}
96+
fun viewState() = combine(
97+
viewState,
98+
tabSwitcherDataStore.isTrackersAnimationInfoTileHidden(),
99+
) { currentViewState, isTrackersAnimationTileHidden ->
100+
currentViewState.copy(
101+
isTrackersCountInTabSwitcherEnabled = !isTrackersAnimationTileHidden,
102+
)
103+
}.stateIn(viewModelScope, SharingStarted.Lazily, viewState.value)
106104

105+
private val command = Channel<Command>(1, BufferOverflow.DROP_OLDEST)
107106
fun commands(): Flow<Command> = command.receiveAsFlow()
108107

109108
private fun canForceDarkMode(): Boolean = themingDataStore.theme != DuckDuckGoTheme.LIGHT
@@ -119,7 +118,7 @@ class AppearanceViewModel @Inject constructor(
119118
}
120119

121120
fun userRequestedToChangeAddressBarPosition() {
122-
viewModelScope.launch { command.send(Command.LaunchOmnibarPositionSettings(viewState.value.omnibarPosition)) }
121+
viewModelScope.launch { command.send(Command.LaunchOmnibarTypeSettings(viewState.value.omnibarType)) }
123122
pixel.fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_PRESSED)
124123
}
125124

@@ -132,34 +131,33 @@ class AppearanceViewModel @Inject constructor(
132131
viewModelScope.launch(dispatcherProvider.io()) {
133132
themingDataStore.theme = selectedTheme
134133
withContext(dispatcherProvider.main()) {
135-
viewState.update { currentViewState().copy(theme = selectedTheme, forceDarkModeEnabled = canForceDarkMode()) }
134+
viewState.update { it.copy(theme = selectedTheme, forceDarkModeEnabled = canForceDarkMode()) }
136135
command.send(Command.UpdateTheme)
137136
}
138137
}
139138

140139
val pixelName =
141140
when (selectedTheme) {
142-
LIGHT -> SETTINGS_THEME_TOGGLED_LIGHT
143-
DARK -> SETTINGS_THEME_TOGGLED_DARK
144-
SYSTEM_DEFAULT -> SETTINGS_THEME_TOGGLED_SYSTEM_DEFAULT
141+
DuckDuckGoTheme.LIGHT -> SETTINGS_THEME_TOGGLED_LIGHT
142+
DuckDuckGoTheme.DARK -> SETTINGS_THEME_TOGGLED_DARK
143+
DuckDuckGoTheme.SYSTEM_DEFAULT -> SETTINGS_THEME_TOGGLED_SYSTEM_DEFAULT
145144
}
146145
pixel.fire(pixelName)
147146
}
148147

149-
fun onOmnibarPositionUpdated(position: OmnibarPosition) {
148+
fun setOmnibarType(type: OmnibarType) {
150149
viewModelScope.launch(dispatcherProvider.io()) {
151-
settingsDataStore.omnibarPosition = position
152-
viewState.update { currentViewState().copy(omnibarPosition = position) }
150+
settingsDataStore.omnibarType = type
151+
viewState.update { it.copy(omnibarType = type) }
153152

154-
when (position) {
155-
OmnibarPosition.TOP -> pixel.fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_SELECTED_TOP)
156-
OmnibarPosition.BOTTOM -> pixel.fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_SELECTED_BOTTOM)
153+
when (type) {
154+
OmnibarType.SINGLE_TOP -> pixel.fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_SELECTED_TOP)
155+
OmnibarType.SINGLE_BOTTOM -> pixel.fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_SELECTED_BOTTOM)
156+
OmnibarType.SPLIT -> pixel.fire(AppPixelName.SETTINGS_ADDRESS_BAR_POSITION_SELECTED_SPLIT_TOP)
157157
}
158158
}
159159
}
160160

161-
private fun currentViewState(): ViewState = viewState.value
162-
163161
fun onForceDarkModeSettingChanged(checked: Boolean) {
164162
viewModelScope.launch(dispatcherProvider.io()) {
165163
if (checked) {

app/src/main/java/com/duckduckgo/app/browser/BrowserActivity.kt

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,7 @@ import com.duckduckgo.appbuildconfig.api.AppBuildConfig
9494
import com.duckduckgo.autofill.api.emailprotection.EmailProtectionLinkVerifier
9595
import com.duckduckgo.browser.api.ui.BrowserScreens.BookmarksScreenNoParams
9696
import com.duckduckgo.browser.api.ui.BrowserScreens.SettingsScreenNoParams
97-
import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.BOTTOM
98-
import com.duckduckgo.browser.ui.omnibar.OmnibarPosition.TOP
97+
import com.duckduckgo.browser.ui.omnibar.OmnibarType
9998
import com.duckduckgo.common.ui.DuckDuckGoActivity
10099
import com.duckduckgo.common.ui.tabs.SwipingTabsFeatureProvider
101100
import com.duckduckgo.common.ui.view.addBottomShadow
@@ -765,9 +764,9 @@ open class BrowserActivity : DuckDuckGoActivity() {
765764
delay(500)
766765

767766
val anchorView =
768-
when (settingsDataStore.omnibarPosition) {
769-
TOP -> null
770-
BOTTOM -> currentTab?.getOmnibar()?.omnibarView?.toolbar ?: binding.fragmentContainer
767+
when (settingsDataStore.omnibarType) {
768+
OmnibarType.SINGLE_TOP, OmnibarType.SPLIT -> null
769+
OmnibarType.SINGLE_BOTTOM -> currentTab?.getOmnibar()?.omnibarView?.toolbar ?: binding.fragmentContainer
771770
}
772771
DefaultSnackbar(
773772
parentView = binding.fragmentContainer,
@@ -1397,8 +1396,8 @@ open class BrowserActivity : DuckDuckGoActivity() {
13971396
}
13981397

13991398
private fun bindMockupToolbars() {
1400-
when (settingsDataStore.omnibarPosition) {
1401-
TOP -> {
1399+
when (settingsDataStore.omnibarType) {
1400+
OmnibarType.SINGLE_TOP, OmnibarType.SPLIT -> {
14021401
if (Build.VERSION.SDK_INT < 28) {
14031402
binding.topMockupSingleToolbar.mockOmniBarContainerShadow.cardElevation = 2f.toPx(this)
14041403
}
@@ -1415,7 +1414,7 @@ open class BrowserActivity : DuckDuckGoActivity() {
14151414
}
14161415
}
14171416

1418-
BOTTOM -> {
1417+
OmnibarType.SINGLE_BOTTOM -> {
14191418
if (Build.VERSION.SDK_INT < 28) {
14201419
binding.bottomMockupSingleToolbar.mockOmniBarContainerShadow.cardElevation = 0.5f.toPx(this)
14211420
}

0 commit comments

Comments
 (0)