Deselect message during swipe
When swiping a selected message we remove the selection state at the start and restore it afterwards if the list item isn't removed. Except when the swipe action is "toggle selection". Then we keep the current selection state while the list item is dragged.
This commit is contained in:
parent
273d0b433d
commit
789fbe4d43
4 changed files with 140 additions and 31 deletions
|
@ -480,11 +480,11 @@ class MessageListAdapter internal constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun selectMessage(item: MessageListItem) {
|
||||
fun selectMessage(item: MessageListItem) {
|
||||
selected = selected + item.uniqueId
|
||||
}
|
||||
|
||||
private fun deselectMessage(item: MessageListItem) {
|
||||
fun deselectMessage(item: MessageListItem) {
|
||||
selected = selected - item.uniqueId
|
||||
}
|
||||
|
||||
|
|
|
@ -810,7 +810,24 @@ class MessageListFragment :
|
|||
|
||||
private fun toggleMessageSelect(messageListItem: MessageListItem) {
|
||||
adapter.toggleSelection(messageListItem)
|
||||
updateAfterSelectionChange()
|
||||
}
|
||||
|
||||
private fun selectMessage(messageListItem: MessageListItem) {
|
||||
adapter.selectMessage(messageListItem)
|
||||
updateAfterSelectionChange()
|
||||
}
|
||||
|
||||
private fun deselectMessage(messageListItem: MessageListItem) {
|
||||
adapter.deselectMessage(messageListItem)
|
||||
updateAfterSelectionChange()
|
||||
}
|
||||
|
||||
private fun isMessageSelected(messageListItem: MessageListItem): Boolean {
|
||||
return adapter.isSelected(messageListItem)
|
||||
}
|
||||
|
||||
private fun updateAfterSelectionChange() {
|
||||
if (adapter.selectedCount == 0) {
|
||||
actionMode?.finish()
|
||||
actionMode = null
|
||||
|
@ -1434,36 +1451,67 @@ class MessageListFragment :
|
|||
private val isPullToRefreshAllowed: Boolean
|
||||
get() = isRemoteSearchAllowed || isCheckMailAllowed
|
||||
|
||||
private val swipeListener = MessageListSwipeListener { item, action ->
|
||||
when (action) {
|
||||
SwipeAction.None -> Unit
|
||||
SwipeAction.ToggleSelection -> {
|
||||
toggleMessageSelect(item)
|
||||
private var itemSelectedOnSwipeStart = false
|
||||
|
||||
private val swipeListener = object : MessageListSwipeListener {
|
||||
override fun onSwipeStarted(item: MessageListItem, action: SwipeAction) {
|
||||
itemSelectedOnSwipeStart = isMessageSelected(item)
|
||||
if (itemSelectedOnSwipeStart && action != SwipeAction.ToggleSelection) {
|
||||
deselectMessage(item)
|
||||
}
|
||||
SwipeAction.ToggleRead -> {
|
||||
setFlag(item, Flag.SEEN, !item.isRead)
|
||||
}
|
||||
SwipeAction.ToggleStar -> {
|
||||
setFlag(item, Flag.FLAGGED, !item.isStarred)
|
||||
}
|
||||
SwipeAction.Archive -> {
|
||||
onArchive(item.messageReference)
|
||||
}
|
||||
SwipeAction.Delete -> {
|
||||
if (K9.isConfirmDelete) {
|
||||
notifyItemChanged(item)
|
||||
}
|
||||
|
||||
override fun onSwipeActionChanged(item: MessageListItem, action: SwipeAction) {
|
||||
if (action == SwipeAction.ToggleSelection) {
|
||||
if (itemSelectedOnSwipeStart && !isMessageSelected(item)) {
|
||||
selectMessage(item)
|
||||
}
|
||||
onDelete(listOf(item.messageReference))
|
||||
} else if (isMessageSelected(item)) {
|
||||
deselectMessage(item)
|
||||
}
|
||||
SwipeAction.Spam -> {
|
||||
if (K9.isConfirmSpam) {
|
||||
notifyItemChanged(item)
|
||||
}
|
||||
|
||||
override fun onSwipeAction(item: MessageListItem, action: SwipeAction) {
|
||||
if (action.removesItem || action == SwipeAction.ToggleSelection) {
|
||||
itemSelectedOnSwipeStart = false
|
||||
}
|
||||
|
||||
when (action) {
|
||||
SwipeAction.None -> Unit
|
||||
SwipeAction.ToggleSelection -> {
|
||||
toggleMessageSelect(item)
|
||||
}
|
||||
SwipeAction.ToggleRead -> {
|
||||
setFlag(item, Flag.SEEN, !item.isRead)
|
||||
}
|
||||
SwipeAction.ToggleStar -> {
|
||||
setFlag(item, Flag.FLAGGED, !item.isStarred)
|
||||
}
|
||||
SwipeAction.Archive -> {
|
||||
onArchive(item.messageReference)
|
||||
}
|
||||
SwipeAction.Delete -> {
|
||||
if (K9.isConfirmDelete) {
|
||||
notifyItemChanged(item)
|
||||
}
|
||||
onDelete(listOf(item.messageReference))
|
||||
}
|
||||
SwipeAction.Spam -> {
|
||||
if (K9.isConfirmSpam) {
|
||||
notifyItemChanged(item)
|
||||
}
|
||||
onSpam(listOf(item.messageReference))
|
||||
}
|
||||
SwipeAction.Move -> {
|
||||
notifyItemChanged(item)
|
||||
onMove(item.messageReference)
|
||||
}
|
||||
onSpam(listOf(item.messageReference))
|
||||
}
|
||||
SwipeAction.Move -> {
|
||||
notifyItemChanged(item)
|
||||
onMove(item.messageReference)
|
||||
}
|
||||
|
||||
override fun onSwipeEnded(item: MessageListItem) {
|
||||
if (itemSelectedOnSwipeStart && !isMessageSelected(item)) {
|
||||
selectMessage(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,9 +68,28 @@ class MessageListSwipeCallback(
|
|||
throw UnsupportedOperationException("not implemented")
|
||||
}
|
||||
|
||||
override fun onSwipeStarted(viewHolder: ViewHolder, direction: Int) {
|
||||
val swipeAction = when (direction) {
|
||||
ItemTouchHelper.RIGHT -> swipeRightAction
|
||||
ItemTouchHelper.LEFT -> swipeLeftAction
|
||||
else -> error("Unsupported direction: $direction")
|
||||
}
|
||||
|
||||
listener.onSwipeStarted(viewHolder.messageListItem, swipeAction)
|
||||
}
|
||||
|
||||
override fun onSwipeDirectionChanged(viewHolder: ViewHolder, direction: Int) {
|
||||
val swipeAction = when (direction) {
|
||||
ItemTouchHelper.RIGHT -> swipeRightAction
|
||||
ItemTouchHelper.LEFT -> swipeLeftAction
|
||||
else -> error("Unsupported direction: $direction")
|
||||
}
|
||||
|
||||
listener.onSwipeActionChanged(viewHolder.messageListItem, swipeAction)
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
|
||||
val holder = viewHolder as MessageViewHolder
|
||||
val item = adapter.getItemById(holder.uniqueId) ?: error("Couldn't find MessageListItem")
|
||||
val item = viewHolder.messageListItem
|
||||
|
||||
// Mark view to prevent MessageListItemAnimator from interfering with swipe animations
|
||||
viewHolder.markAsSwiped(true)
|
||||
|
@ -82,6 +101,10 @@ class MessageListSwipeCallback(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onSwipeEnded(viewHolder: ViewHolder) {
|
||||
listener.onSwipeEnded(viewHolder.messageListItem)
|
||||
}
|
||||
|
||||
override fun clearView(recyclerView: RecyclerView, viewHolder: ViewHolder) {
|
||||
super.clearView(recyclerView, viewHolder)
|
||||
viewHolder.markAsSwiped(false)
|
||||
|
@ -206,14 +229,21 @@ class MessageListSwipeCallback(
|
|||
val percentage = abs(animateDx) / recyclerView.width
|
||||
return (super.getAnimationDuration(recyclerView, animationType, animateDx, animateDy) * percentage).toLong()
|
||||
}
|
||||
|
||||
private val ViewHolder.messageListItem: MessageListItem
|
||||
get() = (this as? MessageViewHolder)?.uniqueId?.let { adapter.getItemById(it) }
|
||||
?: error("Couldn't find MessageListItem")
|
||||
}
|
||||
|
||||
fun interface SwipeActionSupportProvider {
|
||||
fun isActionSupported(item: MessageListItem, action: SwipeAction): Boolean
|
||||
}
|
||||
|
||||
fun interface MessageListSwipeListener {
|
||||
interface MessageListSwipeListener {
|
||||
fun onSwipeStarted(item: MessageListItem, action: SwipeAction)
|
||||
fun onSwipeActionChanged(item: MessageListItem, action: SwipeAction)
|
||||
fun onSwipeAction(item: MessageListItem, action: SwipeAction)
|
||||
fun onSwipeEnded(item: MessageListItem)
|
||||
}
|
||||
|
||||
private fun ViewHolder.markAsSwiped(value: Boolean) {
|
||||
|
|
|
@ -193,6 +193,11 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
|
|||
|
||||
float mDy;
|
||||
|
||||
/**
|
||||
* Current swipe direction. Used for {@link Callback#onSwipeDirectionChanged(ViewHolder, int)}
|
||||
*/
|
||||
private int mSwipeDirection = 0;
|
||||
|
||||
/**
|
||||
* The coordinates of the selected view at the time it is selected. We record these values
|
||||
* when action starts so that we can consistently position it even if LayoutManager moves the
|
||||
|
@ -554,6 +559,14 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
|
|||
if ((mSelectedFlags & (LEFT | RIGHT)) != 0 && dx != 0) {
|
||||
dx = limitDeltaX(parent, dx);
|
||||
}
|
||||
|
||||
if (dx != 0) {
|
||||
int direction = dx > 0 ? RIGHT : LEFT;
|
||||
if (direction != mSwipeDirection) {
|
||||
mSwipeDirection = direction;
|
||||
mCallback.onSwipeDirectionChanged(mSelected, direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mCallback.onDraw(c, parent, mSelected,
|
||||
|
@ -582,6 +595,7 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
|
|||
if (selected == mSelected && actionState == mActionState) {
|
||||
return;
|
||||
}
|
||||
|
||||
mDragScrollStartTimeInMs = Long.MIN_VALUE;
|
||||
final int prevActionState = mActionState;
|
||||
// prevent duplicate animations
|
||||
|
@ -726,6 +740,8 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
|
|||
mCallback.onSwiped(anim.mViewHolder, swipeDir);
|
||||
if (moveBackAfterwards) {
|
||||
startMoveBackAnimation(anim);
|
||||
} else {
|
||||
mCallback.onSwipeEnded(anim.mViewHolder);
|
||||
}
|
||||
} else {
|
||||
mRecyclerView.post(this);
|
||||
|
@ -1061,6 +1077,10 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
|
|||
}
|
||||
mDx = mDy = 0f;
|
||||
mActivePointerId = motionEvent.getPointerId(0);
|
||||
|
||||
mSwipeDirection = dx > 0 ? RIGHT : LEFT;
|
||||
mCallback.onSwipeStarted(vh, mSwipeDirection);
|
||||
|
||||
select(vh, ACTION_STATE_SWIPE);
|
||||
}
|
||||
|
||||
|
@ -2240,9 +2260,18 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
|
|||
* @param direction The swipe direction.
|
||||
* @return The maximum distance in pixels that a view can be moved during a swipe.
|
||||
*/
|
||||
public int getMaxSwipeDistance(RecyclerView recyclerView, int direction) {
|
||||
public int getMaxSwipeDistance(@NonNull RecyclerView recyclerView, int direction) {
|
||||
return recyclerView.getWidth();
|
||||
}
|
||||
|
||||
public void onSwipeStarted(@NonNull ViewHolder viewHolder, int direction) {
|
||||
}
|
||||
|
||||
public void onSwipeDirectionChanged(@NonNull ViewHolder viewHolder, int direction) {
|
||||
}
|
||||
|
||||
public void onSwipeEnded(@NonNull ViewHolder viewHolder) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2551,6 +2580,8 @@ public class ItemTouchHelper extends RecyclerView.ItemDecoration
|
|||
return;
|
||||
}
|
||||
|
||||
mCallback.onSwipeEnded(mViewHolder);
|
||||
|
||||
mCallback.clearView(mRecyclerView, mViewHolder);
|
||||
// full cleanup will happen on onDrawOver
|
||||
|
||||
|
|
Loading…
Reference in a new issue