fix(android)/aspect-ratio (#4594)
This commit is contained in:
committed by
GitHub
parent
86ea0e3101
commit
481aa7ca65
@@ -4,8 +4,6 @@ import android.content.Context
|
|||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.GradientDrawable
|
import android.graphics.drawable.GradientDrawable
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.SurfaceView
|
|
||||||
import android.view.TextureView
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.View.MeasureSpec
|
import android.view.View.MeasureSpec
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
@@ -19,14 +17,13 @@ import androidx.media3.ui.DefaultTimeBar
|
|||||||
import androidx.media3.ui.PlayerView
|
import androidx.media3.ui.PlayerView
|
||||||
import com.brentvatne.common.api.ResizeMode
|
import com.brentvatne.common.api.ResizeMode
|
||||||
import com.brentvatne.common.api.SubtitleStyle
|
import com.brentvatne.common.api.SubtitleStyle
|
||||||
import com.brentvatne.common.api.ViewType
|
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
|
class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
|
||||||
FrameLayout(context, attrs, defStyleAttr) {
|
FrameLayout(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
private var localStyle = SubtitleStyle()
|
private var localStyle = SubtitleStyle()
|
||||||
private var surfaceView: View? = null
|
private var pendingResizeMode: Int? = null
|
||||||
private val liveBadge: TextView = TextView(context).apply {
|
private val liveBadge: TextView = TextView(context).apply {
|
||||||
text = "LIVE"
|
text = "LIVE"
|
||||||
setTextColor(Color.WHITE)
|
setTextColor(Color.WHITE)
|
||||||
@@ -51,15 +48,19 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
|||||||
// Enable proper surface view handling to prevent rendering issues
|
// Enable proper surface view handling to prevent rendering issues
|
||||||
setUseArtwork(false)
|
setUseArtwork(false)
|
||||||
setDefaultArtwork(null)
|
setDefaultArtwork(null)
|
||||||
// Ensure proper video scaling
|
// Ensure proper video scaling - start with FIT mode
|
||||||
resizeMode = androidx.media3.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT
|
resizeMode = androidx.media3.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
addView(playerView)
|
// Add PlayerView with explicit layout parameters
|
||||||
val lp = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
val playerViewLayoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
||||||
lp.setMargins(16, 16, 16, 16)
|
addView(playerView, playerViewLayoutParams)
|
||||||
addView(liveBadge, lp)
|
|
||||||
|
// Add live badge with its own layout parameters
|
||||||
|
val liveBadgeLayoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
||||||
|
liveBadgeLayoutParams.setMargins(16, 16, 16, 16)
|
||||||
|
addView(liveBadge, liveBadgeLayoutParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setPlayer(player: ExoPlayer?) {
|
fun setPlayer(player: ExoPlayer?) {
|
||||||
@@ -67,21 +68,16 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
|||||||
|
|
||||||
if (currentPlayer != null) {
|
if (currentPlayer != null) {
|
||||||
currentPlayer.removeListener(playerListener)
|
currentPlayer.removeListener(playerListener)
|
||||||
// Clear any existing surface from the player
|
|
||||||
when (surfaceView) {
|
|
||||||
is SurfaceView -> currentPlayer.clearVideoSurfaceView(surfaceView as SurfaceView)
|
|
||||||
is TextureView -> currentPlayer.clearVideoTextureView(surfaceView as TextureView)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
playerView.player = player
|
playerView.player = player
|
||||||
|
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
player.addListener(playerListener)
|
player.addListener(playerListener)
|
||||||
// Set the surface view for the new player
|
|
||||||
when (surfaceView) {
|
// Apply pending resize mode if we have one
|
||||||
is SurfaceView -> player.setVideoSurfaceView(surfaceView as SurfaceView)
|
pendingResizeMode?.let { resizeMode ->
|
||||||
is TextureView -> player.setVideoTextureView(surfaceView as TextureView)
|
playerView.resizeMode = resizeMode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +85,7 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
|||||||
fun getPlayerView(): PlayerView = playerView
|
fun getPlayerView(): PlayerView = playerView
|
||||||
|
|
||||||
fun setResizeMode(@ResizeMode.Mode resizeMode: Int) {
|
fun setResizeMode(@ResizeMode.Mode resizeMode: Int) {
|
||||||
playerView.resizeMode = when (resizeMode) {
|
val targetResizeMode = when (resizeMode) {
|
||||||
ResizeMode.RESIZE_MODE_FILL -> AspectRatioFrameLayout.RESIZE_MODE_FILL
|
ResizeMode.RESIZE_MODE_FILL -> AspectRatioFrameLayout.RESIZE_MODE_FILL
|
||||||
ResizeMode.RESIZE_MODE_CENTER_CROP -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
ResizeMode.RESIZE_MODE_CENTER_CROP -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
||||||
ResizeMode.RESIZE_MODE_FIT -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
ResizeMode.RESIZE_MODE_FIT -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||||
@@ -97,6 +93,18 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
|||||||
ResizeMode.RESIZE_MODE_FIXED_HEIGHT -> AspectRatioFrameLayout.RESIZE_MODE_FIXED_HEIGHT
|
ResizeMode.RESIZE_MODE_FIXED_HEIGHT -> AspectRatioFrameLayout.RESIZE_MODE_FIXED_HEIGHT
|
||||||
else -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
else -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply the resize mode to PlayerView immediately
|
||||||
|
playerView.resizeMode = targetResizeMode
|
||||||
|
|
||||||
|
// Store it for reapplication if needed
|
||||||
|
pendingResizeMode = targetResizeMode
|
||||||
|
|
||||||
|
// Force PlayerView to recalculate its layout
|
||||||
|
playerView.requestLayout()
|
||||||
|
|
||||||
|
// Also request layout on the parent to ensure proper sizing
|
||||||
|
requestLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSubtitleStyle(style: SubtitleStyle) {
|
fun setSubtitleStyle(style: SubtitleStyle) {
|
||||||
@@ -132,49 +140,20 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateSurfaceView(viewType: Int) {
|
fun updateSurfaceView(viewType: Int) {
|
||||||
val currentSurfaceView = surfaceView
|
// TODO: Implement proper surface type switching if needed
|
||||||
val newSurfaceView: View = when (viewType) {
|
|
||||||
ViewType.VIEW_TYPE_TEXTURE -> TextureView(context)
|
|
||||||
|
|
||||||
ViewType.VIEW_TYPE_SURFACE_SECURE -> SurfaceView(context).apply {
|
|
||||||
// Requires API 17+
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
|
||||||
(this as SurfaceView).setSecure(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> SurfaceView(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentSurfaceView === newSurfaceView) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the old surface view
|
|
||||||
if (currentSurfaceView != null) {
|
|
||||||
removeView(currentSurfaceView)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the new surface view
|
|
||||||
surfaceView = newSurfaceView
|
|
||||||
val layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
||||||
addView(newSurfaceView, 0, layoutParams)
|
|
||||||
|
|
||||||
// Attach player to the new surface
|
|
||||||
playerView.player?.let {
|
|
||||||
when (newSurfaceView) {
|
|
||||||
is SurfaceView -> it.setVideoSurfaceView(newSurfaceView)
|
|
||||||
is TextureView -> it.setVideoTextureView(newSurfaceView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val isPlaying: Boolean
|
val isPlaying: Boolean
|
||||||
get() = playerView.player?.isPlaying ?: false
|
get() = playerView.player?.isPlaying ?: false
|
||||||
|
|
||||||
fun invalidateAspectRatio() {
|
fun invalidateAspectRatio() {
|
||||||
// PlayerView handles aspect ratio automatically
|
// PlayerView handles aspect ratio automatically through its internal AspectRatioFrameLayout
|
||||||
requestLayout()
|
playerView.requestLayout()
|
||||||
|
|
||||||
|
// Reapply the current resize mode to ensure it's properly set
|
||||||
|
pendingResizeMode?.let { resizeMode ->
|
||||||
|
playerView.resizeMode = resizeMode
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setUseController(useController: Boolean) {
|
fun setUseController(useController: Boolean) {
|
||||||
@@ -245,7 +224,13 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
|||||||
|
|
||||||
private val playerListener = object : Player.Listener {
|
private val playerListener = object : Player.Listener {
|
||||||
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
|
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
|
||||||
playerView.post { playerView.requestLayout() }
|
playerView.post {
|
||||||
|
playerView.requestLayout()
|
||||||
|
// Reapply resize mode to ensure it's properly set after timeline changes
|
||||||
|
pendingResizeMode?.let { resizeMode ->
|
||||||
|
playerView.resizeMode = resizeMode
|
||||||
|
}
|
||||||
|
}
|
||||||
updateLiveUi()
|
updateLiveUi()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,6 +240,15 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
|||||||
) {
|
) {
|
||||||
updateLiveUi()
|
updateLiveUi()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle video size changes which affect aspect ratio
|
||||||
|
if (events.contains(Player.EVENT_VIDEO_SIZE_CHANGED)) {
|
||||||
|
pendingResizeMode?.let { resizeMode ->
|
||||||
|
playerView.resizeMode = resizeMode
|
||||||
|
}
|
||||||
|
playerView.requestLayout()
|
||||||
|
requestLayout()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,4 +276,14 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
|
|||||||
// Post a second layout pass so the ExoPlayer internal views get correct bounds.
|
// Post a second layout pass so the ExoPlayer internal views get correct bounds.
|
||||||
post(layoutRunnable)
|
post(layoutRunnable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||||
|
super.onLayout(changed, left, top, right, bottom)
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
pendingResizeMode?.let { resizeMode ->
|
||||||
|
playerView.resizeMode = resizeMode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user