Skip to content

Commit 1001220

Browse files
committed
support for select and selectAll in ItemSelectionActions
1 parent 8e48c39 commit 1001220

File tree

8 files changed

+73
-18
lines changed

8 files changed

+73
-18
lines changed

oneadapter/src/main/java/com/idanatz/oneadapter/internal/InternalAdapter.kt

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import java.lang.IllegalStateException
3131

3232
private const val UPDATE_DATA_DELAY_MILLIS = 100L
3333

34-
@Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
34+
@Suppress("UNCHECKED_CAST")
3535
internal class InternalAdapter(val recyclerView: RecyclerView) : RecyclerView.Adapter<OneViewHolder<Diffable>>(),
3636
LoadMoreObserver, SelectionObserver, ItemSelectionActions {
3737

@@ -44,7 +44,7 @@ internal class InternalAdapter(val recyclerView: RecyclerView) : RecyclerView.Ad
4444
private val context
4545
get() = recyclerView.context
4646

47-
private val viewHolderCreatorsStore = ViewHolderCreatorsStore()
47+
private val viewHolderCreatorsStore = ViewHolderCreatorsStore()
4848
private val holderPositionHandler = HolderPositionHandler()
4949
private val logger = Logger(this)
5050

@@ -137,7 +137,7 @@ internal class InternalAdapter(val recyclerView: RecyclerView) : RecyclerView.Ad
137137
override fun getItemId(position: Int): Long {
138138
val item = data[position]
139139
// javaClass is used for lettings different Diffable models share the same unique identifier
140-
return item.javaClass.name.hashCode() + item.uniqueIdentifier
140+
return (item.javaClass.name.hashCode() + item.uniqueIdentifier)
141141
}
142142

143143
override fun getItemViewType(position: Int) = viewHolderCreatorsStore.getCreatorUniqueIndex(data[position].javaClass)
@@ -317,7 +317,14 @@ internal class InternalAdapter(val recyclerView: RecyclerView) : RecyclerView.Ad
317317
fun enableSelection(itemSelectionModule: ItemSelectionModule) {
318318
itemSelectionModule.actions = this
319319
modules.itemSelectionModule = itemSelectionModule
320-
oneSelectionHandler = OneSelectionHandler(itemSelectionModule, recyclerView).also { it.observer = this }
320+
oneSelectionHandler = OneSelectionHandler(
321+
selectionModule = itemSelectionModule,
322+
recyclerView = recyclerView,
323+
getItemModuleByItemId = { itemId ->
324+
val model = data.find { getItemId(data.indexOf(it)) == itemId } ?: return@OneSelectionHandler null
325+
modules.itemModules[model::class.java]
326+
}
327+
).also { it.observer = this }
321328
}
322329

323330
override fun onItemStateChanged(holder: OneViewHolder<Diffable>, position: Int, selected: Boolean) {
@@ -340,7 +347,15 @@ internal class InternalAdapter(val recyclerView: RecyclerView) : RecyclerView.Ad
340347
oneSelectionHandler?.startSelection()
341348
}
342349

343-
override fun clearSelection(): Boolean {
350+
override fun select(position: Int): Boolean? {
351+
return oneSelectionHandler?.select(position)
352+
}
353+
354+
override fun selectAll(): Boolean? {
355+
return oneSelectionHandler?.selectAll()
356+
}
357+
358+
override fun clearSelection(): Boolean {
344359
return oneSelectionHandler?.clearSelection() ?: false
345360
}
346361

oneadapter/src/main/java/com/idanatz/oneadapter/internal/selection/ItemSelectionActions.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import com.idanatz.oneadapter.external.interfaces.Diffable
55
interface ItemSelectionActions {
66

77
fun startSelection()
8+
fun select(position: Int): Boolean?
9+
fun selectAll(): Boolean?
810
fun clearSelection(): Boolean?
911

1012
fun getSelectedItems(): List<Diffable>

oneadapter/src/main/java/com/idanatz/oneadapter/internal/selection/OneItemDetailLookup.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ import com.idanatz.oneadapter.internal.utils.extensions.toOneViewHolder
99

1010
internal class OneItemDetailLookup(private val recyclerView: RecyclerView) : ItemDetailsLookup<Long>() {
1111

12-
@Nullable
13-
override fun getItemDetails(@NotNull e: MotionEvent): ItemDetails<Long>? {
12+
override fun getItemDetails(e: MotionEvent): ItemDetails<Long>? {
1413
return recyclerView.findChildViewUnder(e.x, e.y)?.let {
1514
val viewHolder = recyclerView.getChildViewHolder(it).toOneViewHolder()
1615
return viewHolder.createItemLookupInformation()

oneadapter/src/main/java/com/idanatz/oneadapter/internal/selection/OneSelectionHandler.kt

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ package com.idanatz.oneadapter.internal.selection
33
import androidx.recyclerview.selection.SelectionTracker
44
import androidx.recyclerview.selection.StorageStrategy
55
import androidx.recyclerview.widget.RecyclerView
6+
import com.idanatz.oneadapter.external.modules.ItemModule
67
import com.idanatz.oneadapter.external.modules.ItemSelectionModule
78
import com.idanatz.oneadapter.external.modules.ItemSelectionModuleConfig
89
import com.idanatz.oneadapter.external.states.SelectionStateConfig
910
import com.idanatz.oneadapter.internal.utils.extensions.toOneViewHolder
1011
import java.util.*
1112

12-
@Suppress("UNCHECKED_CAST")
1313
internal class OneSelectionHandler(
14-
selectionModule: ItemSelectionModule,
15-
val recyclerView: RecyclerView
14+
selectionModule: ItemSelectionModule,
15+
val recyclerView: RecyclerView,
16+
val getItemModuleByItemId: (Long) -> ItemModule<*>?
1617
) : SelectionTracker.SelectionObserver<Long>() {
1718

1819
private val ghostKey = UUID.randomUUID().mostSignificantBits
@@ -29,15 +30,13 @@ internal class OneSelectionHandler(
2930
.withSelectionPredicate(object : SelectionTracker.SelectionPredicate<Long>() {
3031
override fun canSetStateForKey(key: Long, nextState: Boolean): Boolean {
3132
if (key == ghostKey)
32-
return true // always accept let the ghost key
33+
return true // always accept the ghost key
3334

34-
val forbidSelection = recyclerView.findViewHolderForItemId(key)?.toOneViewHolder()?.let { holder ->
35-
holder.statesHooksMap?.getSelectionState()?.config?.let { selectionStateConfig ->
36-
selectionStateConfig.selectionTrigger == SelectionStateConfig.SelectionTrigger.Manual && !isInManualSelection()
37-
} ?: true
38-
} ?: true
35+
val itemModule = getItemModuleByItemId(key) ?: return false
3936

40-
return !forbidSelection
37+
val isEnabled = itemModule.states.getSelectionState()?.config?.enabled ?: false
38+
val forbidDueToManualSelection = itemModule.states.getSelectionState()?.config?.selectionTrigger == SelectionStateConfig.SelectionTrigger.Manual && !isInManualSelection()
39+
return isEnabled && !forbidDueToManualSelection
4140
}
4241

4342
override fun canSetStateAtPosition(position: Int, nextState: Boolean): Boolean = true
@@ -56,6 +55,24 @@ internal class OneSelectionHandler(
5655
selectionTracker.select(ghostKey)
5756
}
5857

58+
fun select(position: Int): Boolean {
59+
val key = itemKeyProvider.getKey(position) ?: return false
60+
return selectionTracker.select(key)
61+
}
62+
63+
fun selectAll(): Boolean {
64+
val itemCount = recyclerView.adapter?.itemCount ?: 0
65+
val toBeSelectedKeys = arrayListOf<Long>()
66+
for (i in 0 until itemCount) {
67+
val key = itemKeyProvider.getKey(i)
68+
if (key != null && !selectionTracker.isSelected(key)) {
69+
toBeSelectedKeys.add(key)
70+
}
71+
72+
}
73+
return selectionTracker.setItemsSelected(toBeSelectedKeys, true)
74+
}
75+
5976
fun clearSelection(): Boolean = selectionTracker.clearSelection()
6077

6178
fun getSelectedPositions(): List<Int> {

sample/app/src/main/java/com/idanatz/sample/examples/complete/CompleteExampleActivity.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import com.bumptech.glide.Glide
2828
import com.idanatz.oneadapter.external.event_hooks.SwipeEventHook
2929
import com.idanatz.oneadapter.external.modules.*
3030
import com.idanatz.oneadapter.external.modules.ItemSelectionModuleConfig.*
31+
import com.idanatz.oneadapter.external.states.SelectionStateConfig
3132
import com.idanatz.sample.examples.BaseExampleActivity
3233
import com.idanatz.sample.examples.ActionsDialog.*
3334
import com.idanatz.sample.models.StoriesModel
@@ -282,6 +283,10 @@ class CompleteExampleActivity : BaseExampleActivity() {
282283
oneAdapter.modules.itemSelectionModule?.actions?.startSelection()
283284
return true
284285
}
286+
R.id.action_select_all -> {
287+
oneAdapter.modules.itemSelectionModule?.actions?.selectAll()
288+
return true
289+
}
285290
else -> super.onOptionsItemSelected(item)
286291
}
287292
}

sample/app/src/main/java/com/idanatz/sample/examples/features/ItemSelectionModuleActivity.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ class ItemSelectionModuleActivity : BaseExampleActivity() {
119119
oneAdapter.modules.itemSelectionModule?.actions?.startSelection()
120120
return true
121121
}
122+
R.id.action_select_all -> {
123+
oneAdapter.modules.itemSelectionModule?.actions?.selectAll()
124+
return true
125+
}
122126
else -> super.onOptionsItemSelected(item)
123127
}
124128
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<vector android:height="24dp" android:tint="#FFFFFF"
2+
android:viewportHeight="24" android:viewportWidth="24"
3+
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
4+
<path android:fillColor="@android:color/white" android:pathData="M22,7h-9v2h9V7zM22,15h-9v2h9V15zM5.54,11L2,7.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,11zM5.54,19L2,15.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,19z"/>
5+
</vector>

sample/app/src/main/res/menu/menu_main.xml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,18 @@
88
android:id="@+id/action_start_selection"
99
android:orderInCategory="300"
1010
android:title="Start Selection"
11-
android:visible="false"
11+
android:visible="true"
1212
android:icon="@drawable/ic_check_box_white_24dp"
1313
app:showAsAction="ifRoom"/>
1414

15+
<item
16+
android:id="@+id/action_select_all"
17+
android:orderInCategory="300"
18+
android:title="Select All"
19+
android:visible="true"
20+
android:icon="@drawable/baseline_checklist_24"
21+
app:showAsAction="ifRoom"/>
22+
1523
<item
1624
android:id="@+id/action_delete"
1725
android:orderInCategory="300"

0 commit comments

Comments
 (0)