Procházet zdrojové kódy

media player controls

Milan Jurkulak před 3 roky
rodič
revize
df6106cada

+ 42 - 0
app/src/main/kotlin/org/mjdev/tvapp/activity/MainActivity.kt

@@ -8,8 +8,10 @@
 
 package org.mjdev.tvapp.activity
 
+import android.os.Bundle
 import androidx.compose.runtime.Composable
 import dagger.hilt.android.AndroidEntryPoint
+import org.mjdev.tvapp.BuildConfig
 import org.mjdev.tvlib.activity.ComposableActivity
 import org.mjdev.tvlib.annotations.TvPreview
 import org.mjdev.tvlib.extensions.NavGraphBuilderExt.screen
@@ -18,10 +20,50 @@ import org.mjdev.tvapp.ui.screens.DetailScreen
 import org.mjdev.tvapp.ui.screens.MainScreen
 import org.mjdev.tvapp.ui.screens.PlayerScreen
 import org.mjdev.tvapp.ui.screens.SplashScreen
+import org.mjdev.tvlib.helpers.anr.agent.ANRSpyAgent
+import org.mjdev.tvlib.helpers.anr.agent.ANRSpyListener
+import org.mjdev.tvlib.helpers.anr.models.MethodModel
+import timber.log.Timber
 
 @AndroidEntryPoint
 class MainActivity : ComposableActivity() {
 
+    private var anrCallback = object : ANRSpyListener {
+        override fun onWait(ms: Long) {
+        }
+
+        @Deprecated("This callback will be removed in future")
+        override fun onAnrStackTrace(stackstrace: Array<StackTraceElement>) {
+        }
+
+        override fun onReportAvailable(methodList: List<MethodModel>) {
+        }
+
+        override fun onAnrDetected(
+            details: String,
+            stackTrace: Array<StackTraceElement>,
+            packageMethods: List<String>?
+        ) {
+            Timber.e(details)
+            Timber.e(stackTrace.toString())
+            Timber.e(packageMethods.toString())
+        }
+    }
+
+    private val anrSpyAgent = ANRSpyAgent.Builder(this)
+        .setTimeOut(500)
+        .setSpyListener(anrCallback)
+        .setThrowException(false)
+        .enableReportAnnotatedMethods(true)
+        .build()
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        if (BuildConfig.DEBUG) {
+            anrSpyAgent.start()
+        }
+    }
+
     @TvPreview
     @Composable
     override fun Compose() = super.Compose()

+ 39 - 7
app/src/main/kotlin/org/mjdev/tvapp/ui/components/MediaPlayer.kt

@@ -10,12 +10,22 @@ package org.mjdev.tvapp.ui.components
 
 import android.annotation.SuppressLint
 import android.net.Uri
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.layout.ContentScale
 import org.mjdev.tvlib.annotations.TvPreview
+import org.mjdev.tvlib.extensions.ModifierExt.tvAspectRatio
 import org.mjdev.tvlib.extensions.StringExt.parseUri
+import org.mjdev.tvlib.interfaces.ItemWithBackground
+import org.mjdev.tvlib.interfaces.ItemWithImage
 import org.mjdev.tvlib.interfaces.ItemWithUri
+import org.mjdev.tvlib.ui.components.image.ImageAny
 import org.mjdev.tvlib.ui.components.media.MediaPlayerContainer
 import java.net.URL
 
