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:
cketti 2022-11-11 14:00:11 +01:00
parent 273d0b433d
commit 789fbe4d43
4 changed files with 140 additions and 31 deletions

View file

@ -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
}

View file

@ -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)
}
}
}

View file

@ -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) {

View file

@ -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