layout improvements
This commit is contained in:
@@ -15,6 +15,8 @@ import com.dtourolle.jellytau.player.JellyTauPlayer
|
||||
object VideoOverlayManager {
|
||||
|
||||
private var attachedSurfaceView: SurfaceView? = null
|
||||
private var contentLayoutListener: android.view.View.OnLayoutChangeListener? = null
|
||||
private var listenerContentView: ViewGroup? = null
|
||||
|
||||
/**
|
||||
* Attach the video SurfaceView to the Activity's content view.
|
||||
@@ -51,6 +53,23 @@ object VideoOverlayManager {
|
||||
contentView.addView(surfaceView, 0, layoutParams)
|
||||
attachedSurfaceView = surfaceView
|
||||
|
||||
// Re-fit the video whenever the content view's bounds change (e.g. on
|
||||
// device rotation) so the video is letterboxed to fit instead of being
|
||||
// stretched/cropped by the MATCH_PARENT surface.
|
||||
removeLayoutListener()
|
||||
val listener = android.view.View.OnLayoutChangeListener {
|
||||
_, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
|
||||
if (right - left != oldRight - oldLeft || bottom - top != oldBottom - oldTop) {
|
||||
player.fitSurfaceToScreen()
|
||||
}
|
||||
}
|
||||
contentView.addOnLayoutChangeListener(listener)
|
||||
contentLayoutListener = listener
|
||||
listenerContentView = contentView
|
||||
|
||||
// Fit once now that the surface is attached and the parent is sized.
|
||||
player.fitSurfaceToScreen()
|
||||
|
||||
android.util.Log.d("VideoOverlayManager", "Video surface attached to view hierarchy")
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("VideoOverlayManager", "Failed to attach video surface", e)
|
||||
@@ -64,6 +83,7 @@ object VideoOverlayManager {
|
||||
*/
|
||||
fun detachVideoSurface(activity: Activity) {
|
||||
try {
|
||||
removeLayoutListener()
|
||||
attachedSurfaceView?.let { surfaceView ->
|
||||
val contentView = activity.window.decorView.findViewById<ViewGroup>(android.R.id.content)
|
||||
contentView.removeView(surfaceView)
|
||||
@@ -83,4 +103,12 @@ object VideoOverlayManager {
|
||||
fun isVideoSurfaceAttached(): Boolean {
|
||||
return attachedSurfaceView != null
|
||||
}
|
||||
|
||||
private fun removeLayoutListener() {
|
||||
contentLayoutListener?.let { listener ->
|
||||
listenerContentView?.removeOnLayoutChangeListener(listener)
|
||||
}
|
||||
contentLayoutListener = null
|
||||
listenerContentView = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +161,9 @@ class JellyTauPlayer(private val appContext: Context) {
|
||||
/** SurfaceView for video playback */
|
||||
private var surfaceView: SurfaceView? = null
|
||||
private var surfaceHolder: SurfaceHolder? = null
|
||||
/** Last reported video frame size, used to fit the surface to the screen preserving aspect ratio */
|
||||
private var videoWidth: Int = 0
|
||||
private var videoHeight: Int = 0
|
||||
private var currentMediaType: MediaType = MediaType.AUDIO
|
||||
private var currentActivity: java.lang.ref.WeakReference<android.app.Activity>? = null
|
||||
|
||||
@@ -262,7 +265,11 @@ class JellyTauPlayer(private val appContext: Context) {
|
||||
}
|
||||
|
||||
override fun onVideoSizeChanged(videoSize: androidx.media3.common.VideoSize) {
|
||||
android.util.Log.d("JellyTauPlayer", "▶ Video size: ${videoSize.width}x${videoSize.height}")
|
||||
android.util.Log.d("JellyTauPlayer", "▶ Video size: ${videoSize.width}x${videoSize.height} par=${videoSize.pixelWidthHeightRatio}")
|
||||
// Apply pixel aspect ratio so anamorphic content isn't distorted
|
||||
videoWidth = (videoSize.width * videoSize.pixelWidthHeightRatio).toInt()
|
||||
videoHeight = videoSize.height
|
||||
fitSurfaceToScreen()
|
||||
}
|
||||
|
||||
override fun onRenderedFirstFrame() {
|
||||
@@ -873,17 +880,62 @@ class JellyTauPlayer(private val appContext: Context) {
|
||||
|
||||
/**
|
||||
* Resize the video surface (for orientation changes).
|
||||
*
|
||||
* Re-fits the surface to the screen preserving the video's aspect ratio so
|
||||
* nothing is cropped when the device rotates.
|
||||
*/
|
||||
fun resizeSurface(width: Int, height: Int) {
|
||||
fitSurfaceToScreen()
|
||||
}
|
||||
|
||||
/**
|
||||
* Size the video SurfaceView so the video fits entirely inside its parent
|
||||
* (the full-screen content view) while preserving aspect ratio (letterbox/
|
||||
* pillarbox). A raw SurfaceView with MATCH_PARENT otherwise stretches the
|
||||
* video to the surface bounds, which crops the bottom on rotation.
|
||||
*/
|
||||
fun fitSurfaceToScreen() {
|
||||
mainHandler.post {
|
||||
surfaceView?.let { view ->
|
||||
view.layoutParams = view.layoutParams.apply {
|
||||
this.width = width
|
||||
this.height = height
|
||||
}
|
||||
view.requestLayout()
|
||||
android.util.Log.d("JellyTauPlayer", "Video surface resized to ${width}x${height}")
|
||||
val view = surfaceView ?: return@post
|
||||
val parent = view.parent as? ViewGroup
|
||||
// Available area: prefer the parent's measured size, fall back to the screen.
|
||||
val availW = parent?.width?.takeIf { it > 0 }
|
||||
?: appContext.resources.displayMetrics.widthPixels
|
||||
val availH = parent?.height?.takeIf { it > 0 }
|
||||
?: appContext.resources.displayMetrics.heightPixels
|
||||
|
||||
if (videoWidth <= 0 || videoHeight <= 0 || availW <= 0 || availH <= 0) {
|
||||
return@post
|
||||
}
|
||||
|
||||
val videoAspect = videoWidth.toFloat() / videoHeight.toFloat()
|
||||
val viewAspect = availW.toFloat() / availH.toFloat()
|
||||
|
||||
val targetW: Int
|
||||
val targetH: Int
|
||||
if (videoAspect > viewAspect) {
|
||||
// Video is wider than the screen → fit width, letterbox top/bottom
|
||||
targetW = availW
|
||||
targetH = (availW / videoAspect).toInt()
|
||||
} else {
|
||||
// Video is taller than the screen → fit height, pillarbox sides
|
||||
targetH = availH
|
||||
targetW = (availH * videoAspect).toInt()
|
||||
}
|
||||
|
||||
val lp = view.layoutParams
|
||||
// FrameLayout child: center the fitted surface within the full-screen parent.
|
||||
if (lp is FrameLayout.LayoutParams) {
|
||||
lp.gravity = android.view.Gravity.CENTER
|
||||
}
|
||||
lp.width = targetW
|
||||
lp.height = targetH
|
||||
view.layoutParams = lp
|
||||
view.requestLayout()
|
||||
android.util.Log.d(
|
||||
"JellyTauPlayer",
|
||||
"Video surface fitted to ${targetW}x${targetH} (video ${videoWidth}x${videoHeight}, avail ${availW}x${availH})"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user