Skip to content

Multi-selection: Selection mode #5623

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b66d7d8
Add item checkbox resources and image view
0nko Feb 10, 2025
2992f91
Modify tab item decorator to handle the selection mode decoration
0nko Feb 10, 2025
4774700
Add selection mode logic
0nko Feb 10, 2025
1979097
Fix the tab selection adapter update
0nko Feb 10, 2025
47e74be
Fix the selection decorator radios
0nko Feb 10, 2025
481d735
Trigger selection mode from the menu
0nko Feb 10, 2025
b55ee8f
Add a way to cancel selection mode
0nko Feb 10, 2025
684881a
Do not cancel selection mode when scrolling
0nko Feb 10, 2025
0fa86cd
Update toolbar title on selection change
0nko Feb 10, 2025
a247925
Update FAB type based on the selection mode
0nko Feb 12, 2025
f37d813
Fix a lint error
0nko Feb 13, 2025
d428582
Trigger normal mode when all tabs are cleared
0nko Feb 13, 2025
6ccecf6
Remove tab selection when tab is deleted and restore it when it's undone
0nko Feb 24, 2025
5abb3fa
Reverse unrelated code
0nko Feb 24, 2025
4047cdc
Fix the tab selection after rebase
0nko Feb 27, 2025
56cc7d0
Fix the failing unit test
0nko Mar 3, 2025
25062a6
Add Deselect all menu item
0nko Mar 4, 2025
b134d15
Add a feature flag for the tab switcher items
0nko Mar 5, 2025
5003aac
Add deselect menu
0nko Mar 10, 2025
ae5913e
Fix the tab selection
0nko Mar 10, 2025
8ed9ed9
Fix the build
0nko Mar 10, 2025
effdcbe
Uncomment the layout mode change
0nko Mar 10, 2025
e11a35a
Add selection image content description
0nko Mar 10, 2025
8f31cab
Avoid null case for layout mode update
0nko Mar 10, 2025
d4c2ac3
Avoid passing around the selection mode
0nko Mar 10, 2025
8e651b4
Fix ktlint issues
0nko Mar 10, 2025
4ba72b5
Simplify live data creation
0nko Mar 10, 2025
2a454d6
Make the back button cancel the selection mode
0nko Mar 11, 2025
c8077b7
Make the FAB animations smoother
0nko Mar 11, 2025
d5bfab8
Fix the tests
0nko Mar 11, 2025
e6c4f0e
Update the content description of the selection checkmark based on th…
0nko Mar 12, 2025
52308c9
Use a Close command on back press instead of finish() in activity
0nko Mar 12, 2025
4fda098
Use a Tab type instead of TabSwitcherItem in the adapter
0nko Mar 12, 2025
545d6ce
Use a sealed class instead of an abstract class
0nko Mar 12, 2025
e9ff169
Fix ktlint issues
0nko Mar 12, 2025
373c5a3
Fix the imports
0nko Mar 12, 2025
c47530e
Change the back button icon to X when in select mode
0nko Mar 12, 2025
e359a18
Fix exiting the selection mode
0nko Mar 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ package com.duckduckgo.app.browser.tabs.adapter
import android.os.Bundle
import androidx.recyclerview.widget.DiffUtil
import com.duckduckgo.app.tabs.ui.TabSwitcherItem
import com.duckduckgo.app.tabs.ui.TabSwitcherItem.Tab.SelectableTab

class TabSwitcherItemDiffCallback(old: List<TabSwitcherItem>, new: List<TabSwitcherItem>) : DiffUtil.Callback() {
class TabSwitcherItemDiffCallback(
old: List<TabSwitcherItem>,
new: List<TabSwitcherItem>,
) : DiffUtil.Callback() {

// keep a local copy of the lists to avoid any changes to the lists during the diffing process
private val oldList = old.toList()
Expand All @@ -42,7 +46,8 @@ class TabSwitcherItemDiffCallback(old: List<TabSwitcherItem>, new: List<TabSwitc
oldItem.tabEntity.tabPreviewFile == newItem.tabEntity.tabPreviewFile &&
oldItem.tabEntity.viewed == newItem.tabEntity.viewed &&
oldItem.tabEntity.title == newItem.tabEntity.title &&
oldItem.tabEntity.url == newItem.tabEntity.url
oldItem.tabEntity.url == newItem.tabEntity.url &&
(oldItem as? SelectableTab)?.isSelected == (newItem as? SelectableTab)?.isSelected
}
else -> false
}
Expand Down Expand Up @@ -71,6 +76,10 @@ class TabSwitcherItemDiffCallback(old: List<TabSwitcherItem>, new: List<TabSwitc
if (oldItem.tabEntity.tabPreviewFile != newItem.tabEntity.tabPreviewFile) {
diffBundle.putString(DIFF_KEY_PREVIEW, newItem.tabEntity.tabPreviewFile)
}

if ((oldItem as? SelectableTab)?.isSelected != (newItem as? SelectableTab)?.isSelected) {
diffBundle.putString(DIFF_KEY_SELECTION, null)
}
}
}

