Skip to content

Commit

Permalink
Use a custom popup menu for the selection
Browse files Browse the repository at this point in the history
  • Loading branch information
0nko committed Feb 14, 2025
1 parent f4cc139 commit f607998
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 206 deletions.
140 changes: 91 additions & 49 deletions app/src/main/java/com/duckduckgo/app/tabs/ui/TabSwitcherActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,17 @@ package com.duckduckgo.app.tabs.ui

import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatDelegate.FEATURE_SUPPORT_ACTION_BAR
import androidx.appcompat.content.res.AppCompatResources
import androidx.appcompat.widget.Toolbar
import androidx.core.view.MenuCompat
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
Expand All @@ -39,6 +38,8 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.duckduckgo.anvil.annotations.InjectWith
import com.duckduckgo.app.browser.R
import com.duckduckgo.app.browser.databinding.ActivityTabSwitcherBinding
import com.duckduckgo.app.browser.databinding.PopupTabsMenuBinding
import com.duckduckgo.app.browser.favicon.FaviconManager
import com.duckduckgo.app.browser.tabpreview.WebViewPreviewPersister
import com.duckduckgo.app.di.AppCoroutineScope
Expand All @@ -59,26 +60,28 @@ import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.Command.Close
import com.duckduckgo.app.tabs.ui.TabSwitcherViewModel.Command.CloseAllTabsRequest
import com.duckduckgo.appbuildconfig.api.AppBuildConfig
import com.duckduckgo.common.ui.DuckDuckGoActivity
import com.duckduckgo.common.ui.menu.PopupMenu
import com.duckduckgo.common.ui.view.button.ButtonType.DESTRUCTIVE
import com.duckduckgo.common.ui.view.button.ButtonType.GHOST_ALT
import com.duckduckgo.common.ui.view.dialog.TextAlertDialogBuilder
import com.duckduckgo.common.ui.view.gone
import com.duckduckgo.common.ui.view.hide
import com.duckduckgo.common.ui.view.show
import com.duckduckgo.common.ui.viewbinding.viewBinding
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.di.scopes.ActivityScope
import com.duckduckgo.duckchat.api.DuckChat
import com.duckduckgo.duckchat.impl.DuckChatPixelName
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext

@InjectWith(ActivityScope::class)
class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, CoroutineScope {
Expand Down Expand Up @@ -144,17 +147,21 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
private lateinit var toolbar: Toolbar
private lateinit var tabsFab: ExtendedFloatingActionButton

private var popupMenuItem: MenuItem? = null
private var layoutTypeMenuItem: MenuItem? = null
private var layoutType: LayoutType? = null

private val binding: ActivityTabSwitcherBinding by viewBinding()
private val popupMenu by lazy {
PopupMenu(layoutInflater, R.layout.popup_tabs_menu)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_tab_switcher)
setContentView(binding.root)

firstTimeLoadingTabsList = savedInstanceState?.getBoolean(KEY_FIRST_TIME_LOADING) ?: true

tabsFab = findViewById(R.id.tabsFab)

extractIntentExtras()
configureViewReferences()
setupToolbar(toolbar)
Expand All @@ -165,6 +172,7 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
}

