diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt index fa33d42fbb..51b6544c59 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt @@ -17,6 +17,7 @@ import android.view.GestureDetector import android.view.MotionEvent import android.view.ScaleGestureDetector import android.view.View +import android.view.ViewConfiguration import android.view.animation.Interpolator import android.widget.FrameLayout import android.widget.TextView @@ -209,12 +210,17 @@ class StoryViewerPageFragment : requireActivity().onBackPressed() } + val singleTapHandler = SingleTapHandler( + cardWrapper, + viewModel::goToNextPost, + viewModel::goToPreviousPost, + ) + val gestureDetector = GestureDetectorCompat( requireContext(), StoryGestureListener( cardWrapper, - viewModel::goToNextPost, - viewModel::goToPreviousPost, + singleTapHandler, this::startReply, requireListener()::onContentTranslation, sharedViewModel = sharedViewModel @@ -242,6 +248,10 @@ class StoryViewerPageFragment : if (event.actionMasked == MotionEvent.ACTION_DOWN) { viewModel.setIsUserTouching(true) } else if (event.actionMasked == MotionEvent.ACTION_UP || event.actionMasked == MotionEvent.ACTION_CANCEL) { + if (event.actionMasked == MotionEvent.ACTION_UP) { + singleTapHandler.onActionUp(event) + } + viewModel.setIsUserTouching(false) val canCloseFromHorizontalSlide = requireView().translationX > DimensionUnit.DP.toPixels(56f) @@ -1053,6 +1063,72 @@ class StoryViewerPageFragment : ) } + class SingleTapHandler( + private val container: View, + private val onGoToNext: () -> Unit, + private val onGoToPrevious: () -> Unit + ) { + + companion object { + private const val BOUNDARY_NEXT = 0.80f + private const val BOUNDARY_PREV = 1f - BOUNDARY_NEXT + } + + private var tapStart: Long = 0L + + fun onGestureHappened() { + tapStart = 0L + } + + fun onDown() { + tapStart = System.currentTimeMillis() + } + + fun onActionUp(e: MotionEvent) { + if (System.currentTimeMillis() - tapStart > ViewConfiguration.getTapTimeout()) { + return + } + + if (e.x < container.measuredWidth * getLeftBoundary()) { + performLeftAction() + } else if (e.x > container.measuredWidth - (container.measuredWidth * getRightBoundary())) { + performRightAction() + } + } + + private fun performLeftAction() { + if (container.layoutDirection == View.LAYOUT_DIRECTION_RTL) { + onGoToNext() + } else { + onGoToPrevious() + } + } + + private fun performRightAction() { + if (container.layoutDirection == View.LAYOUT_DIRECTION_RTL) { + onGoToPrevious() + } else { + onGoToNext() + } + } + + private fun getLeftBoundary(): Float { + return if (container.layoutDirection == View.LAYOUT_DIRECTION_LTR) { + BOUNDARY_PREV + } else { + BOUNDARY_NEXT + } + } + + private fun getRightBoundary(): Float { + return if (container.layoutDirection == View.LAYOUT_DIRECTION_LTR) { + BOUNDARY_NEXT + } else { + BOUNDARY_PREV + } + } + } + companion object { private val TAG = Log.tag(StoryViewerPageFragment::class.java) @@ -1123,8 +1199,7 @@ class StoryViewerPageFragment : private class StoryGestureListener( private val container: View, - private val onGoToNext: () -> Unit, - private val onGoToPrevious: () -> Unit, + private val singleTapHandler: SingleTapHandler, private val onReplyToPost: () -> Unit, private val onContentTranslation: (Float, Float) -> Unit, private val viewToTranslate: View = container.parent as View, @@ -1132,15 +1207,13 @@ class StoryViewerPageFragment : ) : GestureDetector.SimpleOnGestureListener() { companion object { - private const val BOUNDARY_NEXT = 0.80f - private const val BOUNDARY_PREV = 1f - BOUNDARY_NEXT - val INTERPOLATOR: Interpolator = PathInterpolatorCompat.create(0.4f, 0f, 0.2f, 1f) } private val maxSlide = DimensionUnit.DP.toPixels(56f * 2) override fun onDown(e: MotionEvent): Boolean { + singleTapHandler.onDown() return true } @@ -1179,6 +1252,7 @@ class StoryViewerPageFragment : onContentTranslation(viewToTranslate.translationX, viewToTranslate.translationY) + singleTapHandler.onGestureHappened() return true } @@ -1200,52 +1274,9 @@ class StoryViewerPageFragment : onReplyToPost() } + singleTapHandler.onGestureHappened() return true } - - private fun getLeftBoundary(): Float { - return if (container.layoutDirection == View.LAYOUT_DIRECTION_LTR) { - BOUNDARY_PREV - } else { - BOUNDARY_NEXT - } - } - - private fun getRightBoundary(): Float { - return if (container.layoutDirection == View.LAYOUT_DIRECTION_LTR) { - BOUNDARY_NEXT - } else { - BOUNDARY_PREV - } - } - - override fun onSingleTapUp(e: MotionEvent): Boolean { - if (e.x < container.measuredWidth * getLeftBoundary()) { - performLeftAction() - return true - } else if (e.x > container.measuredWidth - (container.measuredWidth * getRightBoundary())) { - performRightAction() - return true - } - - return false - } - - private fun performLeftAction() { - if (container.layoutDirection == View.LAYOUT_DIRECTION_RTL) { - onGoToNext() - } else { - onGoToPrevious() - } - } - - private fun performRightAction() { - if (container.layoutDirection == View.LAYOUT_DIRECTION_RTL) { - onGoToPrevious() - } else { - onGoToNext() - } - } } private class FallbackPhotoProvider : Recipient.FallbackPhotoProvider() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageViewModel.kt index 27790553af..62803161b0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageViewModel.kt @@ -134,6 +134,7 @@ class StoryViewerPageViewModel( } val postIndex = store.state.selectedPostIndex + val nextUnreadPost: StoryPost? = getNextUnreadPost(store.state.posts.drop(postIndex + 1)) when { nextUnreadPost == null && args.isJumpForwardToUnviewed -> setSelectedPostIndex(store.state.posts.size)