From bd1f3388f7722fa29ae2df2831e5c2818f4e9e54 Mon Sep 17 00:00:00 2001 From: Dawn-used-yeet Date: Sun, 27 Oct 2024 22:56:11 +0530 Subject: [PATCH] fix: swipy (#501) --- .../dantotsu/media/manga/mangareader/Swipy.kt | 250 +++++++----------- 1 file changed, 100 insertions(+), 150 deletions(-) diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/Swipy.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/Swipy.kt index 2b7c14de54..bed646fc14 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/Swipy.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/Swipy.kt @@ -8,7 +8,7 @@ import android.view.MotionEvent import android.view.View import android.view.ViewConfiguration import android.widget.FrameLayout -import kotlin.math.absoluteValue +import kotlin.math.abs class Swipy @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null @@ -17,7 +17,6 @@ class Swipy @JvmOverloads constructor( var dragDivider: Int = 5 var vertical = true - //public, in case a different sub child needs to be considered var child: View? = getChildAt(0) var topBeingSwiped: ((Float) -> Unit) = {} @@ -30,87 +29,80 @@ class Swipy @JvmOverloads constructor( var rightBeingSwiped: ((Float) -> Unit) = {} companion object { - private const val DRAG_RATE = .5f + private const val DRAG_RATE = 0.5f private const val INVALID_POINTER = -1 } - private var touchSlop = ViewConfiguration.get(context).scaledTouchSlop - + private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop private var activePointerId = INVALID_POINTER private var isBeingDragged = false private var initialDown = 0f private var initialMotion = 0f - enum class ScrollPosition { - None, - Start, - End, - Both - } - - private var scrollPos = ScrollPosition.None - - private fun setScrollPosition() = child?.run { - val (top, bottom) = if (vertical) - !canScrollVertically(-1) to !canScrollVertically(1) - else - !canScrollHorizontally(-1) to !canScrollHorizontally(1) - - scrollPos = when { - top && !bottom -> ScrollPosition.Start - !top && bottom -> ScrollPosition.End - top && bottom -> ScrollPosition.Both - else -> ScrollPosition.None + private enum class VerticalPosition { Top, None, Bottom } + private enum class HorizontalPosition { Left, None, Right } + + private var horizontalPos = HorizontalPosition.None + private var verticalPos = VerticalPosition.None + + private fun setChildPosition() { + child?.let { + if (vertical) { + verticalPos = when { + !it.canScrollVertically(1) && !it.canScrollVertically(-1) -> { + if (initialDown > (Resources.getSystem().displayMetrics.heightPixels / 2)) + VerticalPosition.Bottom + else + VerticalPosition.Top + } + !it.canScrollVertically(1) -> VerticalPosition.Bottom + !it.canScrollVertically(-1) -> VerticalPosition.Top + else -> VerticalPosition.None + } + } else { + horizontalPos = when { + !it.canScrollHorizontally(1) && !it.canScrollHorizontally(-1) -> { + if (initialDown > (Resources.getSystem().displayMetrics.widthPixels / 2)) + HorizontalPosition.Right + else + HorizontalPosition.Left + } + !it.canScrollHorizontally(1) -> HorizontalPosition.Right + !it.canScrollHorizontally(-1) -> HorizontalPosition.Left + else -> HorizontalPosition.None + } + } } } - private fun canChildScroll(): Boolean { - setScrollPosition() - return scrollPos == ScrollPosition.None + setChildPosition() + return if (vertical) verticalPos == VerticalPosition.None + else horizontalPos == HorizontalPosition.None } private fun onSecondaryPointerUp(ev: MotionEvent) { val pointerIndex = ev.actionIndex - val pointerId = ev.getPointerId(pointerIndex) - if (pointerId == activePointerId) { - val newPointerIndex = if (pointerIndex == 0) 1 else 0 - activePointerId = ev.getPointerId(newPointerIndex) + if (ev.getPointerId(pointerIndex) == activePointerId) { + activePointerId = ev.getPointerId(if (pointerIndex == 0) 1 else 0) } } override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { - val action = ev.actionMasked - val pointerIndex: Int - if (!isEnabled || canChildScroll()) { - return false - } + if (!isEnabled || canChildScroll()) return false - when (action) { + when (ev.actionMasked) { MotionEvent.ACTION_DOWN -> { activePointerId = ev.getPointerId(0) + initialDown = if (vertical) ev.getY(0) else ev.getX(0) isBeingDragged = false - pointerIndex = ev.findPointerIndex(activePointerId) - if (pointerIndex < 0) { - return false - } - - initialDown = if (vertical) ev.getY(pointerIndex) else ev.getX(pointerIndex) } - MotionEvent.ACTION_MOVE -> { - if (activePointerId == INVALID_POINTER) { - //("Got ACTION_MOVE event but don't have an active pointer id.") - return false - } - pointerIndex = ev.findPointerIndex(activePointerId) - if (pointerIndex < 0) { - return false + val pointerIndex = ev.findPointerIndex(activePointerId) + if (pointerIndex >= 0) { + startDragging(if (vertical) ev.getY(pointerIndex) else ev.getX(pointerIndex)) } - val pos = if (vertical) ev.getY(pointerIndex) else ev.getX(pointerIndex) - startDragging(pos) } - MotionEvent.ACTION_POINTER_UP -> onSecondaryPointerUp(ev) MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { isBeingDragged = false @@ -122,141 +114,99 @@ class Swipy @JvmOverloads constructor( @SuppressLint("ClickableViewAccessibility") override fun onTouchEvent(ev: MotionEvent): Boolean { - val action = ev.actionMasked + if (!isEnabled || canChildScroll()) return false + val pointerIndex: Int - if (!isEnabled || canChildScroll()) { - return false - } - when (action) { + when (ev.actionMasked) { MotionEvent.ACTION_DOWN -> { activePointerId = ev.getPointerId(0) isBeingDragged = false } - MotionEvent.ACTION_MOVE -> { pointerIndex = ev.findPointerIndex(activePointerId) - if (pointerIndex < 0) return false - - val pos = if (vertical) ev.getY(pointerIndex) else ev.getX(pointerIndex) - startDragging(pos) - - if (!isBeingDragged) return false - - val overscroll = getDiff(pos) * DRAG_RATE - if (overscroll.absoluteValue <= 0) return false - - parent.requestDisallowInterceptTouchEvent(true) - - if (vertical) { - val dragDistance = - Resources.getSystem().displayMetrics.heightPixels / dragDivider - performSwiping(overscroll, dragDistance, topBeingSwiped, bottomBeingSwiped) - } else { - val dragDistance = - Resources.getSystem().displayMetrics.widthPixels / dragDivider - performSwiping(overscroll, dragDistance, leftBeingSwiped, rightBeingSwiped) + if (pointerIndex >= 0) { + val pos = if (vertical) ev.getY(pointerIndex) else ev.getX(pointerIndex) + startDragging(pos) + if (isBeingDragged) handleDrag(pos) } } - MotionEvent.ACTION_POINTER_DOWN -> { pointerIndex = ev.actionIndex - if (pointerIndex < 0) { - //("Got ACTION_POINTER_DOWN event but have an invalid action index.") - return false - } - activePointerId = ev.getPointerId(pointerIndex) + if (pointerIndex >= 0) activePointerId = ev.getPointerId(pointerIndex) } - MotionEvent.ACTION_POINTER_UP -> onSecondaryPointerUp(ev) MotionEvent.ACTION_UP -> { - if (vertical) { - topBeingSwiped(0f) - bottomBeingSwiped(0f) - } else { - rightBeingSwiped(0f) - leftBeingSwiped(0f) - } + resetSwipes() pointerIndex = ev.findPointerIndex(activePointerId) - if (pointerIndex < 0) { - //("Got ACTION_UP event but don't have an active pointer id.") - return false - } - if (isBeingDragged) { - val pos = if (vertical) ev.getY(pointerIndex) else ev.getX(pointerIndex) - val overscroll = getDiff(pos) * DRAG_RATE - isBeingDragged = false - finishSpinner(overscroll) - } + if (pointerIndex >= 0) finishSpinner(if (vertical) ev.getY(pointerIndex) else ev.getX(pointerIndex)) activePointerId = INVALID_POINTER return false } - MotionEvent.ACTION_CANCEL -> return false } return true } - private fun getDiff(pos: Float) = when (scrollPos) { - ScrollPosition.None -> 0f - ScrollPosition.Start, ScrollPosition.Both -> pos - initialMotion - ScrollPosition.End -> initialMotion - pos - } - private fun startDragging(pos: Float) { - val posDiff = getDiff(pos).absoluteValue + val posDiff = if ((vertical && verticalPos == VerticalPosition.Top) || (!vertical && horizontalPos == HorizontalPosition.Left)) + pos - initialDown + else + initialDown - pos if (posDiff > touchSlop && !isBeingDragged) { initialMotion = initialDown + touchSlop isBeingDragged = true } } - private fun performSwiping( - overscrollDistance: Float, - totalDragDistance: Int, - startBlock: (Float) -> Unit, - endBlock: (Float) -> Unit - ) { - val distance = overscrollDistance * 2 / totalDragDistance - when (scrollPos) { - ScrollPosition.Start -> startBlock(distance) - ScrollPosition.End -> endBlock(distance) - ScrollPosition.Both -> { - startBlock(distance) - endBlock(-distance) + private fun handleDrag(pos: Float) { + val overscroll = (pos - initialMotion) * DRAG_RATE + if (overscroll > 0) { // Keep this check + parent.requestDisallowInterceptTouchEvent(true) + if (vertical) { + val totalDragDistance = Resources.getSystem().displayMetrics.heightPixels / dragDivider + if (verticalPos == VerticalPosition.Top) + topBeingSwiped.invoke(overscroll * 2 / totalDragDistance) + else + bottomBeingSwiped.invoke(overscroll * 2 / totalDragDistance) + } else { + val totalDragDistance = Resources.getSystem().displayMetrics.widthPixels / dragDivider + if (horizontalPos == HorizontalPosition.Left) + leftBeingSwiped.invoke(overscroll / totalDragDistance) + else + rightBeingSwiped.invoke(overscroll / totalDragDistance) } - else -> {} } } - private fun performSwipe( - overscrollDistance: Float, - totalDragDistance: Int, - startBlock: () -> Unit, - endBlock: () -> Unit - ) { - fun check(distance: Float, block: () -> Unit) { - if (distance * 2 > totalDragDistance) - block.invoke() - } - when (scrollPos) { - ScrollPosition.Start -> check(overscrollDistance) { startBlock() } - ScrollPosition.End -> check(overscrollDistance) { endBlock() } - ScrollPosition.Both -> { - check(overscrollDistance) { startBlock() } - check(-overscrollDistance) { endBlock() } - } - - else -> {} + private fun resetSwipes() { + if (vertical) { + topBeingSwiped.invoke(0f) + bottomBeingSwiped.invoke(0f) + } else { + rightBeingSwiped.invoke( 0f) + leftBeingSwiped.invoke(0f) } } - + private fun finishSpinner(overscrollDistance: Float) { if (vertical) { val totalDragDistance = Resources.getSystem().displayMetrics.heightPixels / dragDivider - performSwipe(overscrollDistance, totalDragDistance, onTopSwiped, onBottomSwiped) + val swipeDistance = abs(overscrollDistance - initialMotion) + if (swipeDistance > totalDragDistance) { + if (verticalPos == VerticalPosition.Top) + onTopSwiped.invoke() + else + onBottomSwiped.invoke() + } } else { val totalDragDistance = Resources.getSystem().displayMetrics.widthPixels / dragDivider - performSwipe(overscrollDistance, totalDragDistance, onLeftSwiped, onRightSwiped) + val swipeDistance = abs(overscrollDistance - initialMotion) + if (swipeDistance > totalDragDistance) { + if (horizontalPos == HorizontalPosition.Left) + onLeftSwiped.invoke() + else + onRightSwiped.invoke() + } } } -} \ No newline at end of file +}