Skip to content
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

[Bulk Update Orders] Enable multiple selection in Order List #13131

Draft
wants to merge 1 commit into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -0,0 +1,26 @@
package com.woocommerce.android.ui.orders

import androidx.recyclerview.selection.ItemKeyProvider
import androidx.recyclerview.widget.RecyclerView
import com.woocommerce.android.ui.orders.list.OrderListAdapter
import com.woocommerce.android.ui.orders.list.OrderListItemUIType

/**
* Class provides the selection library access to stable selection keys and identifying items
* presented by a [RecyclerView] instance.
*/
class OrderSelectionItemKeyProvider(private val recyclerView: RecyclerView) :
ItemKeyProvider<Long>(SCOPE_MAPPED) {
override fun getKey(position: Int): Long? {
return (recyclerView.adapter as? OrderListAdapter)?.currentList?.get(position)?.let { item ->
if (item is OrderListItemUIType.OrderListItemUI) item.orderId else null
}
}

override fun getPosition(key: Long): Int {
return (recyclerView.adapter as? OrderListAdapter)?.currentList
?.indexOfFirst { item ->
item is OrderListItemUIType.OrderListItemUI && item.orderId == key
} ?: RecyclerView.NO_POSITION
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.woocommerce.android.ui.orders

import android.view.MotionEvent
import androidx.recyclerview.selection.ItemDetailsLookup
import androidx.recyclerview.widget.RecyclerView
import com.woocommerce.android.ui.orders.list.OrderListAdapter
import com.woocommerce.android.ui.orders.list.OrderListItemUIType.OrderListItemUI

class SelectableOrderListItemLookup(private val recyclerView: RecyclerView) : ItemDetailsLookup<Long>() {
override fun getItemDetails(event: MotionEvent): ItemDetails<Long>? =
recyclerView
.findChildViewUnder(event.x, event.y)
?.let { view ->
recyclerView.getChildViewHolder(view)?.let { viewHolder ->
val position = viewHolder.bindingAdapterPosition
val item = (recyclerView.adapter as? OrderListAdapter)?.currentList?.get(position)
if (item is OrderListItemUI) {
SelectableOrderItemDetailsLookup(position, item.orderId)
} else {
null
}
}
}
}

class DefaultOrderListItemLookup(private val recyclerView: RecyclerView) : ItemDetailsLookup<Long>() {
override fun getItemDetails(event: MotionEvent): ItemDetails<Long>? =
recyclerView
.findChildViewUnder(event.x, event.y)
?.let { view ->
recyclerView.getChildViewHolder(view)?.let { viewHolder ->
val position = viewHolder.bindingAdapterPosition
val item = (recyclerView.adapter as? OrderListAdapter)?.currentList?.get(position)
if (item is OrderListItemUI) {
DefaultOrderItemDetailsLookup(position, item.orderId)
} else {
null
}
}
}
}

class DefaultOrderItemDetailsLookup(
private val position: Int,
private val orderId: Long
) : ItemDetailsLookup.ItemDetails<Long>() {
override fun getPosition() = position
override fun getSelectionKey() = orderId
}

class SelectableOrderItemDetailsLookup(
private val position: Int,
private val orderId: Long
) : ItemDetailsLookup.ItemDetails<Long>() {
override fun getPosition() = position
override fun getSelectionKey() = orderId
override fun inSelectionHotspot(e: MotionEvent) = true
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.view.ViewGroup
import androidx.core.view.ViewCompat
import androidx.paging.PagedList
import androidx.paging.PagedListAdapter
import androidx.recyclerview.selection.SelectionTracker
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
Expand Down Expand Up @@ -36,6 +37,7 @@ class OrderListAdapter(

var activeOrderStatusMap: Map<String, WCOrderStatusModel> = emptyMap()
var allOrderIds: List<Long> = listOf()
var tracker: SelectionTracker<Long>? = null

override fun getItemViewType(position: Int): Int {
return when (getItem(position)) {
Expand Down Expand Up @@ -165,6 +167,15 @@ class OrderListAdapter(
)
viewBinding.divider.visibility = if (orderItemUI.isLastItemInSection) View.GONE else View.VISIBLE

val isSelected = tracker?.isSelected(orderItemUI.orderId) ?: orderItemUI.isSelected
viewBinding.orderItemLayout.setBackgroundColor(
if (isSelected) {
viewBinding.root.context.getColor(R.color.color_item_selected)
} else {
Color.TRANSPARENT
}
)

when {
orderItemUI.isSelected -> {
viewBinding.orderItemLayout.setBackgroundColor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController
import androidx.paging.PagedList
import androidx.recyclerview.selection.SelectionTracker
import androidx.recyclerview.selection.StorageStrategy
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.transition.TransitionManager
import com.google.android.material.floatingactionbutton.FloatingActionButton
Expand Down Expand Up @@ -58,15 +60,19 @@ import com.woocommerce.android.ui.jitm.JitmMessagePathsProvider
import com.woocommerce.android.ui.main.AppBarStatus
import com.woocommerce.android.ui.main.MainActivity
import com.woocommerce.android.ui.main.MainNavigationRouter
import com.woocommerce.android.ui.orders.DefaultOrderListItemLookup
import com.woocommerce.android.ui.orders.OrderSelectionItemKeyProvider
import com.woocommerce.android.ui.orders.OrderStatusUpdateSource
import com.woocommerce.android.ui.orders.OrdersCommunicationViewModel
import com.woocommerce.android.ui.orders.creation.CodeScannerStatus
import com.woocommerce.android.ui.orders.creation.GoogleBarcodeFormatMapper.BarcodeFormat
import com.woocommerce.android.ui.orders.creation.OrderCreateEditViewModel
import com.woocommerce.android.ui.orders.list.OrderListViewModel.OrderListEvent.ShowErrorSnack
import com.woocommerce.android.ui.orders.list.OrderListViewModel.OrderListEvent.ShowOrderFilters
import com.woocommerce.android.ui.products.MutableMultipleSelectionPredicate
import com.woocommerce.android.util.ChromeCustomTabUtils
import com.woocommerce.android.util.CurrencyFormatter
import com.woocommerce.android.util.FeatureFlag
import com.woocommerce.android.viewmodel.MultiLiveEvent
import com.woocommerce.android.widgets.WCEmptyView.EmptyViewType
import dagger.hilt.android.AndroidEntryPoint
Expand Down Expand Up @@ -108,6 +114,8 @@ class OrderListFragment :
@Inject
lateinit var feedbackPrefs: FeedbackPrefs

private var tracker: SelectionTracker<Long>? = null
private val selectionPredicate = MutableMultipleSelectionPredicate<Long>()
private val viewModel: OrderListViewModel by viewModels()
private val communicationViewModel: OrdersCommunicationViewModel by activityViewModels()
private var snackBar: Snackbar? = null
Expand Down Expand Up @@ -239,6 +247,36 @@ class OrderListFragment :
binding.orderFiltersCard.setClickListener { viewModel.onFiltersButtonTapped() }
initCreateOrderFAB(binding.createOrderButton)
initSwipeBehaviour()

if (FeatureFlag.BULK_UPDATE_ORDERS_STATUS.isEnabled()) {
addSelectionTracker()
}
}

private fun addSelectionTracker() {
tracker = SelectionTracker.Builder(
"orderSelection", // a string to identify our selection in the context of this fragment
binding.orderListView.ordersList, // the RecyclerView where we will apply the tracker
OrderSelectionItemKeyProvider(binding.orderListView.ordersList), // the source of selection keys
DefaultOrderListItemLookup(
binding.orderListView.ordersList
), // the source of information about recycler items
StorageStrategy.createLongStorage() // strategy for type-safe storage of the selection state
).withSelectionPredicate(selectionPredicate)
.build()

binding.orderListView.adapter.tracker = tracker // Use the new property

tracker?.addObserver(
object : SelectionTracker.SelectionObserver<Long>() {
override fun onSelectionChanged() {
val selectionCount = tracker?.selection?.size() ?: 0
// TODO: add onSelectionChanged handling to OrderListViewModel
// Temporarily showing toast instead to debug size selection
ToastUtils.showToast(context, "Current selection count: $selectionCount")
}
}
)
}

private fun setupToolbar() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ class OrderListView @JvmOverloads constructor(
val ordersList
get() = binding.ordersList

val adapter: OrderListAdapter
get() = ordersAdapter

fun init(
currencyFormatter: CurrencyFormatter,
orderListListener: OrderListListener
Expand Down
Loading