add function to subtitle layer

This commit is contained in:
2026-10-26 12:02:41 +08:00
parent fe21617c63
commit 9b7c7d726b
2 changed files with 73 additions and 1 deletions

View File

@@ -4,6 +4,7 @@ import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.GestureDetector import android.view.GestureDetector
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration import android.view.ViewConfiguration
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.media3.ui.PlayerView import androidx.media3.ui.PlayerView
@@ -123,6 +124,26 @@ class OralTrainerPlayerView @JvmOverloads constructor(
verticalSwipeListener = listener verticalSwipeListener = listener
} }
/** Forwards touches from an overlay (for example a subtitle layer) as player gestures. */
fun forwardGestureFrom(source: View, event: MotionEvent): Boolean {
if (controller == null || !gestureControls.enabled) {
return false
}
val sourceLocation = IntArray(2).also(source::getLocationOnScreen)
val playerLocation = IntArray(2).also(::getLocationOnScreen)
val playerEvent = MotionEvent.obtain(event).apply {
offsetLocation(
(playerLocation[0] - sourceLocation[0]).toFloat(),
(playerLocation[1] - sourceLocation[1]).toFloat(),
)
}
return try {
handleLongPressSpeed(playerEvent) || gestureDetector.onTouchEvent(playerEvent)
} finally {
playerEvent.recycle()
}
}
override fun onTouchEvent(event: MotionEvent): Boolean { override fun onTouchEvent(event: MotionEvent): Boolean {
return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event) return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event)
} }

View File

@@ -8,6 +8,7 @@ import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.InsetDrawable import android.graphics.drawable.InsetDrawable
import android.graphics.Typeface import android.graphics.Typeface
import android.text.TextUtils import android.text.TextUtils
import android.view.MotionEvent
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -166,7 +167,28 @@ internal class PlayerChrome(
addView(createPlayerView()) addView(createPlayerView())
}, },
) )
val subtitleContainer = LinearLayout(activity).apply { val subtitleContainer = object : LinearLayout(activity) {
private var forwardTouchToPlayer = false
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
forwardTouchToPlayer = !isTouchOverInteractiveControl(this, event)
}
val forwarded = if (forwardTouchToPlayer) {
bindings.playerView.forwardGestureFrom(this, event)
} else {
false
}
val handled = forwarded || super.dispatchTouchEvent(event)
if (event.actionMasked == MotionEvent.ACTION_UP ||
event.actionMasked == MotionEvent.ACTION_CANCEL
) {
forwardTouchToPlayer = false
}
return handled
}
}.apply {
orientation = LinearLayout.VERTICAL orientation = LinearLayout.VERTICAL
layoutParams = FrameLayout.LayoutParams( layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@@ -503,4 +525,33 @@ internal class PlayerChrome(
} }
view.startAnimation(animation) view.startAnimation(animation)
} }
/** Lets controls own their touches while the subtitle background drives player gestures. */
private fun isTouchOverInteractiveControl(root: View, event: MotionEvent): Boolean =
hasInteractiveTouchTarget(root, event.rawX, event.rawY)
private fun hasInteractiveTouchTarget(view: View, screenX: Float, screenY: Float): Boolean {
if (view.visibility != View.VISIBLE || !view.isEnabled) {
return false
}
if (view.isClickable || view.isLongClickable || view.isContextClickable) {
val location = IntArray(2).also(view::getLocationOnScreen)
if (screenX >= location[0] &&
screenX < location[0] + view.width &&
screenY >= location[1] &&
screenY < location[1] + view.height
) {
return true
}
}
if (view !is ViewGroup) {
return false
}
for (index in view.childCount - 1 downTo 0) {
if (hasInteractiveTouchTarget(view.getChildAt(index), screenX, screenY)) {
return true
}
}
return false
}
} }