Expand Down Expand Up @@ -114,5 +123,6 @@ class TabSwitcherItemDiffCallback(old: List<TabSwitcherItem>, new: List<TabSwitc
const val DIFF_KEY_URL = "url"
const val DIFF_KEY_PREVIEW = "previewImage"
const val DIFF_KEY_VIEWED = "viewed"
const val DIFF_KEY_SELECTION = "selection"
}
}
49 changes: 29 additions & 20 deletions app/src/main/java/com/duckduckgo/app/tabs/ui/TabItemDecorator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,52 +23,60 @@ import android.graphics.RectF
import android.util.TypedValue
import android.view.View
import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.recyclerview.widget.RecyclerView
import com.duckduckgo.app.tabs.ui.TabSwitcherItem.Tab.NormalTab
import com.duckduckgo.app.tabs.ui.TabSwitcherItem.Tab.SelectableTab
import com.duckduckgo.common.ui.view.toPx
import com.duckduckgo.mobile.android.R as CommonR

class TabItemDecorator(
context: Context,
var tabSwitcherItemId: String?,
) : RecyclerView.ItemDecoration() {

private val borderStroke: Paint = Paint().apply {
class TabItemDecorator(context: Context) : RecyclerView.ItemDecoration() {
private val activeTabBorderStroke: Paint = Paint().apply {
isAntiAlias = true
style = Paint.Style.STROKE
strokeWidth = BORDER_WIDTH
strokeWidth = ACTIVE_TAB_BORDER_WIDTH

val typedValue = TypedValue()
context.theme.resolveAttribute(CommonR.attr.daxColorBackgroundInverted, typedValue, true)
color = ContextCompat.getColor(context, typedValue.resourceId)
}

private val selectionBorderStroke: Paint = Paint().apply {
isAntiAlias = true
style = Paint.Style.STROKE
strokeWidth = SELECTION_BORDER_WIDTH

val typedValue = TypedValue()
context.theme.resolveAttribute(CommonR.attr.daxColorAccentBlue, typedValue, true)
color = ContextCompat.getColor(context, typedValue.resourceId)
}

override fun onDrawOver(
canvas: Canvas,
recyclerView: RecyclerView,
state: RecyclerView.State,
) {
val adapter = recyclerView.adapter as TabSwitcherAdapter? ?: return

for (i in 0 until recyclerView.childCount) {
val child = recyclerView.getChildAt(i)

recyclerView.children.forEach { child ->
val positionInAdapter = recyclerView.getChildAdapterPosition(child)
adapter.getTabSwitcherItem(positionInAdapter)?.let { tabSwitcherItem ->
if (tabSwitcherItem.id == tabSwitcherItemId) {
drawSelectedTabDecoration(child, canvas)
when {
tabSwitcherItem is SelectableTab && tabSwitcherItem.isSelected -> {
drawTabDecoration(child, canvas, selectionBorderStroke)
}
tabSwitcherItem is NormalTab && tabSwitcherItem.isActive -> {
drawTabDecoration(child, canvas, activeTabBorderStroke)
}
}
}
}

super.onDrawOver(canvas, recyclerView, state)
}

private fun drawSelectedTabDecoration(
child: View,
c: Canvas,
) {
borderStroke.alpha = (child.alpha * 255).toInt()
c.drawRoundRect(child.getBounds(), BORDER_RADIUS, BORDER_RADIUS, borderStroke)
private fun drawTabDecoration(child: View, c: Canvas, paint: Paint) {
selectionBorderStroke.alpha = (child.alpha * 255).toInt()
c.drawRoundRect(child.getBounds(), BORDER_RADIUS, BORDER_RADIUS, paint)
}

private fun View.getBounds(): RectF {
Expand All @@ -83,7 +91,8 @@ class TabItemDecorator(

companion object {
private val BORDER_RADIUS = 12.toPx().toFloat()
private val BORDER_WIDTH = 2.toPx().toFloat()
private val ACTIVE_TAB_BORDER_WIDTH = 2.toPx().toFloat()
private val SELECTION_BORDER_WIDTH = 4.toPx().toFloat()
private val BORDER_PADDING = 3.toPx().toFloat()
}
}
Loading
Loading