Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import android.text.format.DateUtils
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.Surface
import android.view.View
import android.view.ViewGroup
Expand Down Expand Up @@ -87,7 +88,6 @@ import kotlin.math.roundToInt
import com.lagradost.cloudstream3.utils.BackPressedCallbackHelper.attachBackPressedCallback
import com.lagradost.cloudstream3.utils.BackPressedCallbackHelper.detachBackPressedCallback


const val MINIMUM_SEEK_TIME = 7000L // when swipe seeking
const val MINIMUM_VERTICAL_SWIPE = 2.0f // in percentage
const val MINIMUM_HORIZONTAL_SWIPE = 2.0f // in percentage
Expand Down Expand Up @@ -255,6 +255,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
animateLayoutChanges()
}

@OptIn(androidx.media3.common.util.UnstableApi::class)
private fun animateLayoutChangesForSubtitles() =
// Post here as bottomPlayerBar is gone the first frame => bottomPlayerBar.height = 0
playerBinding?.bottomPlayerBar?.post {
Expand All @@ -275,6 +276,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
}
}

@OptIn(UnstableApi::class)
protected fun animateLayoutChanges() {
if (isLayout(PHONE)) { // isEnabled also disables the onKeyDown
playerBinding?.exoProgress?.isEnabled = isShowing // Prevent accidental clicks/drags
Expand Down Expand Up @@ -1056,6 +1058,13 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
}
}

private var scaleGestureDetector: ScaleGestureDetector? = null
private var scaleFactor = 1f
private var lastPanX = 0f
private var lastPanY = 0f
private var isPanning = false

@OptIn(androidx.media3.common.util.UnstableApi::class)
@SuppressLint("SetTextI18n")
private fun handleMotionEvent(view: View?, event: MotionEvent?): Boolean {
if (event == null || view == null) return false
Expand All @@ -1065,7 +1074,69 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
playerBinding?.apply {
playerIntroPlay.isGone = true

when (event.action) {
// Gesture detectors for zoom & pan
if (scaleGestureDetector == null) {
scaleGestureDetector = ScaleGestureDetector(view.context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
scaleFactor *= detector.scaleFactor
scaleFactor = scaleFactor.coerceIn(1.0f, 4.0f)
playerView?.videoSurfaceView?.let { videoView ->
videoView.scaleX = scaleFactor
videoView.scaleY = scaleFactor

val maxTransX = (videoView.width * scaleFactor - videoView.width) / 2f
val maxTransY = (videoView.height * scaleFactor - videoView.height) / 2f
videoView.translationX = videoView.translationX.coerceIn(-maxTransX, maxTransX)
videoView.translationY = videoView.translationY.coerceIn(-maxTransY, maxTransY)
}
return true
}
})
}

// Handle pan with two fingers
if (event.pointerCount == 2) {
when (event.actionMasked) {
MotionEvent.ACTION_POINTER_DOWN -> {
lastPanX = (event.getX(0) + event.getX(1)) / 2f
lastPanY = (event.getY(0) + event.getY(1)) / 2f
isPanning = true
}
MotionEvent.ACTION_MOVE -> {
if (isPanning) {
val newX = (event.getX(0) + event.getX(1)) / 2f
val newY = (event.getY(0) + event.getY(1)) / 2f
val dx = newX - lastPanX
val dy = newY - lastPanY

playerView?.videoSurfaceView?.let { videoView ->
val maxTransX = (videoView.width * scaleFactor - videoView.width) / 2f
val maxTransY = (videoView.height * scaleFactor - videoView.height) / 2f

videoView.translationX = (videoView.translationX + dx).coerceIn(-maxTransX, maxTransX)
videoView.translationY = (videoView.translationY + dy).coerceIn(-maxTransY, maxTransY)
}

lastPanX = newX
lastPanY = newY
}
}
MotionEvent.ACTION_POINTER_UP, MotionEvent.ACTION_UP -> {
isPanning = false
lastPanX = 0f
lastPanY = 0f
currentTouchStart = null
currentTouchLast = null
currentTouchAction = null
}
}
scaleGestureDetector?.onTouchEvent(event)
return true
}

scaleGestureDetector?.onTouchEvent(event)

when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
// validates if the touch is inside of the player area
isCurrentTouchValid = view.isValidTouch(currentTouch.x, currentTouch.y)
Expand Down Expand Up @@ -1101,7 +1172,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
player.setPlaybackSpeed(DataStoreHelper.playBackSpeed)
showOrHideSpeedUp(false)
}
if (isCurrentTouchValid && !isLocked && isFullScreenPlayer) {
if (isCurrentTouchValid && !isLocked && isFullScreenPlayer && !isPanning) {
// seek time
if (swipeHorizontalEnabled && currentTouchAction == TouchAction.Time) {
val startTime = currentTouchStartPlayerTime
Expand Down Expand Up @@ -1197,7 +1268,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
if (hasTriggeredSpeedUp) {
return true
}
if (startTouch != null && isCurrentTouchValid && !isLocked && isFullScreenPlayer) {
if (startTouch != null && isCurrentTouchValid && !isLocked && isFullScreenPlayer && !isPanning) {
// action is unassigned and can therefore be assigned

if (currentTouchAction == null) {
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/layout/fragment_player.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:show_timeout="0" />
app:show_timeout="0"
app:surface_type="texture_view" />

<FrameLayout
android:id="@+id/player_loading_overlay"
Expand Down