Skip to content

Commit 7148198

Browse files
authored
Split Omnibar: Tab switcher bottom bar (#7056)
Task/Issue URL: https://app.asana.com/1/137249556945/project/1207418217763355/task/1211586528616254?focus=true ### Description This PR adds the bottom navigation bar to the tab switcher. ### Steps to test this PR - [x] Make sure the `splitOmnibar` feature flag is enabled in the internal settings - [x] Go to Settings -> Appearance and enable the split omnibar - [x] Go back to the browser - [x] Open the tab switcher - [x] Notice the bottom navigation bar is displayed - [x] Verify the duck.ai button works as expected - [x] Verify the New tab button works as expected - [x] Verify the Fire button works as expected - [x] Verify the Menu button shows the menu (with items reversed, like for the bottom omnibar) - [x] Tap on the Select tabs menu item - [x] Verify the bottom navigation bar is hidden - [x] Verify the menu button is moved to the top toolbar - [x] Try selecting tabs and verify it works as expected - [x] Tap on the X button - [x] Verify the bottom navigation bar is restored
1 parent d91ffc3 commit 7148198

File tree

13 files changed

+437
-137
lines changed

13 files changed

+437
-137
lines changed

android-design-system/design-system/src/main/java/com/duckduckgo/common/ui/menu/PopupMenu.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ open class PopupMenu(
142142
val y = if (anchorMoreToTopOfScreen) {
143143
anchorLocation[1] + anchorView.height + 4.toDp()
144144
} else {
145-
screenHeight - anchorLocation[1] + 4.toDp()
145+
screenHeight - anchorLocation[1] - anchorView.height + 4.toDp()
146146
}
147147

148148
showAtLocation(rootView, gravity, x, y)

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ class AppearanceActivity : DuckDuckGoActivity() {
200200
val subtitle =
201201
getString(
202202
when (omnibarType) {
203-
OmnibarType.SINGLE_TOP, OmnibarType.SPLIT -> R.string.settingsAddressBarPositionTop
203+
OmnibarType.SPLIT, OmnibarType.SINGLE_TOP -> R.string.settingsAddressBarPositionTop
204204
OmnibarType.SINGLE_BOTTOM -> R.string.settingsAddressBarPositionBottom
205205
},
206206
)
@@ -238,9 +238,9 @@ class AppearanceActivity : DuckDuckGoActivity() {
238238
override fun onPositiveButtonClicked(selectedItem: Int) {
239239
val selectedTheme =
240240
when (selectedItem) {
241-
2 -> DuckDuckGoTheme.LIGHT
242-
3 -> DuckDuckGoTheme.DARK
243-
else -> DuckDuckGoTheme.SYSTEM_DEFAULT
241+
2 -> LIGHT
242+
3 -> DARK
243+
else -> SYSTEM_DEFAULT
244244
}
245245
viewModel.onThemeSelected(selectedTheme)
246246
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,8 +774,9 @@ open class BrowserActivity : DuckDuckGoActivity() {
774774

775775
val anchorView =
776776
when (settingsDataStore.omnibarType) {
777-
OmnibarType.SINGLE_TOP, OmnibarType.SPLIT -> null
777+
OmnibarType.SINGLE_TOP -> null
778778
OmnibarType.SINGLE_BOTTOM -> currentTab?.getOmnibar()?.omnibarView?.toolbar ?: binding.fragmentContainer
779+
OmnibarType.SPLIT -> currentTab?.getBottomNavigationBar() ?: binding.fragmentContainer
779780
}
780781
DefaultSnackbar(
781782
parentView = binding.fragmentContainer,

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

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ import com.duckduckgo.app.browser.model.BasicAuthenticationRequest
142142
import com.duckduckgo.app.browser.model.LongPressTarget
143143
import com.duckduckgo.app.browser.navigation.bar.BrowserNavigationBarViewIntegration
144144
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarObserver
145+
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarView
145146
import com.duckduckgo.app.browser.newtab.NewTabPageProvider
146147
import com.duckduckgo.app.browser.omnibar.Omnibar
147148
import com.duckduckgo.app.browser.omnibar.Omnibar.FindInPageListener
@@ -1187,18 +1188,6 @@ class BrowserTabFragment :
11871188
onBrowserMenuButtonPressed()
11881189
}
11891190

1190-
override fun onBackButtonClicked() {
1191-
onBackArrowClicked()
1192-
}
1193-
1194-
override fun onBackButtonLongClicked() {
1195-
onBackArrowLongClicked()
1196-
}
1197-
1198-
override fun onForwardButtonClicked() {
1199-
onForwardArrowClicked()
1200-
}
1201-
12021191
override fun onNewTabButtonClicked() {
12031192
viewModel.onNavigationBarNewTabButtonClicked()
12041193
}
@@ -2009,6 +1998,10 @@ class BrowserTabFragment :
20091998
}
20101999
}
20112000

2001+
fun getBottomNavigationBar(): BrowserNavigationBarView {
2002+
return binding.navigationBar
2003+
}
2004+
20122005
private fun processCommand(it: Command?) {
20132006
if (it is NavigationCommand) {
20142007
omnibar.cancelTrackersAnimation()

app/src/main/java/com/duckduckgo/app/browser/navigation/bar/view/BrowserNavigationBarObserver.kt

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,6 @@ interface BrowserNavigationBarObserver {
3535
fun onMenuButtonClicked() {
3636
}
3737

38-
@EmptySuper
39-
fun onBackButtonClicked() {
40-
}
41-
42-
@EmptySuper
43-
fun onBackButtonLongClicked() {
44-
}
45-
46-
@EmptySuper
47-
fun onForwardButtonClicked() {
48-
}
49-
5038
@EmptySuper
5139
fun onNewTabButtonClicked() {
5240
}

app/src/main/java/com/duckduckgo/app/browser/navigation/bar/view/BrowserNavigationBarView.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,8 @@ import com.duckduckgo.app.browser.PulseAnimation
3636
import com.duckduckgo.app.browser.databinding.ViewBrowserNavigationBarBinding
3737
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarViewModel.Command
3838
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarViewModel.Command.NotifyAutofillButtonClicked
39-
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarViewModel.Command.NotifyBackButtonClicked
40-
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarViewModel.Command.NotifyBackButtonLongClicked
4139
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarViewModel.Command.NotifyBookmarksButtonClicked
4240
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarViewModel.Command.NotifyFireButtonClicked
43-
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarViewModel.Command.NotifyForwardButtonClicked
4441
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarViewModel.Command.NotifyMenuButtonClicked
4542
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarViewModel.Command.NotifyNewTabButtonClicked
4643
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarViewModel.Command.NotifyTabsButtonClicked
@@ -208,9 +205,6 @@ class BrowserNavigationBarView @JvmOverloads constructor(
208205
NotifyTabsButtonClicked -> browserNavigationBarObserver?.onTabsButtonClicked()
209206
NotifyTabsButtonLongClicked -> browserNavigationBarObserver?.onTabsButtonLongClicked()
210207
NotifyMenuButtonClicked -> browserNavigationBarObserver?.onMenuButtonClicked()
211-
NotifyBackButtonClicked -> browserNavigationBarObserver?.onBackButtonClicked()
212-
NotifyBackButtonLongClicked -> browserNavigationBarObserver?.onBackButtonLongClicked()
213-
NotifyForwardButtonClicked -> browserNavigationBarObserver?.onForwardButtonClicked()
214208
NotifyBookmarksButtonClicked -> browserNavigationBarObserver?.onBookmarksButtonClicked()
215209
NotifyNewTabButtonClicked -> browserNavigationBarObserver?.onNewTabButtonClicked()
216210
NotifyAutofillButtonClicked -> browserNavigationBarObserver?.onAutofillButtonClicked()
@@ -232,12 +226,13 @@ class BrowserNavigationBarView @JvmOverloads constructor(
232226
enum class ViewMode {
233227
NewTab,
234228
Browser,
229+
TabManager,
235230
}
236231

237232
/**
238233
* Behavior that offsets the navigation bar proportionally to the offset of the top omnibar.
239234
*/
240-
private class BottomViewBehavior(
235+
inner class BottomViewBehavior(
241236
context: Context,
242237
attrs: AttributeSet?,
243238
) : Behavior<View>(context, attrs) {

app/src/main/java/com/duckduckgo/app/browser/navigation/bar/view/BrowserNavigationBarViewModel.kt

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,6 @@ class BrowserNavigationBarViewModel @Inject constructor(
122122
it.copy(
123123
newTabButtonVisible = false,
124124
autofillButtonVisible = true,
125-
fireButtonVisible = true,
126-
tabsButtonVisible = true,
127125
)
128126
}
129127
}
@@ -133,8 +131,17 @@ class BrowserNavigationBarViewModel @Inject constructor(
133131
it.copy(
134132
newTabButtonVisible = true,
135133
autofillButtonVisible = false,
136-
fireButtonVisible = true,
137-
tabsButtonVisible = true,
134+
)
135+
}
136+
}
137+
138+
ViewMode.TabManager -> {
139+
_viewState.update {
140+
it.copy(
141+
newTabButtonVisible = true,
142+
autofillButtonVisible = false,
143+
tabsButtonVisible = false,
144+
bookmarksButtonVisible = false,
138145
)
139146
}
140147
}
@@ -154,9 +161,6 @@ class BrowserNavigationBarViewModel @Inject constructor(
154161
data object NotifyTabsButtonClicked : Command()
155162
data object NotifyTabsButtonLongClicked : Command()
156163
data object NotifyMenuButtonClicked : Command()
157-
data object NotifyBackButtonClicked : Command()
158-
data object NotifyBackButtonLongClicked : Command()
159-
data object NotifyForwardButtonClicked : Command()
160164
data object NotifyNewTabButtonClicked : Command()
161165
data object NotifyAutofillButtonClicked : Command()
162166
data object NotifyBookmarksButtonClicked : Command()

app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherActivity.kt

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ import com.duckduckgo.app.browser.R
4242
import com.duckduckgo.app.browser.databinding.ActivityTabSwitcherBinding
4343
import com.duckduckgo.app.browser.databinding.PopupTabsMenuBinding
4444
import com.duckduckgo.app.browser.favicon.FaviconManager
45+
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarObserver
46+
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarView
4547
import com.duckduckgo.app.browser.tabpreview.WebViewPreviewPersister
4648
import com.duckduckgo.app.di.AppCoroutineScope
4749
import com.duckduckgo.app.downloads.DownloadsActivity
@@ -50,7 +52,6 @@ import com.duckduckgo.app.global.events.db.UserEventsStore
5052
import com.duckduckgo.app.global.view.ClearDataAction
5153
import com.duckduckgo.app.global.view.FireDialog
5254
import com.duckduckgo.app.onboardingdesignexperiment.OnboardingDesignExperimentManager
53-
import com.duckduckgo.app.pixels.AppPixelName
5455
import com.duckduckgo.app.settings.SettingsActivity
5556
import com.duckduckgo.app.settings.clear.OnboardingExperimentFireAnimationHelper
5657
import com.duckduckgo.app.settings.db.SettingsDataStore
@@ -72,8 +73,8 @@ import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.Command.ShareLinks
7273
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.Command.ShowAnimatedTileDismissalDialog
7374
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.Command.ShowUndoBookmarkMessage
7475
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.Command.ShowUndoDeleteTabsMessage
75-
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.SelectionViewState.Mode
76-
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.SelectionViewState.Mode.Selection
76+
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.ViewState.Mode
77+
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.ViewState.Mode.Selection
7778
import com.duckduckgo.appbuildconfig.api.AppBuildConfig
7879
import com.duckduckgo.browser.ui.omnibar.OmnibarType
7980
import com.duckduckgo.common.ui.DuckDuckGoActivity
@@ -224,8 +225,7 @@ class TabSwitcherActivity :
224225
null
225226
}
226227
OmnibarType.SPLIT -> {
227-
null
228-
// TODO: add bottom bar
228+
binding.navigationBar
229229
}
230230
}
231231
}
@@ -244,13 +244,32 @@ class TabSwitcherActivity :
244244
configureViewReferences()
245245
setupToolbar(toolbar)
246246
configureRecycler()
247+
configureNavigationBar()
247248

248249
configureObservers()
249250
configureOnBackPressedListener()
250251

251252
initMenuClickListeners()
252253
}
253254

255+
private fun configureNavigationBar() {
256+
binding.navigationBar.browserNavigationBarObserver =
257+
object : BrowserNavigationBarObserver {
258+
override fun onMenuButtonClicked() {
259+
showPopupMenu(binding.navigationBar.popupMenuAnchor.id)
260+
}
261+
262+
override fun onNewTabButtonClicked() {
263+
viewModel.onNewTabRequested()
264+
}
265+
266+
override fun onFireButtonClicked() {
267+
viewModel.onFireButtonTapped()
268+
}
269+
}
270+
binding.navigationBar.setViewMode(BrowserNavigationBarView.ViewMode.TabManager)
271+
}
272+
254273
override fun onSaveInstanceState(outState: Bundle) {
255274
super.onSaveInstanceState(outState)
256275

@@ -267,11 +286,9 @@ class TabSwitcherActivity :
267286
when (settingsDataStore.omnibarType) {
268287
OmnibarType.SINGLE_TOP -> {
269288
binding.root.removeView(binding.tabSwitcherToolbarBottom.root)
270-
// TODO: remove bottom bar
271289
}
272290
OmnibarType.SINGLE_BOTTOM -> {
273291
binding.root.removeView(binding.tabSwitcherToolbarTop.root)
274-
// TODO: remove bottom bar
275292
}
276293
OmnibarType.SPLIT -> {
277294
binding.root.removeView(binding.tabSwitcherToolbarBottom.root)
@@ -392,7 +409,7 @@ class TabSwitcherActivity :
392409

393410
private fun configureObservers() {
394411
lifecycleScope.launch {
395-
viewModel.selectionViewState.flowWithLifecycle(lifecycle).collectLatest {
412+
viewModel.viewState.flowWithLifecycle(lifecycle).collectLatest {
396413
tabsRecycler.invalidateItemDecorations()
397414
tabsAdapter.updateData(it.tabSwitcherItems)
398415

@@ -546,6 +563,7 @@ class TabSwitcherActivity :
546563
is ShowUndoDeleteTabsMessage -> showTabsDeletedSnackbar(command.tabIds)
547564
ShowAnimatedTileDismissalDialog -> showAnimatedTileDismissalDialog()
548565
DismissAnimatedTileDismissalDialog -> tabSwitcherAnimationTileRemovalDialog!!.dismiss()
566+
Command.ShowFireBottomSheet -> onFireButtonClicked()
549567
}
550568
}
551569

@@ -566,14 +584,15 @@ class TabSwitcherActivity :
566584
menuInflater.inflate(R.menu.menu_tab_switcher_activity, menu)
567585

568586
val popupBinding = PopupTabsMenuBinding.bind(popupMenu.contentView)
569-
val viewState = viewModel.selectionViewState.value
587+
val viewState = viewModel.viewState.value
570588

571-
val numSelectedTabs = viewModel.selectionViewState.value.numSelectedTabs
589+
val numSelectedTabs = viewModel.viewState.value.numSelectedTabs
572590
menu.createDynamicInterface(
573591
numSelectedTabs = numSelectedTabs,
574592
popupMenu = popupBinding,
575593
toolbar = toolbar,
576594
dynamicMenu = viewState.dynamicInterface,
595+
navigationBar = binding.navigationBar,
577596
)
578597

579598
return true
@@ -598,7 +617,7 @@ class TabSwitcherActivity :
598617

599618
override fun onOptionsItemSelected(item: MenuItem): Boolean {
600619
when (item.itemId) {
601-
R.id.fireToolbarButton -> onFireButtonClicked()
620+
R.id.fireToolbarButton -> viewModel.onFireButtonTapped()
602621
R.id.popupMenuToolbarButton -> showPopupMenu(item.itemId)
603622
R.id.newTabToolbarButton -> onNewTabRequested(fromOverflowMenu = false)
604623
R.id.duckAIToolbarButton -> viewModel.onDuckAIButtonClicked()
@@ -627,7 +646,6 @@ class TabSwitcherActivity :
627646
}
628647

629648
private fun onFireButtonClicked() {
630-
pixel.fire(AppPixelName.FORGET_ALL_PRESSED_TABSWITCHING)
631649
val dialog =
632650
FireDialog(
633651
context = this,
@@ -930,7 +948,5 @@ class TabSwitcherActivity :
930948
private const val TAB_GRID_COLUMN_WIDTH_DP = 180
931949
private const val TAB_GRID_MAX_COLUMN_COUNT = 4
932950
private const val KEY_FIRST_TIME_LOADING = "FIRST_TIME_LOADING"
933-
private const val FAB_SCROLL_THRESHOLD = 7
934-
private const val TABS_CONTENT_PADDING_DP = 56
935951
}
936952
}

app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherMenuExt.kt

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,21 @@ import androidx.appcompat.widget.Toolbar
2222
import androidx.core.view.isVisible
2323
import com.duckduckgo.app.browser.R
2424
import com.duckduckgo.app.browser.databinding.PopupTabsMenuBinding
25-
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.SelectionViewState.BackButtonType.ARROW
26-
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.SelectionViewState.BackButtonType.CLOSE
27-
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.SelectionViewState.DynamicInterface
28-
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.SelectionViewState.LayoutMode.GRID
29-
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.SelectionViewState.LayoutMode.HIDDEN
30-
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.SelectionViewState.LayoutMode.LIST
25+
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarView
26+
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.ViewState.BackButtonType.ARROW
27+
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.ViewState.BackButtonType.CLOSE
28+
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.ViewState.DynamicInterface
29+
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.ViewState.LayoutMode.GRID
30+
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.ViewState.LayoutMode.HIDDEN
31+
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.ViewState.LayoutMode.LIST
3132
import com.duckduckgo.mobile.android.R as commonR
3233

3334
fun Menu.createDynamicInterface(
3435
numSelectedTabs: Int,
3536
popupMenu: PopupTabsMenuBinding,
3637
toolbar: Toolbar,
3738
dynamicMenu: DynamicInterface,
39+
navigationBar: BrowserNavigationBarView,
3840
) {
3941
popupMenu.selectAllMenuItem.isVisible = dynamicMenu.isSelectAllVisible
4042
popupMenu.deselectAllMenuItem.isVisible = dynamicMenu.isDeselectAllVisible
@@ -94,4 +96,7 @@ fun Menu.createDynamicInterface(
9496
findItem(R.id.fireToolbarButton).isVisible = dynamicMenu.isFireButtonVisible
9597
findItem(R.id.duckAIToolbarButton).isVisible = dynamicMenu.isDuckAIButtonVisible
9698
findItem(R.id.newTabToolbarButton).isVisible = dynamicMenu.isNewTabButtonVisible
99+
findItem(R.id.popupMenuToolbarButton).isVisible = dynamicMenu.isMenuButtonVisible
100+
101+
navigationBar.isVisible = dynamicMenu.isBottomBarVisible
97102
}

0 commit comments

Comments
 (0)