@@ -23,11 +33,10 @@ import java.net.URL
 @TvPreview
 @Composable
 fun MediaPlayer(
-    data: Any? = null,
-    modifier: Modifier = Modifier
+    data: Any? = null, modifier: Modifier = Modifier
 ) {
 
-    val uriToPlay : String? = when (data) {
+    val uriToPlay: String? = when (data) {
         null -> null
         is ItemWithUri<*> -> data.uri.toString()
         is String -> data.toString()
@@ -36,9 +45,32 @@ fun MediaPlayer(
         else -> data.toString()
     }
 
-    MediaPlayerContainer(
-        modifier = modifier.fillMaxSize(),
-        uri = uriToPlay?.parseUri()
-    )
+    val background = when (data) {
+        is ItemWithBackground<*> -> data.background
+        is ItemWithImage<*> -> data.image
+        is ItemWithUri<*> -> data.uri
+        is String -> data.toString()
+        is URL -> data.toString()
+        is Uri -> data.toString()
+        else -> data.toString()
+    }
+
+    Box(
+        modifier = modifier
+            .fillMaxSize()
+            .background(Color.Black, RectangleShape),
+    ) {
+        // todo does not show
+        ImageAny(
+            modifier = Modifier.fillMaxSize(),
+            src = background,
+            contentScale = ContentScale.FillWidth,
+            alignment = Alignment.Center
+        )
+        MediaPlayerContainer(
+            modifier = modifier.fillMaxSize(),
+            uri = uriToPlay?.parseUri(),
+        )
+    }
 
 }

+ 4 - 2
app/src/main/kotlin/org/mjdev/tvapp/ui/pages/MainPage.kt

@@ -100,11 +100,13 @@ class MainPage : Page() {
         }
 
         val onItemSelect: (item: Any?) -> Unit = { item ->
-            backgroundState.value = when (item) {
+            when (item) {
                 is ItemPhoto -> item.uri
                 is ItemWithBackground<*> -> item.background
                 is ItemWithImage<*> -> item.image
-                else -> {}
+                else -> null
+            }.also { uri ->
+                backgroundState.value = uri
             }
         }
 

+ 2 - 2
gradle.properties

@@ -22,6 +22,6 @@ kotlin.code.style=official
 # thereby reducing the size of the R class for that library
 android.nonTransitiveRClass=true
 android.defaults.buildfeatures.buildconfig=true
-android.nonFinalResIds=false
 org.gradle.unsafe.configuration-cache=false
-android.suppressUnsupportedCompileSdk=34
+android.suppressUnsupportedCompileSdk=34
+android.nonFinalResIds=true

+ 6 - 6
tvlib/src/main/AndroidManifest.xml

@@ -1,4 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?><!--
+<?xml version="1.0" encoding="utf-8"?>
+<!--
   ~ Copyright (c) Milan Jurkulák 2023.
   ~  Contact:
   ~  e: mimoccc@gmail.com
@@ -6,12 +7,11 @@
   ~  w: https://mjdev.org
   -->
 
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <permission
-        android:name="android.permission.QUERY_ALL_PACKAGES"
-        tools:ignore="ReservedSystemPermission" />
+<!--    <permission-->
+<!--        android:name="android.permission.QUERY_ALL_PACKAGES"-->
+<!--        tools:ignore="ReservedSystemPermission" />-->
 
     <queries>
         <intent>

+ 538 - 3
tvlib/src/main/kotlin/org/mjdev/tvlib/ui/components/media/ExoPlayerImpl.kt

@@ -9,6 +9,11 @@
 package org.mjdev.tvlib.ui.components.media
 
 import android.net.Uri
+import android.os.Looper
+import android.view.Surface
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+import android.view.TextureView
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.size
@@ -19,7 +24,19 @@ import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.media3.common.AudioAttributes
+import androidx.media3.common.DeviceInfo
 import androidx.media3.common.MediaItem
+import androidx.media3.common.MediaMetadata
+import androidx.media3.common.PlaybackException
+import androidx.media3.common.PlaybackParameters
+import androidx.media3.common.Player
+import androidx.media3.common.Timeline
+import androidx.media3.common.TrackSelectionParameters
+import androidx.media3.common.Tracks
+import androidx.media3.common.VideoSize
+import androidx.media3.common.text.CueGroup
+import androidx.media3.common.util.Size
 import androidx.media3.common.util.UnstableApi
 import androidx.media3.exoplayer.ExoPlayer
 import androidx.media3.ui.PlayerView
@@ -27,6 +44,8 @@ import org.mjdev.tvlib.extensions.ComposeExt.isEditMode
 import org.mjdev.tvlib.extensions.ContextExt.isATv
 import org.mjdev.tvlib.extensions.ModifierExt.recomposeHighlighter
 
+@Suppress("DEPRECATION")
+@UnstableApi
 class ExoPlayerImpl(
     private val exoPlayer: ExoPlayer
 ) : IMediaPlayer {
@@ -70,22 +89,24 @@ class ExoPlayerImpl(
     }
 
     override fun play() {
-        exoPlayer.playWhenReady = true
+        exoPlayer.play()
     }
 
     override fun pause() {
-        exoPlayer.playWhenReady = false
+        exoPlayer.pause()
     }
 
     override fun resume() {
+        // todo test
         exoPlayer.playWhenReady = true
     }
 
     override fun stop() {
-        exoPlayer.playWhenReady = false
+        exoPlayer.stop()
     }
 
     override fun dispose() {
+        // todo test
         exoPlayer.release()
     }
 
@@ -93,4 +114,518 @@ class ExoPlayerImpl(
         exoPlayer.seekTo(value)
     }
 
+    override fun seekTo(mediaItemIndex: Int, positionMs: Long) {
+        exoPlayer.seekTo(mediaItemIndex, positionMs)
+    }
+
+    override fun getApplicationLooper(): Looper {
+        return exoPlayer.applicationLooper
+    }
+
+    override fun addListener(listener: Player.Listener) {
+        exoPlayer.addListener(listener)
+    }
+
+    override fun removeListener(listener: Player.Listener) {
+        exoPlayer.removeListener(listener)
+    }
+
+    override fun setMediaItems(mediaItems: MutableList<MediaItem>) {
+        exoPlayer.setMediaItems(mediaItems)
+    }
+
+    override fun setMediaItems(mediaItems: MutableList<MediaItem>, resetPosition: Boolean) {
+        exoPlayer.setMediaItems(mediaItems, resetPosition)
+    }
+
+    override fun setMediaItems(
+        mediaItems: MutableList<MediaItem>,
+        startIndex: Int,
+        startPositionMs: Long
+    ) {
+        exoPlayer.setMediaItems(mediaItems, startIndex, startPositionMs)
+    }
+
+    override fun setMediaItem(mediaItem: MediaItem) {
+        exoPlayer.setMediaItem(mediaItem)
+    }
+
+    override fun setMediaItem(mediaItem: MediaItem, startPositionMs: Long) {
+        exoPlayer.setMediaItem(mediaItem, startPositionMs)
+    }
+
+    override fun setMediaItem(mediaItem: MediaItem, resetPosition: Boolean) {
+        exoPlayer.setMediaItem(mediaItem, resetPosition)
+    }
+
+    override fun addMediaItem(mediaItem: MediaItem) {
+        exoPlayer.addMediaItem(mediaItem)
+    }
+
+    override fun addMediaItem(index: Int, mediaItem: MediaItem) {
+        exoPlayer.addMediaItem(index, mediaItem)
+    }
+
+    override fun addMediaItems(mediaItems: MutableList<MediaItem>) {
+        exoPlayer.addMediaItems(mediaItems)
+    }
+
+    override fun addMediaItems(index: Int, mediaItems: MutableList<MediaItem>) {
+        exoPlayer.addMediaItems(index, mediaItems)
+    }
+
+    override fun moveMediaItem(currentIndex: Int, newIndex: Int) {
+        exoPlayer.moveMediaItem(currentIndex, newIndex)
+    }
+
+    override fun moveMediaItems(fromIndex: Int, toIndex: Int, newIndex: Int) {
+        exoPlayer.moveMediaItems(fromIndex, toIndex, newIndex)
+    }
+
+    override fun replaceMediaItem(index: Int, mediaItem: MediaItem) {
+        exoPlayer.replaceMediaItem(index, mediaItem)
+    }
+
+    override fun replaceMediaItems(
+        fromIndex: Int,
+        toIndex: Int,
+        mediaItems: MutableList<MediaItem>
+    ) {
+        exoPlayer.replaceMediaItems(fromIndex, toIndex, mediaItems)
+    }
+
+    override fun removeMediaItem(index: Int) {
+        exoPlayer.removeMediaItem(index)
+    }
+
+    override fun removeMediaItems(fromIndex: Int, toIndex: Int) {
+        exoPlayer.removeMediaItems(fromIndex, toIndex)
+    }
+
+    override fun clearMediaItems() {
+        exoPlayer.clearMediaItems()
+    }
+
+    override fun isCommandAvailable(command: Int): Boolean {
+        return exoPlayer.isCommandAvailable(command)
+    }
+
+    override fun canAdvertiseSession(): Boolean {
+        return exoPlayer.canAdvertiseSession()
+    }
+
+    override fun getAvailableCommands(): Player.Commands {
+        return exoPlayer.availableCommands
+    }
+
+    override fun getPlaybackState(): Int {
+        return exoPlayer.playbackState
+    }
+
+    override fun getPlaybackSuppressionReason(): Int {
+        return exoPlayer.playbackSuppressionReason
+    }
+
+    override fun isPlaying(): Boolean {
+        return exoPlayer.isPlaying
+    }
+
+    override fun getPlayerError(): PlaybackException? {
+        return exoPlayer.playerError
+    }
+
+    override fun setPlayWhenReady(playWhenReady: Boolean) {
+        exoPlayer.playWhenReady = playWhenReady
+    }
+
+    override fun getPlayWhenReady(): Boolean {
+        return exoPlayer.playWhenReady
+    }
+
+    override fun setRepeatMode(repeatMode: Int) {
+        exoPlayer.repeatMode = repeatMode
+    }
+
+    override fun getRepeatMode(): Int {
+        return exoPlayer.repeatMode
+    }
+
+    override fun setShuffleModeEnabled(shuffleModeEnabled: Boolean) {
+        exoPlayer.shuffleModeEnabled = shuffleModeEnabled
+    }
+
+    override fun getShuffleModeEnabled(): Boolean {
+        return exoPlayer.shuffleModeEnabled
+    }
+
+    override fun isLoading(): Boolean {
+        return exoPlayer.isLoading
+    }
+
+    override fun seekToDefaultPosition() {
+        exoPlayer.seekToDefaultPosition()
+    }
+
+    override fun seekToDefaultPosition(mediaItemIndex: Int) {
+        exoPlayer.seekToDefaultPosition(mediaItemIndex)
+    }
+
+    override fun getSeekBackIncrement(): Long {
+        return exoPlayer.seekBackIncrement
+    }
+
+    override fun seekBack() {
+        exoPlayer.seekBack()
+    }
+
+    override fun getSeekForwardIncrement(): Long {
+        return exoPlayer.seekForwardIncrement
+    }
+
+    override fun seekForward() {
+        exoPlayer.seekForward()
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun hasPrevious(): Boolean {
+        return exoPlayer.hasPrevious()
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun hasPreviousWindow(): Boolean {
+        return exoPlayer.hasPreviousWindow()
+    }
+
+    override fun hasPreviousMediaItem(): Boolean {
+        return exoPlayer.hasPreviousMediaItem()
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun previous() {
+        exoPlayer.previous()
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun seekToPreviousWindow() {
+        exoPlayer.seekToPreviousWindow()
+    }
+
+    override fun seekToPreviousMediaItem() {
+        exoPlayer.seekToPreviousMediaItem()
+    }
+
+    override fun getMaxSeekToPreviousPosition(): Long {
+        return exoPlayer.maxSeekToPreviousPosition
+    }
+
+    override fun seekToPrevious() {
+        exoPlayer.seekToPrevious()
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun hasNext(): Boolean {
+        return exoPlayer.hasNext()
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun hasNextWindow(): Boolean {
+        return exoPlayer.hasNextWindow()
+    }
+
+    override fun hasNextMediaItem(): Boolean {
+        return exoPlayer.hasNextMediaItem()
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun next() {
+        exoPlayer.next()
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun seekToNextWindow() {
+        exoPlayer.seekToNextWindow()
+    }
+
+    override fun seekToNextMediaItem() {
+        exoPlayer.seekToNextMediaItem()
+    }
+
+    override fun seekToNext() {
+        exoPlayer.seekToNext()
+    }
+
+    override fun setPlaybackParameters(playbackParameters: PlaybackParameters) {
+        exoPlayer.playbackParameters = playbackParameters
+    }
+
+    override fun setPlaybackSpeed(speed: Float) {
+        exoPlayer.setPlaybackSpeed(speed)
+    }
+
+    override fun getPlaybackParameters(): PlaybackParameters {
+        return exoPlayer.playbackParameters
+    }
+
+    override fun release() {
+        exoPlayer.release()
+    }
+
+    override fun getCurrentTracks(): Tracks {
+        return exoPlayer.currentTracks
+    }
+
+    override fun getTrackSelectionParameters(): TrackSelectionParameters {
+        return exoPlayer.trackSelectionParameters
+    }
+
+    override fun setTrackSelectionParameters(parameters: TrackSelectionParameters) {
+        exoPlayer.trackSelectionParameters = parameters
+    }
+
+    override fun getMediaMetadata(): MediaMetadata {
+        return exoPlayer.mediaMetadata
+    }
+
+    override fun getPlaylistMetadata(): MediaMetadata {
+        return exoPlayer.playlistMetadata
+    }
+
+    override fun setPlaylistMetadata(mediaMetadata: MediaMetadata) {
+        exoPlayer.playlistMetadata = mediaMetadata
+    }
+
+    override fun getCurrentManifest(): Any? {
+        return exoPlayer.currentManifest
+    }
+
+    override fun getCurrentTimeline(): Timeline {
+        return exoPlayer.currentTimeline
+    }
+
+    override fun getCurrentPeriodIndex(): Int {
+        return exoPlayer.currentPeriodIndex
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun getCurrentWindowIndex(): Int {
+        return exoPlayer.currentWindowIndex
+    }
+
+    override fun getCurrentMediaItemIndex(): Int {
+        return exoPlayer.currentMediaItemIndex
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun getNextWindowIndex(): Int {
+        return exoPlayer.currentMediaItemIndex
+    }
+
+    override fun getNextMediaItemIndex(): Int {
+        return exoPlayer.nextMediaItemIndex
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun getPreviousWindowIndex(): Int {
+        return exoPlayer.previousWindowIndex
+    }
+
+    override fun getPreviousMediaItemIndex(): Int {
+        return exoPlayer.previousMediaItemIndex
+    }
+
+    override fun getCurrentMediaItem(): MediaItem? {
+        return exoPlayer.currentMediaItem
+    }
+
+    override fun getMediaItemCount(): Int {
+        return exoPlayer.mediaItemCount
+    }
+
+    override fun getMediaItemAt(index: Int): MediaItem {
+        return exoPlayer.getMediaItemAt(index)
+    }
+
+    override fun getDuration(): Long {
+        return exoPlayer.duration
+    }
+
+    override fun getCurrentPosition(): Long {
+        return exoPlayer.currentPosition
+    }
+
+    override fun getBufferedPosition(): Long {
+        return exoPlayer.bufferedPosition
+    }
+
+    override fun getBufferedPercentage(): Int {
+        return exoPlayer.bufferedPercentage
+    }
+
+    override fun getTotalBufferedDuration(): Long {
+        return exoPlayer.totalBufferedDuration
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun isCurrentWindowDynamic(): Boolean {
+        return exoPlayer.isCurrentWindowDynamic
+    }
+
+    override fun isCurrentMediaItemDynamic(): Boolean {
+        return exoPlayer.isCurrentMediaItemDynamic
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun isCurrentWindowLive(): Boolean {
+        return exoPlayer.isCurrentWindowLive
+    }
+
+    override fun isCurrentMediaItemLive(): Boolean {
+        return exoPlayer.isCurrentMediaItemLive
+    }
+
+    override fun getCurrentLiveOffset(): Long {
+        return exoPlayer.currentLiveOffset
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun isCurrentWindowSeekable(): Boolean {
+        return exoPlayer.isCurrentWindowSeekable
+    }
+
+    override fun isCurrentMediaItemSeekable(): Boolean {
+        return exoPlayer.isCurrentMediaItemSeekable
+    }
+
+    override fun isPlayingAd(): Boolean {
+        return exoPlayer.isPlayingAd
+    }
+
+    override fun getCurrentAdGroupIndex(): Int {
+        return exoPlayer.currentAdGroupIndex
+    }
+
+    override fun getCurrentAdIndexInAdGroup(): Int {
+        return exoPlayer.currentAdIndexInAdGroup
+    }
+
+    override fun getContentDuration(): Long {
+        return exoPlayer.contentDuration
+    }
+
+    override fun getContentPosition(): Long {
+        return exoPlayer.contentPosition
+    }
+
+    override fun getContentBufferedPosition(): Long {
+        return exoPlayer.contentBufferedPosition
+    }
+
+    override fun getAudioAttributes(): AudioAttributes {
+        return exoPlayer.audioAttributes
+    }
+
+    override fun setVolume(volume: Float) {
+        exoPlayer.volume = volume
+    }
+
+    override fun getVolume(): Float {
+        return exoPlayer.volume
+    }
+
+    override fun clearVideoSurface() {
+        return exoPlayer.clearVideoSurface()
+    }
+
+    override fun clearVideoSurface(surface: Surface?) {
+        return exoPlayer.clearVideoSurface(surface)
+    }
+
+    override fun setVideoSurface(surface: Surface?) {
+        exoPlayer.setVideoSurface(surface)
+    }
+
+    override fun setVideoSurfaceHolder(surfaceHolder: SurfaceHolder?) {
+        exoPlayer.setVideoSurfaceHolder(surfaceHolder)
+    }
+
+    override fun clearVideoSurfaceHolder(surfaceHolder: SurfaceHolder?) {
+        exoPlayer.clearVideoSurfaceHolder(surfaceHolder)
+    }
+
+    override fun setVideoSurfaceView(surfaceView: SurfaceView?) {
+        exoPlayer.setVideoSurfaceView(surfaceView)
+    }
+
+    override fun clearVideoSurfaceView(surfaceView: SurfaceView?) {
+        exoPlayer.clearVideoSurfaceView(surfaceView)
+    }
+
+    override fun setVideoTextureView(textureView: TextureView?) {
+        exoPlayer.setVideoTextureView(textureView)
+    }
+
+    override fun clearVideoTextureView(textureView: TextureView?) {
+        exoPlayer.clearVideoTextureView(textureView)
+    }
+
+    override fun getVideoSize(): VideoSize {
+        return exoPlayer.videoSize
+    }
+
+    override fun getSurfaceSize(): Size {
+        return exoPlayer.surfaceSize
+    }
+
+    override fun getCurrentCues(): CueGroup {
+        return exoPlayer.currentCues
+    }
+
+    override fun getDeviceInfo(): DeviceInfo {
+        return exoPlayer.deviceInfo
+    }
+
+    override fun getDeviceVolume(): Int {
+        return exoPlayer.deviceVolume
+    }
+
+    override fun isDeviceMuted(): Boolean {
+        return exoPlayer.isDeviceMuted
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun setDeviceVolume(volume: Int) {
+        return exoPlayer.setDeviceVolume(volume)
+    }
+
+    override fun setDeviceVolume(volume: Int, flags: Int) {
+        exoPlayer.setDeviceVolume(volume, flags)
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun increaseDeviceVolume() {
+        exoPlayer.increaseDeviceVolume()
+    }
+
+    override fun increaseDeviceVolume(flags: Int) {
+        exoPlayer.increaseDeviceVolume(flags)
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun decreaseDeviceVolume() {
+        exoPlayer.decreaseDeviceVolume()
+    }
+
+    override fun decreaseDeviceVolume(flags: Int) {
+        exoPlayer.decreaseDeviceVolume(flags)
+    }
+
+    @Deprecated("Deprecated in Java")
+    override fun setDeviceMuted(muted: Boolean) {
+        exoPlayer.isDeviceMuted = muted
+    }
+
+    override fun setDeviceMuted(muted: Boolean, flags: Int) {
+        exoPlayer.setDeviceMuted(muted, flags)
+    }
+
 }

+ 475 - 6
tvlib/src/main/kotlin/org/mjdev/tvlib/ui/components/media/IMediaPlayer.kt

@@ -9,28 +9,48 @@
 package org.mjdev.tvlib.ui.components.media
 
 import android.net.Uri
+import android.os.Looper
+import android.view.Surface
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+import android.view.TextureView
 import androidx.compose.runtime.Composable
+import androidx.media3.common.AudioAttributes
+import androidx.media3.common.DeviceInfo
+import androidx.media3.common.MediaItem
+import androidx.media3.common.MediaMetadata
+import androidx.media3.common.PlaybackException
+import androidx.media3.common.PlaybackParameters
+import androidx.media3.common.Player
+import androidx.media3.common.Player.REPEAT_MODE_OFF
+import androidx.media3.common.Timeline
+import androidx.media3.common.TrackSelectionParameters
+import androidx.media3.common.Tracks
+import androidx.media3.common.VideoSize
+import androidx.media3.common.text.CueGroup
+import androidx.media3.common.util.Size
 
-interface IMediaPlayer {
+@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
+interface IMediaPlayer : Player {
 
     @Composable
     fun GetPlayerView()
 
     fun setMediaUri(uri: Uri)
 
-    fun prepare()
+    override fun prepare()
 
-    fun play()
+    override fun play()
 
-    fun pause()
+    override fun pause()
 
     fun resume()
 
-    fun stop()
+    override fun stop()
 
     fun dispose()
 
-    fun seekTo(value: Long)
+    override fun seekTo(value: Long)
 
     companion object {
 
@@ -55,6 +75,455 @@ interface IMediaPlayer {
             override fun dispose() {}
 
             override fun seekTo(value: Long) {}
+
+            override fun seekTo(mediaItemIndex: Int, positionMs: Long) {
+            }
+
+            override fun getApplicationLooper(): Looper {
+                return Looper.getMainLooper()
+            }
+
+            override fun addListener(listener: Player.Listener) {
+            }
+
+            override fun removeListener(listener: Player.Listener) {
+            }
+
+            override fun setMediaItems(mediaItems: MutableList<MediaItem>) {
+            }
+
+            override fun setMediaItems(mediaItems: MutableList<MediaItem>, resetPosition: Boolean) {
+            }
+
+            override fun setMediaItems(
+                mediaItems: MutableList<MediaItem>,
+                startIndex: Int,
+                startPositionMs: Long
+            ) {
+            }
+
+            override fun setMediaItem(mediaItem: MediaItem) {
+            }
+
+            override fun setMediaItem(mediaItem: MediaItem, startPositionMs: Long) {
+            }
+
+            override fun setMediaItem(mediaItem: MediaItem, resetPosition: Boolean) {
+            }
+
+            override fun addMediaItem(mediaItem: MediaItem) {
+            }
+
+            override fun addMediaItem(index: Int, mediaItem: MediaItem) {
+            }
+
+            override fun addMediaItems(mediaItems: MutableList<MediaItem>) {
+            }
+
+            override fun addMediaItems(index: Int, mediaItems: MutableList<MediaItem>) {
+            }
+
+            override fun moveMediaItem(currentIndex: Int, newIndex: Int) {
+            }
+
+            override fun moveMediaItems(fromIndex: Int, toIndex: Int, newIndex: Int) {
+            }
+
+            override fun replaceMediaItem(index: Int, mediaItem: MediaItem) {
+            }
+
+            override fun replaceMediaItems(
+                fromIndex: Int,
+                toIndex: Int,
+                mediaItems: MutableList<MediaItem>
+            ) {
+            }
+
+            override fun removeMediaItem(index: Int) {
+            }
+
+            override fun removeMediaItems(fromIndex: Int, toIndex: Int) {
+            }
+
+            override fun clearMediaItems() {
+            }
+
+            override fun isCommandAvailable(command: Int): Boolean {
+                return false
+            }
+
+            override fun canAdvertiseSession(): Boolean {
+                return false
+            }
+
+            override fun getAvailableCommands(): Player.Commands {
+                return Player.Commands.EMPTY
+            }
+
+            override fun getPlaybackState(): Int {
+                return Player.STATE_IDLE
+            }
+
+            override fun getPlaybackSuppressionReason(): Int {
+                return Player.PLAYBACK_SUPPRESSION_REASON_NONE
+            }
+
+            override fun isPlaying(): Boolean {
+                return false
+            }
+
+            override fun getPlayerError(): PlaybackException? {
+                return null
+            }
+
+            override fun setPlayWhenReady(playWhenReady: Boolean) {
+            }
+
+            override fun getPlayWhenReady(): Boolean {
+                return false
+            }
+
+            override fun setRepeatMode(repeatMode: Int) {
+            }
+
+            override fun getRepeatMode(): Int {
+                return REPEAT_MODE_OFF
+            }
+
+            override fun setShuffleModeEnabled(shuffleModeEnabled: Boolean) {
+            }
+
+            override fun getShuffleModeEnabled(): Boolean {
+                return false
+            }
+
+            override fun isLoading(): Boolean {
+                return false
+            }
+
+            override fun seekToDefaultPosition() {
+            }
+
+            override fun seekToDefaultPosition(mediaItemIndex: Int) {
+            }
+
+            override fun getSeekBackIncrement(): Long {
+                return 0
+            }
+
+            override fun seekBack() {
+            }
+
+            override fun getSeekForwardIncrement(): Long {
+                return 0
+            }
+
+            override fun seekForward() {
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun hasPrevious(): Boolean {
+                return false
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun hasPreviousWindow(): Boolean {
+                return false
+            }
+
+            override fun hasPreviousMediaItem(): Boolean {
+                return false
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun previous() {
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun seekToPreviousWindow() {
+            }
+
+            override fun seekToPreviousMediaItem() {
+            }
+
+            override fun getMaxSeekToPreviousPosition(): Long {
+                return 0
+            }
+
+            override fun seekToPrevious() {
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun hasNext(): Boolean {
+                return false
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun hasNextWindow(): Boolean {
+                return false
+            }
+
+            override fun hasNextMediaItem(): Boolean {
+                return false
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun next() {
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun seekToNextWindow() {
+            }
+
+            override fun seekToNextMediaItem() {
+            }
+
+            override fun seekToNext() {
+            }
+
+            override fun setPlaybackParameters(playbackParameters: PlaybackParameters) {
+            }
+
+            override fun setPlaybackSpeed(speed: Float) {
+            }
+
+            override fun getPlaybackParameters(): PlaybackParameters {
+                return PlaybackParameters(1f)
+            }
+
+            override fun release() {
+            }
+
+            override fun getCurrentTracks(): Tracks {
+                return Tracks.EMPTY
+            }
+
+            override fun getTrackSelectionParameters(): TrackSelectionParameters {
+                return TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT
+            }
+
+            override fun setTrackSelectionParameters(parameters: TrackSelectionParameters) {
+            }
+
+            override fun getMediaMetadata(): MediaMetadata {
+                return MediaMetadata.EMPTY
+            }
+
+            override fun getPlaylistMetadata(): MediaMetadata {
+                return MediaMetadata.EMPTY
+            }
+
+            override fun setPlaylistMetadata(mediaMetadata: MediaMetadata) {
+            }
+
+            override fun getCurrentManifest(): Any? {
+                return null
+            }
+
+            override fun getCurrentTimeline(): Timeline {
+                return Timeline.EMPTY
+            }
+
+            override fun getCurrentPeriodIndex(): Int {
+                return 0
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun getCurrentWindowIndex(): Int {
+                return 0
+            }
+
+            override fun getCurrentMediaItemIndex(): Int {
+                return 0
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun getNextWindowIndex(): Int {
+                return 0
+            }
+
+            override fun getNextMediaItemIndex(): Int {
+                return 0
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun getPreviousWindowIndex(): Int {
+                return 0
+            }
+
+            override fun getPreviousMediaItemIndex(): Int {
+                return 0
+            }
+
+            override fun getCurrentMediaItem(): MediaItem? {
+                return null
+            }
+
+            override fun getMediaItemCount(): Int {
+                return 0
+            }
+
+            override fun getMediaItemAt(index: Int): MediaItem {
+                return MediaItem.EMPTY
+            }
+
+            override fun getDuration(): Long {
+                return 0
+            }
+
+            override fun getCurrentPosition(): Long {
+                return 0
+            }
+
+            override fun getBufferedPosition(): Long {
+                return 0
+            }
+
+            override fun getBufferedPercentage(): Int {
+                return 0
+            }
+
+            override fun getTotalBufferedDuration(): Long {
+                return 0
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun isCurrentWindowDynamic(): Boolean {
+                return false
+            }
+
+            override fun isCurrentMediaItemDynamic(): Boolean {
+                return false
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun isCurrentWindowLive(): Boolean {
+                return false
+            }
+
+            override fun isCurrentMediaItemLive(): Boolean {
+                return false
+            }
+
+            override fun getCurrentLiveOffset(): Long {
+                return 0
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun isCurrentWindowSeekable(): Boolean {
+                return false
+            }
+
+            override fun isCurrentMediaItemSeekable(): Boolean {
+                return false
+            }
+
+            override fun isPlayingAd(): Boolean {
+                return false
+            }
+
+            override fun getCurrentAdGroupIndex(): Int {
+                return 0
+            }
+
+            override fun getCurrentAdIndexInAdGroup(): Int {
+                return 0
+            }
+
+            override fun getContentDuration(): Long {
+                return 0
+            }
+
+            override fun getContentPosition(): Long {
+                return 0
+            }
+
+            override fun getContentBufferedPosition(): Long {
+                return 0
+            }
+
+            override fun getAudioAttributes(): AudioAttributes {
+                return AudioAttributes.DEFAULT
+            }
+
+            override fun setVolume(volume: Float) {
+            }
+
+            override fun getVolume(): Float {
+                return 0f
+            }
+
+            override fun clearVideoSurface() {
+            }
+
+            override fun clearVideoSurface(surface: Surface?) {
+            }
+
+            override fun setVideoSurface(surface: Surface?) {
+            }
+
+            override fun setVideoSurfaceHolder(surfaceHolder: SurfaceHolder?) {
+            }
+
+            override fun clearVideoSurfaceHolder(surfaceHolder: SurfaceHolder?) {
+            }
+
+            override fun setVideoSurfaceView(surfaceView: SurfaceView?) {
+            }
+
+            override fun clearVideoSurfaceView(surfaceView: SurfaceView?) {
+            }
+
+            override fun setVideoTextureView(textureView: TextureView?) {
+            }
+
+            override fun clearVideoTextureView(textureView: TextureView?) {
+            }
+
+            override fun getVideoSize(): VideoSize {
+                return VideoSize(0, 0)
+            }
+
+            override fun getSurfaceSize(): Size {
+                return Size(0, 0)
+            }
+
+            override fun getCurrentCues(): CueGroup {
+                return CueGroup.EMPTY_TIME_ZERO
+            }
+
+            override fun getDeviceInfo(): DeviceInfo {
+                return DeviceInfo.UNKNOWN
+            }
+
+            override fun getDeviceVolume(): Int {
+                return 0
+            }
+
+            override fun isDeviceMuted(): Boolean {
+                return false
+            }
+
+            @Deprecated("Deprecated in Java")
+            override fun setDeviceVolume(volume: Int) {}
+
+            override fun setDeviceVolume(volume: Int, flags: Int) {}
+
+            @Deprecated("Deprecated in Java")
+            override fun increaseDeviceVolume() {}
+
+            override fun increaseDeviceVolume(flags: Int) {}
+
+            @Deprecated("Deprecated in Java")
+            override fun decreaseDeviceVolume() {}
+
+            override fun decreaseDeviceVolume(flags: Int) {}
+
+            @Deprecated("Deprecated in Java")
+            override fun setDeviceMuted(muted: Boolean) {}
+
+            override fun setDeviceMuted(muted: Boolean, flags: Int) {}
+
         }
 
     }

+ 14 - 7
tvlib/src/main/kotlin/org/mjdev/tvlib/ui/components/media/MediaPlayerContainer.kt

@@ -28,15 +28,22 @@ fun MediaPlayerContainer(
     autoPlay: Boolean = true,
     startSeek: Long = 0L,
     mediaPlayer: IMediaPlayer = MediaPlayerContainerDefaults.exoPlayer,
-    background: @Composable (state: MediaPlayerState) -> Unit = {},
     mediaPlayerOverlay: @Composable (state: MediaPlayerState) -> Unit = {},
-    mediaPlayerControls: @Composable (state: MediaPlayerState) -> Unit = {}
+    mediaPlayerControls: @Composable (state: MediaPlayerState) -> Unit = { state ->
+        MediaPlayerControls(state)
+    }
 ) {
-    val state = rememberMediaPlayerState(uri ?: Uri.EMPTY, autoPlay, startSeek)
+    val state = rememberMediaPlayerState(
+        mediaPlayer,
+        uri ?: Uri.EMPTY,
+        autoPlay,
+        startSeek
+    )
     Box(
-        modifier = modifier.recomposeHighlighter().fillMaxSize()
+        modifier = modifier
+            .recomposeHighlighter()
+            .fillMaxSize()
     ) {
-        background(state)
         mediaPlayer.GetPlayerView()
         mediaPlayerOverlay(state)
         mediaPlayerControls(state)
@@ -53,8 +60,8 @@ fun MediaPlayerContainer(
             }
         }
         onDispose {
-            mediaPlayer.stop()
-            mediaPlayer.dispose()
+            state.stop()
+            state.dispose()
         }
     }
 }

+ 9 - 9
tvlib/src/main/kotlin/org/mjdev/tvlib/ui/components/media/MediaPlayerContainerDefaults.kt

@@ -26,7 +26,7 @@ object MediaPlayerContainerDefaults {
         @Composable
         get() = arrayOf(
             exoPlayer,
-            internalPlayer
+//            internalPlayer
         )
 
     val exoPlayer: IMediaPlayer
@@ -39,13 +39,13 @@ object MediaPlayerContainerDefaults {
             }
         }
 
-    val internalPlayer: IMediaPlayer
-        @Composable
-        get() = run {
-            if (isEdit) IMediaPlayer.EMPTY
-            else remember {
-                NativePlayerImpl(MediaPlayer())
-            }
-        }
+//    val internalPlayer: IMediaPlayer
+//        @Composable
+//        get() = run {
+//            if (isEdit) IMediaPlayer.EMPTY
+//            else remember {
+//                NativePlayerImpl(MediaPlayer())
+//            }
+//        }
 
 }

+ 44 - 0
tvlib/src/main/kotlin/org/mjdev/tvlib/ui/components/media/MediaPlayerControls.kt

@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) Milan Jurkulák 2023.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package org.mjdev.tvlib.ui.components.media
+
+import android.annotation.SuppressLint
+import android.view.View
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.allViews
+import androidx.media3.ui.PlayerControlView
+import org.mjdev.tvlib.annotations.TvPreview
+import org.mjdev.tvlib.ui.components.media.MediaPlayerState.Companion.rememberMediaPlayerState
+
+@SuppressLint("PrivateResource", "ModifierParameter")
+@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
+@TvPreview
+@Composable
+fun MediaPlayerControls(
+    state: MediaPlayerState = rememberMediaPlayerState(null),
+    modifier: Modifier = Modifier,
+    views: MutableMap<Int, View?> = remember { mutableMapOf() }
+) {
+    AndroidView(
+        modifier = modifier.fillMaxSize(),
+        factory = { context ->
+            PlayerControlView(context).apply {
+                player = state.player
+                allViews.forEach { v ->
+                    if (v.id >= 0) views[v.id] = v
+                }
+            }
+        }
+    )
+
+}

+ 10 - 0
tvlib/src/main/kotlin/org/mjdev/tvlib/ui/components/media/MediaPlayerState.kt

@@ -17,16 +17,19 @@ import androidx.compose.runtime.remember
 
 @Suppress("unused", "MemberVisibilityCanBePrivate")
 class MediaPlayerState(
+    val player: IMediaPlayer?,
     uri: Uri = Uri.EMPTY,
     autoPlay: Boolean = true,
     startSeek: Long = 0,
 ) {
 
     constructor(
+        player: IMediaPlayer?,
         uri: String? = null,
         autoPlay: Boolean = true,
         startSeek: Long = 0
     ) : this(
+        player,
         if (uri === null) Uri.EMPTY else Uri.parse(uri),
         autoPlay,
         startSeek
@@ -59,15 +62,22 @@ class MediaPlayerState(
         seek.value = ms
     }
 
+    fun dispose() {
+        player?.release()
+        player?.dispose()
+    }
+
     companion object {
 
         @Composable
         fun rememberMediaPlayerState(
+            player: IMediaPlayer?,
             uri: Uri = Uri.EMPTY,
             autoPlay: Boolean = true,
             startSeek: Long = 0
         ) = remember {
             MediaPlayerState(
+                player,
                 uri,
                 autoPlay,
                 startSeek

+ 604 - 91
tvlib/src/main/kotlin/org/mjdev/tvlib/ui/components/media/NativePlayerImpl.kt

@@ -1,91 +1,604 @@
-/*
- * Copyright (c) Milan Jurkulák 2023.
- *  Contact:
- *  e: mimoccc@gmail.com
- *  e: mj@mjdev.org
- *  w: https://mjdev.org
- */
-
-package org.mjdev.tvlib.ui.components.media
-
-import android.media.MediaPlayer
-import android.net.Uri
-import android.view.SurfaceView
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.size
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.platform.LocalConfiguration
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.viewinterop.AndroidView
-import org.mjdev.tvlib.extensions.ComposeExt
-import org.mjdev.tvlib.extensions.ModifierExt.recomposeHighlighter
-
-// todo
-@Suppress("unused")
-class NativePlayerImpl(
-    private val mediaPlayer: MediaPlayer
-) : IMediaPlayer {
-
-    @Composable
-    override fun GetPlayerView() {
-        val width = LocalConfiguration.current.screenWidthDp
-        val height = LocalConfiguration.current.screenHeightDp
-        val isEdit = ComposeExt.isEditMode()
-        Box(
-            modifier = Modifier
-                .recomposeHighlighter()
-                .size(width.dp, height.dp)
-                .background(Color.Black, RectangleShape)
-        ) {
-            if (!isEdit) {
-                AndroidView(
-                    modifier = Modifier
-                        .size(width.dp, height.dp)
-                        .recomposeHighlighter(),
-                    factory = { context ->
-                        SurfaceView(context).apply {
-                            mediaPlayer.setDisplay(holder)
-                        }
-                    }
-                )
-            }
-        }
-    }
-
-    override fun setMediaUri(uri: Uri) {
-        mediaPlayer.setDataSource(uri.toString())
-    }
-
-    override fun prepare() {
-        mediaPlayer.prepareAsync()
-    }
-
-    override fun play() {
-        mediaPlayer.start()
-    }
-
-    override fun pause() {
-        mediaPlayer.pause()
-    }
-
-    override fun resume() {
-        mediaPlayer.start()
-    }
-
-    override fun stop() {
-        mediaPlayer.stop()
-    }
-
-    override fun dispose() {
-        mediaPlayer.release()
-    }
-
-    override fun seekTo(value: Long) {
-        mediaPlayer.seekTo(value.toInt())
-    }
-
-}
+///*
+// * Copyright (c) Milan Jurkulák 2023.
+// *  Contact:
+// *  e: mimoccc@gmail.com
+// *  e: mj@mjdev.org
+// *  w: https://mjdev.org
+// */
+//
+//package org.mjdev.tvlib.ui.components.media
+//
+//import android.media.MediaPlayer
+//import android.net.Uri
+//import android.os.Looper
+//import android.view.Surface
+//import android.view.SurfaceHolder
+//import android.view.SurfaceView
+//import android.view.TextureView
+//import androidx.compose.foundation.background
+//import androidx.compose.foundation.layout.Box
+//import androidx.compose.foundation.layout.size
+//import androidx.compose.runtime.Composable
+//import androidx.compose.ui.Modifier
+//import androidx.compose.ui.graphics.Color
+//import androidx.compose.ui.graphics.RectangleShape
+//import androidx.compose.ui.platform.LocalConfiguration
+//import androidx.compose.ui.unit.dp
+//import androidx.compose.ui.viewinterop.AndroidView
+//import androidx.media3.common.AudioAttributes
+//import androidx.media3.common.DeviceInfo
+//import androidx.media3.common.MediaItem
+//import androidx.media3.common.MediaMetadata
+//import androidx.media3.common.PlaybackException
+//import androidx.media3.common.PlaybackParameters
+//import androidx.media3.common.Player
+//import androidx.media3.common.Timeline
+//import androidx.media3.common.TrackSelectionParameters
+//import androidx.media3.common.Tracks
+//import androidx.media3.common.VideoSize
+//import androidx.media3.common.text.CueGroup
+//import androidx.media3.common.util.Size
+//import org.mjdev.tvlib.extensions.ComposeExt
+//import org.mjdev.tvlib.extensions.ModifierExt.recomposeHighlighter
+//
+//// todo
+//@Suppress("unused")
+//class NativePlayerImpl(
+//    private val mediaPlayer: MediaPlayer
+//) : IMediaPlayer {
+//
+//    @Composable
+//    override fun GetPlayerView() {
+//        val width = LocalConfiguration.current.screenWidthDp
+//        val height = LocalConfiguration.current.screenHeightDp
+//        val isEdit = ComposeExt.isEditMode()
+//        Box(
+//            modifier = Modifier
+//                .recomposeHighlighter()
+//                .size(width.dp, height.dp)
+//                .background(Color.Black, RectangleShape)
+//        ) {
+//            if (!isEdit) {
+//                AndroidView(
+//                    modifier = Modifier
+//                        .size(width.dp, height.dp)
+//                        .recomposeHighlighter(),
+//                    factory = { context ->
+//                        SurfaceView(context).apply {
+//                            mediaPlayer.setDisplay(holder)
+//                        }
+//                    }
+//                )
+//            }
+//        }
+//    }
+//
+//    override fun setMediaUri(uri: Uri) {
+//        mediaPlayer.setDataSource(uri.toString())
+//    }
+//
+//    override fun prepare() {
+//        mediaPlayer.prepareAsync()
+//    }
+//
+//    override fun play() {
+//        mediaPlayer.start()
+//    }
+//
+//    override fun pause() {
+//        mediaPlayer.pause()
+//    }
+//
+//    override fun resume() {
+//        mediaPlayer.start()
+//    }
+//
+//    override fun stop() {
+//        mediaPlayer.stop()
+//    }
+//
+//    override fun dispose() {
+//        mediaPlayer.release()
+//    }
+//
+//    override fun seekTo(value: Long) {
+//        mediaPlayer.seekTo(value.toInt())
+//    }
+//
+//    override fun seekTo(mediaItemIndex: Int, positionMs: Long) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getApplicationLooper(): Looper {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun addListener(listener: Player.Listener) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun removeListener(listener: Player.Listener) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setMediaItems(mediaItems: MutableList<MediaItem>) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setMediaItems(mediaItems: MutableList<MediaItem>, resetPosition: Boolean) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setMediaItems(
+//        mediaItems: MutableList<MediaItem>,
+//        startIndex: Int,
+//        startPositionMs: Long
+//    ) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setMediaItem(mediaItem: MediaItem) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setMediaItem(mediaItem: MediaItem, startPositionMs: Long) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setMediaItem(mediaItem: MediaItem, resetPosition: Boolean) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun addMediaItem(mediaItem: MediaItem) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun addMediaItem(index: Int, mediaItem: MediaItem) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun addMediaItems(mediaItems: MutableList<MediaItem>) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun addMediaItems(index: Int, mediaItems: MutableList<MediaItem>) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun moveMediaItem(currentIndex: Int, newIndex: Int) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun moveMediaItems(fromIndex: Int, toIndex: Int, newIndex: Int) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun replaceMediaItem(index: Int, mediaItem: MediaItem) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun replaceMediaItems(
+//        fromIndex: Int,
+//        toIndex: Int,
+//        mediaItems: MutableList<MediaItem>
+//    ) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun removeMediaItem(index: Int) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun removeMediaItems(fromIndex: Int, toIndex: Int) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun clearMediaItems() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun isCommandAvailable(command: Int): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun canAdvertiseSession(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getAvailableCommands(): Player.Commands {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getPlaybackState(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getPlaybackSuppressionReason(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun isPlaying(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getPlayerError(): PlaybackException? {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setPlayWhenReady(playWhenReady: Boolean) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getPlayWhenReady(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setRepeatMode(repeatMode: Int) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getRepeatMode(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setShuffleModeEnabled(shuffleModeEnabled: Boolean) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getShuffleModeEnabled(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun isLoading(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun seekToDefaultPosition() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun seekToDefaultPosition(mediaItemIndex: Int) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getSeekBackIncrement(): Long {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun seekBack() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getSeekForwardIncrement(): Long {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun seekForward() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun hasPrevious(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun hasPreviousWindow(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun hasPreviousMediaItem(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun previous() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun seekToPreviousWindow() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun seekToPreviousMediaItem() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getMaxSeekToPreviousPosition(): Long {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun seekToPrevious() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun hasNext(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun hasNextWindow(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun hasNextMediaItem(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun next() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun seekToNextWindow() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun seekToNextMediaItem() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun seekToNext() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setPlaybackParameters(playbackParameters: PlaybackParameters) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setPlaybackSpeed(speed: Float) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getPlaybackParameters(): PlaybackParameters {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun release() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getCurrentTracks(): Tracks {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getTrackSelectionParameters(): TrackSelectionParameters {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setTrackSelectionParameters(parameters: TrackSelectionParameters) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getMediaMetadata(): MediaMetadata {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getPlaylistMetadata(): MediaMetadata {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setPlaylistMetadata(mediaMetadata: MediaMetadata) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getCurrentManifest(): Any? {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getCurrentTimeline(): Timeline {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getCurrentPeriodIndex(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getCurrentWindowIndex(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getCurrentMediaItemIndex(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getNextWindowIndex(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getNextMediaItemIndex(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getPreviousWindowIndex(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getPreviousMediaItemIndex(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getCurrentMediaItem(): MediaItem? {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getMediaItemCount(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getMediaItemAt(index: Int): MediaItem {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getDuration(): Long {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getCurrentPosition(): Long {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getBufferedPosition(): Long {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getBufferedPercentage(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getTotalBufferedDuration(): Long {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun isCurrentWindowDynamic(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun isCurrentMediaItemDynamic(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun isCurrentWindowLive(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun isCurrentMediaItemLive(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getCurrentLiveOffset(): Long {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun isCurrentWindowSeekable(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun isCurrentMediaItemSeekable(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun isPlayingAd(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getCurrentAdGroupIndex(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getCurrentAdIndexInAdGroup(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getContentDuration(): Long {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getContentPosition(): Long {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getContentBufferedPosition(): Long {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getAudioAttributes(): AudioAttributes {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setVolume(volume: Float) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getVolume(): Float {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun clearVideoSurface() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun clearVideoSurface(surface: Surface?) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setVideoSurface(surface: Surface?) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setVideoSurfaceHolder(surfaceHolder: SurfaceHolder?) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun clearVideoSurfaceHolder(surfaceHolder: SurfaceHolder?) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setVideoSurfaceView(surfaceView: SurfaceView?) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun clearVideoSurfaceView(surfaceView: SurfaceView?) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setVideoTextureView(textureView: TextureView?) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun clearVideoTextureView(textureView: TextureView?) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getVideoSize(): VideoSize {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getSurfaceSize(): Size {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getCurrentCues(): CueGroup {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getDeviceInfo(): DeviceInfo {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun getDeviceVolume(): Int {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun isDeviceMuted(): Boolean {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setDeviceVolume(volume: Int) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setDeviceVolume(volume: Int, flags: Int) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun increaseDeviceVolume() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun increaseDeviceVolume(flags: Int) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun decreaseDeviceVolume() {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun decreaseDeviceVolume(flags: Int) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setDeviceMuted(muted: Boolean) {
+//        TODO("Not yet implemented")
+//    }
+//
+//    override fun setDeviceMuted(muted: Boolean, flags: Int) {
+//        TODO("Not yet implemented")
+//    }
+//
+//}

+ 0 - 18
tvlib/src/main/kotlin/org/mjdev/tvlib/ui/components/media/PlayerControls.kt

@@ -1,18 +0,0 @@
-/*
- * Copyright (c) Milan Jurkulák 2023.
- *  Contact:
- *  e: mimoccc@gmail.com
- *  e: mj@mjdev.org
- *  w: https://mjdev.org
- */
-
-package org.mjdev.tvlib.ui.components.media
-
-import androidx.compose.runtime.Composable
-import org.mjdev.tvlib.annotations.TvPreview
-
-// todo
-@TvPreview
-@Composable
-fun PlayerControls () {
-}