Implement slide to close in Story viewer.

fork-5.53.8
Alex Hart 2022-03-23 09:01:22 -03:00 zatwierdzone przez Greyson Parrelli
rodzic 403958fed3
commit db309b7930
3 zmienionych plików z 61 dodań i 3 usunięć

Wyświetl plik

@ -36,6 +36,10 @@ class StoryViewerFragment : Fragment(R.layout.stories_viewer_fragment), StoryVie
val adapter = StoryViewerPagerAdapter(this, storyId) val adapter = StoryViewerPagerAdapter(this, storyId)
storyPager.adapter = adapter storyPager.adapter = adapter
viewModel.isChildScrolling.observe(viewLifecycleOwner) {
storyPager.isUserInputEnabled = !it
}
viewModel.state.observe(viewLifecycleOwner) { state -> viewModel.state.observe(viewLifecycleOwner) { state ->
adapter.setPages(state.pages) adapter.setPages(state.pages)
if (state.pages.isNotEmpty() && storyPager.currentItem != state.page) { if (state.pages.isNotEmpty() && storyPager.currentItem != state.page) {

Wyświetl plik

@ -22,6 +22,9 @@ class StoryViewerViewModel(
private val scrollStatePublisher: MutableLiveData<Boolean> = MutableLiveData(false) private val scrollStatePublisher: MutableLiveData<Boolean> = MutableLiveData(false)
val isScrolling: LiveData<Boolean> = scrollStatePublisher val isScrolling: LiveData<Boolean> = scrollStatePublisher
private val childScrollStatePublisher: MutableLiveData<Boolean> = MutableLiveData(false)
val isChildScrolling: LiveData<Boolean> = childScrollStatePublisher
init { init {
refresh() refresh()
} }
@ -103,6 +106,10 @@ class StoryViewerViewModel(
} }
} }
fun setIsChildScrolling(isChildScrolling: Boolean) {
childScrollStatePublisher.value = isChildScrolling
}
class Factory( class Factory(
private val startRecipientId: RecipientId, private val startRecipientId: RecipientId,
private val repository: StoryViewerRepository private val repository: StoryViewerRepository

Wyświetl plik

@ -10,15 +10,16 @@ import android.os.Bundle
import android.view.GestureDetector import android.view.GestureDetector
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.animation.Interpolator
import android.widget.TextView import android.widget.TextView
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.GestureDetectorCompat import androidx.core.view.GestureDetectorCompat
import androidx.core.view.animation.PathInterpolatorCompat
import androidx.core.view.doOnNextLayout import androidx.core.view.doOnNextLayout
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.setFragmentResultListener
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Observable
@ -61,6 +62,7 @@ import org.thoughtcrime.securesms.util.visible
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.max
class StoryViewerPageFragment : class StoryViewerPageFragment :
Fragment(R.layout.stories_viewer_fragment_page), Fragment(R.layout.stories_viewer_fragment_page),
@ -148,7 +150,9 @@ class StoryViewerPageFragment :
cardWrapper, cardWrapper,
viewModel::goToNextPost, viewModel::goToNextPost,
viewModel::goToPreviousPost, viewModel::goToPreviousPost,
this::startReply this::startReply,
sharedViewModel = sharedViewModel
) )
) )
@ -161,6 +165,18 @@ class StoryViewerPageFragment :
} else if (event.actionMasked == MotionEvent.ACTION_UP || event.actionMasked == MotionEvent.ACTION_CANCEL) { } else if (event.actionMasked == MotionEvent.ACTION_UP || event.actionMasked == MotionEvent.ACTION_CANCEL) {
viewModel.setIsUserTouching(false) viewModel.setIsUserTouching(false)
showChrome() showChrome()
val canCloseFromHorizontalSlide = requireView().translationX > DimensionUnit.DP.toPixels(56f)
val canCloseFromVerticalSlide = requireView().translationY > DimensionUnit.DP.toPixels(56f)
if ((canCloseFromHorizontalSlide || canCloseFromVerticalSlide) && event.actionMasked == MotionEvent.ACTION_UP) {
requireActivity().finish()
} else {
requireView().animate()
.setInterpolator(StoryGestureListener.INTERPOLATOR)
.setDuration(100)
.translationX(0f)
.translationY(0f)
}
} }
result result
@ -664,18 +680,49 @@ class StoryViewerPageFragment :
private val container: View, private val container: View,
private val onGoToNext: () -> Unit, private val onGoToNext: () -> Unit,
private val onGoToPrevious: () -> Unit, private val onGoToPrevious: () -> Unit,
private val onReplyToPost: () -> Unit private val onReplyToPost: () -> Unit,
private val viewToTranslate: View = container.parent as View,
private val sharedViewModel: StoryViewerViewModel
) : GestureDetector.SimpleOnGestureListener() { ) : GestureDetector.SimpleOnGestureListener() {
companion object { companion object {
private const val BOUNDARY_NEXT = 0.80f private const val BOUNDARY_NEXT = 0.80f
private const val BOUNDARY_PREV = 1f - BOUNDARY_NEXT 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 { override fun onDown(e: MotionEvent?): Boolean {
return true return true
} }
override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
val isFirstStory = sharedViewModel.state.value?.page == 0
val isXMagnitudeGreaterThanYMagnitude = abs(distanceX) > abs(distanceY) || viewToTranslate.translationX > 0f
val isFirstAndHasYTranslationOrNegativeY = isFirstStory && (viewToTranslate.translationY > 0f || distanceY < 0f)
sharedViewModel.setIsChildScrolling(isXMagnitudeGreaterThanYMagnitude || isFirstAndHasYTranslationOrNegativeY)
if (isFirstStory) {
val delta = max(0f, (e2.rawY - e1.rawY)) / 3f
val percent = INTERPOLATOR.getInterpolation(delta/maxSlide)
val distance = maxSlide * percent
viewToTranslate.animate().cancel()
viewToTranslate.translationY = distance
}
val delta = max(0f, (e2.rawX - e1.rawX)) / 3f
val percent = INTERPOLATOR.getInterpolation(delta / maxSlide)
val distance = maxSlide * percent
viewToTranslate.animate().cancel()
viewToTranslate.translationX = distance
return true
}
override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean { override fun onFling(e1: MotionEvent?, e2: MotionEvent?, velocityX: Float, velocityY: Float): Boolean {
val isSideSwipe = abs(velocityX) > abs(velocityY) val isSideSwipe = abs(velocityX) > abs(velocityY)
if (!isSideSwipe) { if (!isSideSwipe) {