Support a swipe down to dismiss video (#2879)
* Support a swipe down to dismiss video Images can already be dismissed with a swipe, this adds the same functionality to videos. - Add a VideoActionsListener interface for the hosting activity to dismiss the fragment - Add a gesture listener for swipes - Dismiss the fragment if a swipe has a greated Y component than X Fixes https://github.com/tuskyapp/Tusky/issues/2833 * Scale the video view when dragging Provides identical visual feedback to the same operation on images.
This commit is contained in:
parent
4de778d7d4
commit
64a06bfbe2
2 changed files with 65 additions and 1 deletions
|
@ -52,6 +52,7 @@ import com.keylesspalace.tusky.components.viewthread.ViewThreadActivity
|
|||
import com.keylesspalace.tusky.databinding.ActivityViewMediaBinding
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.fragment.ViewImageFragment
|
||||
import com.keylesspalace.tusky.fragment.ViewVideoFragment
|
||||
import com.keylesspalace.tusky.pager.ImagePagerAdapter
|
||||
import com.keylesspalace.tusky.pager.SingleImagePagerAdapter
|
||||
import com.keylesspalace.tusky.util.getTemporaryMediaFilename
|
||||
|
@ -68,7 +69,7 @@ import java.util.Locale
|
|||
|
||||
typealias ToolbarVisibilityListener = (isVisible: Boolean) -> Unit
|
||||
|
||||
class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener {
|
||||
class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener, ViewVideoFragment.VideoActionsListener {
|
||||
|
||||
private val binding by viewBinding(ActivityViewMediaBinding::inflate)
|
||||
|
||||
|
|
|
@ -18,27 +18,36 @@ package com.keylesspalace.tusky.fragment
|
|||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.text.method.ScrollingMovementMethod
|
||||
import android.view.GestureDetector
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.MediaController
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
import com.keylesspalace.tusky.ViewMediaActivity
|
||||
import com.keylesspalace.tusky.databinding.FragmentViewVideoBinding
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import com.keylesspalace.tusky.view.ExposedPlayPauseVideoView
|
||||
import kotlin.math.abs
|
||||
|
||||
class ViewVideoFragment : ViewMediaFragment() {
|
||||
interface VideoActionsListener {
|
||||
fun onDismiss()
|
||||
}
|
||||
|
||||
private var _binding: FragmentViewVideoBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private lateinit var videoActionsListener: VideoActionsListener
|
||||
private lateinit var toolbar: View
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private val hideToolbar = Runnable {
|
||||
|
@ -52,6 +61,11 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
private lateinit var mediaController: MediaController
|
||||
private var isAudio = false
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
videoActionsListener = context as VideoActionsListener
|
||||
}
|
||||
|
||||
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
|
||||
// Start/pause/resume video playback as fragment is shown/hidden
|
||||
super.setUserVisibleHint(isVisibleToUser)
|
||||
|
@ -168,6 +182,7 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
return binding.root
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val attachment = arguments?.getParcelable<Attachment>(ARG_ATTACHMENT)
|
||||
|
@ -177,6 +192,54 @@ class ViewVideoFragment : ViewMediaFragment() {
|
|||
}
|
||||
val url = attachment.url
|
||||
isAudio = attachment.type == Attachment.Type.AUDIO
|
||||
|
||||
val gestureDetector = GestureDetectorCompat(
|
||||
requireContext(),
|
||||
object : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onDown(event: MotionEvent): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onFling(
|
||||
e1: MotionEvent,
|
||||
e2: MotionEvent,
|
||||
velocityX: Float,
|
||||
velocityY: Float
|
||||
): Boolean {
|
||||
if (abs(velocityY) > abs(velocityX)) {
|
||||
videoActionsListener.onDismiss()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
var lastY = 0f
|
||||
binding.root.setOnTouchListener { _, event ->
|
||||
if (event.action == MotionEvent.ACTION_DOWN) {
|
||||
lastY = event.rawY
|
||||
} else if (event.pointerCount == 1 && event.action == MotionEvent.ACTION_MOVE) {
|
||||
val diff = event.rawY - lastY
|
||||
if (binding.videoView.translationY != 0f || abs(diff) > 40) {
|
||||
binding.videoView.translationY += diff
|
||||
val scale = (-abs(binding.videoView.translationY) / 720 + 1).coerceAtLeast(0.5f)
|
||||
binding.videoView.scaleY = scale
|
||||
binding.videoView.scaleX = scale
|
||||
lastY = event.rawY
|
||||
return@setOnTouchListener true
|
||||
}
|
||||
} else if (event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL) {
|
||||
if (abs(binding.videoView.translationY) > 180) {
|
||||
videoActionsListener.onDismiss()
|
||||
} else {
|
||||
binding.videoView.animate().translationY(0f).scaleX(1f).scaleY(1f).start()
|
||||
}
|
||||
}
|
||||
|
||||
gestureDetector.onTouchEvent(event)
|
||||
}
|
||||
|
||||
finalizeViewSetup(url, attachment.previewUrl, attachment.description)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue