Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 789fbe4d authored by cketti's avatar cketti
Browse files

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.
parent 273d0b43
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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
    }

+71 −23
Original line number Diff line number Diff line
@@ -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,7 +1451,31 @@ class MessageListFragment :
    private val isPullToRefreshAllowed: Boolean
        get() = isRemoteSearchAllowed || isCheckMailAllowed

    private val swipeListener = MessageListSwipeListener { item, action ->
    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)
            }
        }

        override fun onSwipeActionChanged(item: MessageListItem, action: SwipeAction) {
            if (action == SwipeAction.ToggleSelection) {
                if (itemSelectedOnSwipeStart && !isMessageSelected(item)) {
                    selectMessage(item)
                }
            } else if (isMessageSelected(item)) {
                deselectMessage(item)
            }
        }

        override fun onSwipeAction(item: MessageListItem, action: SwipeAction) {
            if (action.removesItem || action == SwipeAction.ToggleSelection) {
                itemSelectedOnSwipeStart = false
            }

            when (action) {
                SwipeAction.None -> Unit
                SwipeAction.ToggleSelection -> {
@@ -1468,6 +1509,13 @@ class MessageListFragment :
            }
        }

        override fun onSwipeEnded(item: MessageListItem) {
            if (itemSelectedOnSwipeStart && !isMessageSelected(item)) {
                selectMessage(item)
            }
        }
    }

    private fun notifyItemChanged(item: MessageListItem) {
        val position = adapter.getPosition(item) ?: return
        adapter.notifyItemChanged(position)
+33 −3
Original line number Diff line number Diff line
@@ -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) {
+32 −1
Original line number Diff line number Diff line
@@ -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