79758879

Date: 2025-09-08 12:37:21
Score: 0.5
Natty:
Report link

Try this VideoSurface custom view:

import android.content.Context
import android.graphics.SurfaceTexture
import android.media.MediaPlayer
import android.net.Uri
import android.util.AttributeSet
import android.view.Surface
import android.view.TextureView
import android.view.TextureView.SurfaceTextureListener

/**
 * A [TextureView]-based custom video surface that wraps [MediaPlayer] for
 * lightweight video playback. This class allows playing looping videos inside
 * a Compose `AndroidView` or traditional Views without requiring ExoPlayer.
 *
 * Usage:
 * ```
 * val videoSurface = VideoSurface(context).apply {
 *     setSource(videoUri)
 *     setOnPreparedListener { mp ->
 *         // do something when ready, e.g. hide loader
 *     }
 *     setOnCompletionListener {
 *         // handle completion if looping is disabled
 *     }
 *     setOnErrorListener { mp, what, extra ->
 *         // handle error
 *         true
 *     }
 * }
 * ```
 *
 * Notes:
 * - Releases its [MediaPlayer] automatically on [onDetachedFromWindow].
 * - You must call [setSource] before the surface is available.
 * - Starts playback automatically once prepared.
 */
class VideoSurface @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = 0
) : TextureView(context, attrs, defStyle), SurfaceTextureListener {

    private val mediaPlayer = MediaPlayer()
    private var source: Uri? = null
    private var completionListener: MediaPlayer.OnCompletionListener? = null
    private var preparedListener: MediaPlayer.OnPreparedListener? = null
    private var errorListener: MediaPlayer.OnErrorListener? = null

    init {
        surfaceTextureListener = this
    }

    /**
     * Sets the video source [Uri].
     *
     * Must be called before the surface is available for playback to start.
     */
    fun setSource(source: Uri?) {
        this.source = source
    }

    /**
     * Registers a listener to be notified when playback completes.
     */
    fun setOnCompletionListener(listener: MediaPlayer.OnCompletionListener?) {
        completionListener = listener
    }

    /**
     * Registers a listener to be notified when the video is prepared.
     */
    fun setOnPreparedListener(listener: MediaPlayer.OnPreparedListener?) {
        preparedListener = listener
    }

    /**
     * Registers a listener to be notified when an error occurs during playback.
     */
    fun setOnErrorListener(listener: MediaPlayer.OnErrorListener?) {
        errorListener = listener
    }

    /**
     * Releases the [MediaPlayer] when the view is detached.
     */
    override fun onDetachedFromWindow() {
        mediaPlayer.reset()
        super.onDetachedFromWindow()
    }

    override fun onSurfaceTextureAvailable(
        surfaceTexture: SurfaceTexture,
        width: Int,
        height: Int
    ) {
        val surface = Surface(surfaceTexture)
        try {
            mediaPlayer.apply {
                setOnCompletionListener(completionListener)
                setOnErrorListener(errorListener)
                setSurface(surface)

                isLooping = true
                source?.let { setDataSource(context, it) }

                setOnPreparedListener { mp ->
                    start()
                    preparedListener?.onPrepared(mp)
                }

                prepareAsync()
            }
        } catch (e: Exception) {
            e.printStackTrace()
            mediaPlayer.reset()
        }
    }

    override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) = Unit

    override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
        surface.release()
        return true
    }

    override fun onSurfaceTextureUpdated(surface: SurfaceTexture) = Unit
}
Reasons:
  • Blacklisted phrase (2): Try this Video
  • Blacklisted phrase (1): this Video
  • Long answer (-1):
  • Has code block (-0.5):
  • High reputation (-1):
Posted by: Homayoon Ahmadi