Repository: thunderbird/thunderbird-android
File: legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messageview/MessageViewContainerFragment.kt (Kotlin)

Code before:
        viewPager.registerOnPageChangeCallback(
            object : ViewPager2.OnPageChangeCallback() {
                // The message list is updated each time the active message is changed. To avoid message list updates
                // during the animation, we only set the active message after the animation has finished.
                override fun onPageScrollStateChanged(state: Int) {
                    if (state == ViewPager2.SCROLL_STATE_IDLE) {
                        setActiveMessage(viewPager.currentItem)
                    }
                }

                override fun onPageSelected(position: Int) {
                    if (viewPager.scrollState == ViewPager2.SCROLL_STATE_IDLE) {
                        setActiveMessage(position)
                    }
                }
            },
        )

        val recyclerView = viewPager.getChildAt(0) as? RecyclerView
        recyclerView?.let { rv ->
            // Reduce horizontal fling speed to make page swipes less sensitive and prevent accidental swipes.
            rv.onFlingListener = object : RecyclerView.OnFlingListener() {
                override fun onFling(velocityX: Int, velocityY: Int): Boolean {
                    val scaledVelocity = (velocityX * VIEW_PAGER_FLING_VELOCITY_SCALE).toInt()
                    val adjustedVelocityX = scaledVelocity.coerceIn(VIEW_PAGER_MIN_FLING_VELOCITY_X, VIEW_PAGER_MAX_FLING_VELOCITY_X)

                    return rv.fling(adjustedVelocityX, velocityY)
                }
            }
        }

        return view
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putString(STATE_MESSAGE_REFERENCE, messageReference.toIdentityString())
    }

    fun setViewModel(viewModel: MessageListViewModel) {
        viewModel.getMessageListLiveData().observe(this) { messageListInfo ->
            updateMessageList(messageListInfo.messageListItems)
        }
    }

    private fun updateMessageList(messageListItems: List<MessageListItem>) {
        if (messageListItems.isEmpty() || messageListItems.none { it.messageReference == messageReference }) {
            fragmentListener.closeMessageView()
            return
        }


Review comment (refactor):
I think a `ItemTouchHelper` gives more control of the overall behavior:

```suggestion
        recyclerView?.let { rv ->
            val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
                    override fun onMove(
                        recyclerView: RecyclerView,
                        viewHolder: RecyclerView.ViewHolder,
                        target: RecyclerView.ViewHolder,
                    ): Boolean {
                        return false
                    }

                    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                        if (direction == ItemTouchHelper.LEFT) {
                            viewPager.currentItem = viewPager.currentItem + 1
                        } else if (direction == ItemTouchHelper.RIGHT) {
                            viewPager.currentItem = viewPager.currentItem - 1
                        }
                    }

                    override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float {
                        return VIEW_PAGER_SWIPE_THRESHOLD
                    }

                    override fun getSwipeVelocityThreshold(defaultValue: Float): Float {
                        return VIEW_PAGER_SWIPE_VELOCITY_THRESHOLD
                    }
                })

            itemTouchHelper.attachToRecyclerView(rv)
        }
        
        ...
        
        companion object {
        private const val VIEW_PAGER_SWIPE_THRESHOLD = 0.4f
        private const val VIEW_PAGER_SWIPE_VELOCITY_THRESHOLD = 0.8f
        
        ...

```

Code after:
                override fun onPageScrollStateChanged(state: Int) {
                    if (state == ViewPager2.SCROLL_STATE_IDLE) {
                        setActiveMessage(viewPager.currentItem)
                    }
                }

                override fun onPageSelected(position: Int) {
                    if (viewPager.scrollState == ViewPager2.SCROLL_STATE_IDLE) {
                        setActiveMessage(position)
                    }
                }
            },
        )

        val recyclerView = viewPager.getChildAt(0) as? RecyclerView
        recyclerView?.reduceHorizontalDragSensitivity(
            viewPager = viewPager,
            threshold = VIEW_PAGER_SWIPE_THRESHOLD,
            velocityThreshold = VIEW_PAGER_SWIPE_VELOCITY_THRESHOLD,
        )

        return view
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putString(STATE_MESSAGE_REFERENCE, messageReference.toIdentityString())
    }

    fun setViewModel(viewModel: MessageListViewModel) {
        viewModel.getMessageListLiveData().observe(this) { messageListInfo ->
            updateMessageList(messageListInfo.messageListItems)
        }
    }

    private fun updateMessageList(messageListItems: List<MessageListItem>) {
        if (messageListItems.isEmpty() || messageListItems.none { it.messageReference == messageReference }) {
            fragmentListener.closeMessageView()
            return
        }

        val oldPosition = viewPager.currentItem

        adapter.messageList = messageListItems

        // We only set the adapter on ViewPager2 after the message list has been loaded. This way ViewPager2 can
        // restore its saved state after a configuration change.
        if (viewPager.adapter == null) {
            viewPager.adapter = adapter
        }