private fun configureFab() {
tabsFab = binding.tabsFab
if (tabManagerFeatureFlags.multiSelection().isEnabled()) {
tabsFab.show()
tabsFab.setOnClickListener {
Expand Down Expand Up @@ -376,24 +384,21 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine

override fun onCreateOptionsMenu(menu: Menu): Boolean {
if (tabManagerFeatureFlags.multiSelection().isEnabled()) {
menuInflater.inflate(R.menu.menu_tab_switcher_activity_with_selection, menu)
popupMenuItem = menu.findItem(R.id.popupMenuItem)

val mode = viewModel.viewState.value.mode
when (mode) {
TabSwitcherViewModel.ViewState.Mode.Normal -> {
createNormalModeMenu(menu)
}
is TabSwitcherViewModel.ViewState.Mode.Selection -> {
if (mode.selectedTabs.isNotEmpty()) {
createSelectionModeMenu(menu, mode.selectedTabs.size)
} else {
createEmptySelectionModeMenu(menu)
}
createSelectionModeMenu(menu, mode.selectedTabs.size)
}
}

MenuCompat.setGroupDividerEnabled(menu, true)
} else {
menuInflater.inflate(R.menu.menu_tab_switcher_activity, menu)
layoutTypeMenuItem = menu.findItem(R.id.layoutType)
layoutTypeMenuItem = menu.findItem(R.id.layoutTypeMenuItem)

when (layoutType) {
LayoutType.GRID -> showListLayoutButton()
Expand All @@ -406,54 +411,86 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
}

private fun createSelectionModeMenu(menu: Menu, numSelectedTabs: Int) {
menuInflater.inflate(R.menu.menu_tab_switcher_selection_mode, menu)
menu.findItem(R.id.shareSelectedLinks).title = resources.getQuantityString(R.plurals.shareLinksMenuItem, numSelectedTabs, numSelectedTabs)
menu.findItem(R.id.shareSelectedLinksIcon).title = resources.getQuantityString(
R.plurals.shareLinksMenuItem,
numSelectedTabs,
numSelectedTabs,
)
menu.findItem(R.id.bookmarkSelectedTabs).title = resources.getQuantityString(R.plurals.bookmarkTabsMenuItem, numSelectedTabs, numSelectedTabs)
menu.findItem(R.id.bookmarkSelectedTabsIcon).title = resources.getQuantityString(
R.plurals.bookmarkTabsMenuItem,
numSelectedTabs,
numSelectedTabs,
)
menu.findItem(R.id.closeSelectedTabs).title = resources.getQuantityString(R.plurals.closeTabsMenuItem, numSelectedTabs, numSelectedTabs)
menu.findItem(R.id.closeSelectedTabs).colorRed()
menu.findItem(R.id.closeOtherTabs).colorRed()
}
menu.findItem(R.id.layoutTypeMenuItem).isVisible = false
menu.findItem(R.id.fireMenuItem).isVisible = false

menu.findItem(R.id.bookmarkMenuItem).apply {
if (numSelectedTabs == 0) {
isEnabled = false
iconTintList = ContextCompat.getColorStateList(this@TabSwitcherActivity, com.duckduckgo.mobile.android.R.color.disabledColor)
}
title = resources.getQuantityString(R.plurals.bookmarkTabsMenuItem, numSelectedTabs, numSelectedTabs)
}
menu.findItem(R.id.shareLinkMenuItem).apply {
if (numSelectedTabs == 0) {
isEnabled = false
iconTintList = ContextCompat.getColorStateList(this@TabSwitcherActivity, com.duckduckgo.mobile.android.R.color.disabledColor)
}
title = resources.getQuantityString(R.plurals.shareLinksMenuItem, numSelectedTabs, numSelectedTabs)
}

private fun createEmptySelectionModeMenu(menu: Menu) {
menuInflater.inflate(R.menu.menu_tab_switcher_selection_mode_no_selection, menu)
menu.findItem(R.id.closeAllTabs).colorRed()
val popupBinding = PopupTabsMenuBinding.bind(popupMenu.contentView)

popupBinding.newTabMenuItem.isVisible = false
popupBinding.selectAllMenuItem.isVisible = true
popupBinding.selectionActionsDivider.isVisible = numSelectedTabs > 0
popupBinding.shareSelectedLinksMenuItem.isVisible = numSelectedTabs > 0
popupBinding.bookmarkSelectedTabsMenuItem.isVisible = numSelectedTabs > 0
popupBinding.selectTabsDivider.isVisible = false
popupBinding.selectTabsMenuItem.isVisible = false
popupBinding.closeOtherTabsMenuItem.isVisible = numSelectedTabs > 0
popupBinding.closeSelectedTabsMenuItem.isVisible = numSelectedTabs > 0
popupBinding.closeAllTabsMenuItem.isVisible = numSelectedTabs == 0

popupBinding.shareSelectedLinksMenuItem.apply {
setPrimaryText(resources.getQuantityString(R.plurals.shareLinksMenuItem, numSelectedTabs, numSelectedTabs))
}
popupBinding.bookmarkSelectedTabsMenuItem.apply {
setPrimaryText(resources.getQuantityString(R.plurals.bookmarkTabsMenuItem, numSelectedTabs, numSelectedTabs))
}
popupBinding.closeSelectedTabsMenuItem.apply {
setPrimaryText(resources.getQuantityString(R.plurals.closeTabsMenuItem, numSelectedTabs, numSelectedTabs))
}
}

private fun createNormalModeMenu(menu: Menu) {
menuInflater.inflate(R.menu.menu_tab_switcher_normal_mode, menu)
layoutTypeMenuItem = menu.findItem(R.id.layoutType)
layoutTypeMenuItem = menu.findItem(R.id.layoutTypeMenuItem)

when (layoutType) {
LayoutType.GRID -> showListLayoutButton()
LayoutType.LIST -> showGridLayoutButton()
null -> layoutTypeMenuItem?.isVisible = false
}

menu.findItem(R.id.shareSelectedLinks).title = resources.getQuantityString(R.plurals.shareLinksMenuItem, 1)
menu.findItem(R.id.bookmarkSelectedTabs).title = resources.getQuantityString(R.plurals.bookmarkTabsMenuItem, 1)
menu.findItem(R.id.closeAllTabs).colorRed()
}
menu.findItem(R.id.bookmarkMenuItem).isVisible = false
menu.findItem(R.id.shareLinkMenuItem).isVisible = false

val popupBinding = PopupTabsMenuBinding.bind(popupMenu.contentView)

private fun MenuItem.colorRed() {
val text = SpannableString(title)
text.setSpan(ForegroundColorSpan(Color.RED), 0, text.length, 0)
title = text
popupBinding.newTabMenuItem.isVisible = true
popupBinding.selectAllMenuItem.isVisible = false
popupBinding.selectionActionsDivider.isVisible = true
popupBinding.shareSelectedLinksMenuItem.isVisible = true
popupBinding.bookmarkSelectedTabsMenuItem.isVisible = true
popupBinding.selectTabsDivider.isVisible = true
popupBinding.selectTabsMenuItem.isVisible = true
popupBinding.closeSelectedTabsMenuItem.isVisible = false
popupBinding.closeOtherTabsMenuItem.isVisible = false
popupBinding.closeAllTabsMenuItem.isVisible = true

popupBinding.shareSelectedLinksMenuItem.apply {
setPrimaryText(resources.getQuantityString(R.plurals.shareLinksMenuItem, 1))
}
popupBinding.bookmarkSelectedTabsMenuItem.apply {
setPrimaryText(resources.getQuantityString(R.plurals.bookmarkTabsMenuItem, 1))
}
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.layoutType -> onLayoutTypeToggled()
R.id.fire -> onFire()
R.id.layoutTypeMenuItem -> onLayoutTypeToggled()
R.id.fireMenuItem -> onFire()
R.id.popupMenuItem -> showPopupMenu(item.itemId)
R.id.newTab -> onNewTabRequested(fromOverflowMenu = false)
R.id.newTabOverflow -> onNewTabRequested(fromOverflowMenu = true)
R.id.duckChat -> {
Expand All @@ -472,6 +509,11 @@ class TabSwitcherActivity : DuckDuckGoActivity(), TabSwitcherListener, Coroutine
return super.onOptionsItemSelected(item)
}

private fun showPopupMenu(itemId: Int) {
val anchorView = findViewById<View>(itemId)
popupMenu.show(binding.root, anchorView)
}

override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
val closeAllTabsMenuItem = menu?.findItem(R.id.closeAllTabs)
closeAllTabsMenuItem?.isVisible = viewModel.tabs.value?.isNotEmpty() == true
Expand Down
100 changes: 100 additions & 0 deletions app/src/main/res/layout/popup_tabs_menu.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/popup_menu_bg">

<com.duckduckgo.common.ui.view.PopupMenuItemView
android:id="@+id/newTabMenuItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:primaryText="@string/newTabMenuItem" />

<com.duckduckgo.common.ui.view.PopupMenuItemView
android:id="@+id/selectAllMenuItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:primaryText="@string/selectAllTabsMenuItem" />

<com.duckduckgo.common.ui.view.divider.HorizontalDivider
android:id="@+id/selectionActionsDivider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:defaultPadding="false" />

<com.duckduckgo.common.ui.view.PopupMenuItemView
android:id="@+id/shareSelectedLinksMenuItem"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<com.duckduckgo.common.ui.view.PopupMenuItemView
android:id="@+id/bookmarkSelectedTabsMenuItem"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<com.duckduckgo.common.ui.view.divider.HorizontalDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:defaultPadding="false" />

<com.duckduckgo.common.ui.view.PopupMenuItemView
android:id="@+id/bookmarkAllTabsMenuItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:primaryText="@string/bookmarkAllTabsMenuItem" />

<com.duckduckgo.common.ui.view.divider.HorizontalDivider
android:id="@+id/selectTabsDivider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:defaultPadding="false" />

<com.duckduckgo.common.ui.view.PopupMenuItemView
android:id="@+id/selectTabsMenuItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:primaryText="@string/selectTabsMenuItem" />

<com.duckduckgo.common.ui.view.divider.HorizontalDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:defaultPadding="false" />

<com.duckduckgo.common.ui.view.PopupMenuItemView
android:id="@+id/closeAllTabsMenuItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:primaryText="@string/closeAllTabsMenuItem"
app:primaryTextType="destructive" />

<com.duckduckgo.common.ui.view.PopupMenuItemView
android:id="@+id/closeOtherTabsMenuItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:primaryText="@string/closeOtherTabsMenuItem"
app:primaryTextType="destructive" />

<com.duckduckgo.common.ui.view.PopupMenuItemView
android:id="@+id/closeSelectedTabsMenuItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:primaryTextType="destructive" />

</LinearLayout>
4 changes: 2 additions & 2 deletions app/src/main/res/menu/menu_tab_switcher_activity.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@
tools:ignore="AlwaysShowAction">

<item
android:id="@+id/layoutType"
android:id="@+id/layoutTypeMenuItem"
android:icon="@drawable/ic_list_view_24"
android:title="@string/tabSwitcherListViewMenu"
android:visible="false"
app:showAsAction="always" />

<item
android:id="@+id/fire"
android:id="@+id/fireMenuItem"
android:icon="@drawable/ic_fire"
android:title="@string/fireMenu"
app:showAsAction="always" />
Expand Down
Loading

0 comments on commit f607998

Please sign in to comment.