Skip to content

Commit

Permalink
support for select and selectAll in ItemSelectionActions
Browse files Browse the repository at this point in the history
  • Loading branch information
idanatz committed Mar 28, 2024
1 parent 8e48c39 commit 1001220
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import java.lang.IllegalStateException

private const val UPDATE_DATA_DELAY_MILLIS = 100L

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

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

private val viewHolderCreatorsStore = ViewHolderCreatorsStore()
private val viewHolderCreatorsStore = ViewHolderCreatorsStore()
private val holderPositionHandler = HolderPositionHandler()
private val logger = Logger(this)

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

override fun getItemViewType(position: Int) = viewHolderCreatorsStore.getCreatorUniqueIndex(data[position].javaClass)
Expand Down Expand Up @@ -317,7 +317,14 @@ internal class InternalAdapter(val recyclerView: RecyclerView) : RecyclerView.Ad
fun enableSelection(itemSelectionModule: ItemSelectionModule) {
itemSelectionModule.actions = this
modules.itemSelectionModule = itemSelectionModule
oneSelectionHandler = OneSelectionHandler(itemSelectionModule, recyclerView).also { it.observer = this }
oneSelectionHandler = OneSelectionHandler(
selectionModule = itemSelectionModule,
recyclerView = recyclerView,
getItemModuleByItemId = { itemId ->
val model = data.find { getItemId(data.indexOf(it)) == itemId } ?: return@OneSelectionHandler null
modules.itemModules[model::class.java]
}
).also { it.observer = this }
}

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

override fun clearSelection(): Boolean {
override fun select(position: Int): Boolean? {
return oneSelectionHandler?.select(position)
}

override fun selectAll(): Boolean? {
return oneSelectionHandler?.selectAll()
}

override fun clearSelection(): Boolean {
return oneSelectionHandler?.clearSelection() ?: false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import com.idanatz.oneadapter.external.interfaces.Diffable
interface ItemSelectionActions {

fun startSelection()
fun select(position: Int): Boolean?
fun selectAll(): Boolean?
fun clearSelection(): Boolean?

fun getSelectedItems(): List<Diffable>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import com.idanatz.oneadapter.internal.utils.extensions.toOneViewHolder

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

@Nullable
override fun getItemDetails(@NotNull e: MotionEvent): ItemDetails<Long>? {
override fun getItemDetails(e: MotionEvent): ItemDetails<Long>? {
return recyclerView.findChildViewUnder(e.x, e.y)?.let {
val viewHolder = recyclerView.getChildViewHolder(it).toOneViewHolder()
return viewHolder.createItemLookupInformation()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ package com.idanatz.oneadapter.internal.selection
import androidx.recyclerview.selection.SelectionTracker
import androidx.recyclerview.selection.StorageStrategy
import androidx.recyclerview.widget.RecyclerView
import com.idanatz.oneadapter.external.modules.ItemModule
import com.idanatz.oneadapter.external.modules.ItemSelectionModule
import com.idanatz.oneadapter.external.modules.ItemSelectionModuleConfig
import com.idanatz.oneadapter.external.states.SelectionStateConfig
import com.idanatz.oneadapter.internal.utils.extensions.toOneViewHolder
import java.util.*

@Suppress("UNCHECKED_CAST")
internal class OneSelectionHandler(
selectionModule: ItemSelectionModule,
val recyclerView: RecyclerView
selectionModule: ItemSelectionModule,
val recyclerView: RecyclerView,
val getItemModuleByItemId: (Long) -> ItemModule<*>?
) : SelectionTracker.SelectionObserver<Long>() {

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

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

return !forbidSelection
val isEnabled = itemModule.states.getSelectionState()?.config?.enabled ?: false
val forbidDueToManualSelection = itemModule.states.getSelectionState()?.config?.selectionTrigger == SelectionStateConfig.SelectionTrigger.Manual && !isInManualSelection()
return isEnabled && !forbidDueToManualSelection
}

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

fun select(position: Int): Boolean {
val key = itemKeyProvider.getKey(position) ?: return false
return selectionTracker.select(key)
}

fun selectAll(): Boolean {
val itemCount = recyclerView.adapter?.itemCount ?: 0
val toBeSelectedKeys = arrayListOf<Long>()
for (i in 0 until itemCount) {
val key = itemKeyProvider.getKey(i)
if (key != null && !selectionTracker.isSelected(key)) {
toBeSelectedKeys.add(key)
}

}
return selectionTracker.setItemsSelected(toBeSelectedKeys, true)
}

fun clearSelection(): Boolean = selectionTracker.clearSelection()

fun getSelectedPositions(): List<Int> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.bumptech.glide.Glide
import com.idanatz.oneadapter.external.event_hooks.SwipeEventHook
import com.idanatz.oneadapter.external.modules.*
import com.idanatz.oneadapter.external.modules.ItemSelectionModuleConfig.*
import com.idanatz.oneadapter.external.states.SelectionStateConfig
import com.idanatz.sample.examples.BaseExampleActivity
import com.idanatz.sample.examples.ActionsDialog.*
import com.idanatz.sample.models.StoriesModel
Expand Down Expand Up @@ -282,6 +283,10 @@ class CompleteExampleActivity : BaseExampleActivity() {
oneAdapter.modules.itemSelectionModule?.actions?.startSelection()
return true
}
R.id.action_select_all -> {
oneAdapter.modules.itemSelectionModule?.actions?.selectAll()
return true
}
else -> super.onOptionsItemSelected(item)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ class ItemSelectionModuleActivity : BaseExampleActivity() {
oneAdapter.modules.itemSelectionModule?.actions?.startSelection()
return true
}
R.id.action_select_all -> {
oneAdapter.modules.itemSelectionModule?.actions?.selectAll()
return true
}
else -> super.onOptionsItemSelected(item)
}
}
Expand Down
5 changes: 5 additions & 0 deletions sample/app/src/main/res/drawable/baseline_checklist_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<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"/>
</vector>
10 changes: 9 additions & 1 deletion sample/app/src/main/res/menu/menu_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,18 @@
android:id="@+id/action_start_selection"
android:orderInCategory="300"
android:title="Start Selection"
android:visible="false"
android:visible="true"
android:icon="@drawable/ic_check_box_white_24dp"
app:showAsAction="ifRoom"/>

<item
android:id="@+id/action_select_all"
android:orderInCategory="300"
android:title="Select All"
android:visible="true"
android:icon="@drawable/baseline_checklist_24"
app:showAsAction="ifRoom"/>

<item
android:id="@+id/action_delete"
android:orderInCategory="300"
Expand Down

0 comments on commit 1001220

Please sign in to comment.