Add scale gesture to stories.
This commit is contained in:
parent
9a21f5abca
commit
ffa249885e
4 changed files with 82 additions and 10 deletions
|
@ -14,6 +14,7 @@ import android.os.Build
|
|||
import android.os.Bundle
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.ScaleGestureDetector
|
||||
import android.view.View
|
||||
import android.view.animation.Interpolator
|
||||
import android.widget.FrameLayout
|
||||
|
@ -34,6 +35,7 @@ import io.reactivex.rxjava3.core.Observable
|
|||
import org.signal.core.util.DimensionUnit
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.animation.AnimationCompleteListener
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView
|
||||
import org.thoughtcrime.securesms.components.segmentedprogressbar.SegmentedProgressBar
|
||||
import org.thoughtcrime.securesms.components.segmentedprogressbar.SegmentedProgressBarListener
|
||||
|
@ -226,9 +228,24 @@ class StoryViewerPageFragment :
|
|||
)
|
||||
)
|
||||
|
||||
val scaleListener = StoryScaleListener(
|
||||
viewModel, sharedViewModel, card
|
||||
)
|
||||
|
||||
val scaleDetector = ScaleGestureDetector(
|
||||
requireContext(),
|
||||
scaleListener
|
||||
)
|
||||
|
||||
cardWrapper.setOnInterceptTouchEventListener { !storySlate.state.hasClickableContent && childFragmentManager.findFragmentById(R.id.story_content_container) !is StoryTextPostPreviewFragment }
|
||||
cardWrapper.setOnTouchListener { _, event ->
|
||||
val result = gestureDetector.onTouchEvent(event)
|
||||
scaleDetector.onTouchEvent(event)
|
||||
val result = if (scaleDetector.isInProgress || scaleListener.isPerformingEndAnimation) {
|
||||
true
|
||||
} else {
|
||||
gestureDetector.onTouchEvent(event)
|
||||
}
|
||||
|
||||
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||
viewModel.setIsUserTouching(true)
|
||||
} else if (event.actionMasked == MotionEvent.ACTION_UP || event.actionMasked == MotionEvent.ACTION_CANCEL) {
|
||||
|
@ -1026,6 +1043,53 @@ class StoryViewerPageFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private class StoryScaleListener(
|
||||
val viewModel: StoryViewerPageViewModel,
|
||||
val sharedViewModel: StoryViewerViewModel,
|
||||
val card: View
|
||||
) : ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||
|
||||
private var scaleFactor = 1f
|
||||
|
||||
var isPerformingEndAnimation: Boolean = false
|
||||
private set
|
||||
|
||||
override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
|
||||
viewModel.setIsUserScaling(true)
|
||||
sharedViewModel.setIsChildScrolling(true)
|
||||
card.animate().cancel()
|
||||
card.apply {
|
||||
pivotX = detector.focusX
|
||||
pivotY = detector.focusY
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
||||
scaleFactor *= detector.scaleFactor
|
||||
|
||||
card.apply {
|
||||
scaleX = max(scaleFactor, 1f)
|
||||
scaleY = max(scaleFactor, 1f)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onScaleEnd(detector: ScaleGestureDetector) {
|
||||
scaleFactor = 1f
|
||||
isPerformingEndAnimation = true
|
||||
card.animate().scaleX(1f).scaleY(1f).setListener(object : AnimationCompleteListener() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
isPerformingEndAnimation = false
|
||||
viewModel.setIsUserScaling(false)
|
||||
sharedViewModel.setIsChildScrolling(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private class StoryGestureListener(
|
||||
private val container: View,
|
||||
private val onGoToNext: () -> Unit,
|
||||
|
@ -1044,7 +1108,7 @@ class StoryViewerPageFragment :
|
|||
|
||||
private val maxSlide = DimensionUnit.DP.toPixels(56f * 2)
|
||||
|
||||
override fun onDown(e: MotionEvent?): Boolean {
|
||||
override fun onDown(e: MotionEvent): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -259,6 +259,10 @@ class StoryViewerPageViewModel(
|
|||
storyViewerPlaybackStore.update { it.copy(isDisplayingInfoDialog = isDisplayingInfoDialog) }
|
||||
}
|
||||
|
||||
fun setIsUserScaling(isUserScaling: Boolean) {
|
||||
storyViewerPlaybackStore.update { it.copy(isUserScaling = isUserScaling) }
|
||||
}
|
||||
|
||||
private fun resolveSwipeToReplyState(state: StoryViewerPageState, index: Int): StoryViewerPageState.ReplyState {
|
||||
if (index !in state.posts.indices) {
|
||||
return StoryViewerPageState.ReplyState.NONE
|
||||
|
|
|
@ -19,11 +19,12 @@ data class StoryViewerPlaybackState(
|
|||
val isDisplayingFirstTimeNavigation: Boolean = false,
|
||||
val isDisplayingInfoDialog: Boolean = false,
|
||||
val isUserLongTouching: Boolean = false,
|
||||
val isUserScrollingChild: Boolean = false
|
||||
val isUserScrollingChild: Boolean = false,
|
||||
val isUserScaling: Boolean = false
|
||||
) {
|
||||
val hideChromeImmediate: Boolean = isRunningSharedElementAnimation
|
||||
|
||||
val hideChrome: Boolean = isRunningSharedElementAnimation || isUserLongTouching || isUserScrollingChild
|
||||
val hideChrome: Boolean = isRunningSharedElementAnimation || isUserLongTouching || isUserScrollingChild || isUserScaling
|
||||
|
||||
val isPaused: Boolean = !areSegmentsInitialized ||
|
||||
isUserTouching ||
|
||||
|
@ -42,5 +43,6 @@ data class StoryViewerPlaybackState(
|
|||
isDisplayingReactionAnimation ||
|
||||
isRunningSharedElementAnimation ||
|
||||
isDisplayingFirstTimeNavigation ||
|
||||
isDisplayingInfoDialog
|
||||
isDisplayingInfoDialog ||
|
||||
isUserScaling
|
||||
}
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:viewBindingIgnore="true"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".stories.viewer.StoryViewerActivity">
|
||||
tools:context=".stories.viewer.StoryViewerActivity"
|
||||
tools:viewBindingIgnore="true">
|
||||
|
||||
<org.thoughtcrime.securesms.util.views.TouchInterceptingFrameLayout
|
||||
android:id="@+id/story_content_card_touch_interceptor"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="9:16"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
@ -54,10 +56,10 @@
|
|||
|
||||
<org.thoughtcrime.securesms.stories.StoryVolumeOverlayView
|
||||
android:id="@+id/story_volume_overlay"
|
||||
android:alpha="0"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:alpha="0" />
|
||||
|
||||
</org.thoughtcrime.securesms.util.views.TouchInterceptingFrameLayout>
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue