All Downloads are FREE. Search and download functionalities are using the official Maven repository.

androidMain.tech.skot.libraries.video.SKAudioViewProxy.kt Maven / Gradle / Ivy

There is a newer version: 0.2.4_1.2.7
Show newest version
package tech.skot.libraries.video

import android.content.Context
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.Timeline
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory
import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
import com.google.android.exoplayer2.upstream.cache.CacheDataSource
import kotlinx.coroutines.*
import tech.skot.core.di.get

var skAudioViewProxy: SKAudioViewProxy? = null

class SKAudioViewProxy(applicationContext: Context) : SKAudioVC {

    override var progressRefreshInterval: Long = 1000L

    private val mapMediaItemTrack: MutableMap = mutableMapOf()

    override var keepActiveInBackGroundWithMessageIfNothingPlayed: String? = null

    var state: SKAudioVC.State =
        SKAudioVC.State(track = null, playing = false, position = null, duration = null)
        set(value) {
            field = value
            onState?.invoke(value)
        }


    private fun ExoPlayer.updateState() {
        val track = currentMediaItem?.let { mapMediaItemTrack[it] }
        state = SKAudioVC.State(
            track = track,
            playing = _playing,
            position = track?.let { player.currentPosition }?.let { if (it > 0) it else null },
            duration = track?.let { player.currentDuration() },
        )
    }

    private fun ExoPlayer.currentDuration() = player.duration.let { if (it > 0) it else null }

    var _player: ExoPlayer? = buildPlayer(applicationContext)

    val player: ExoPlayer
        get() = _player ?: throw IllegalStateException("Player released")

    fun renewIfNeeded(applicationContext: Context) {
        if (_player == null) {
            val toRestoredTrack = state.track
            _player = buildPlayer(applicationContext)
            setMediaListToPlayer(trackList)
            if (toRestoredTrack != null) {
                player.seekTo(trackList.indexOf(toRestoredTrack), savedPosition)
                state = SKAudioVC.State(
                    track = toRestoredTrack,
                    playing = _playing,
                    position = savedPosition,
                    duration = saveDuration
                )

            } else {
                player.seekTo(0, 0)
            }

        }
    }

    private fun getCacheDataSourceFactory(): DataSource.Factory {
        return CacheDataSource.Factory()
            .setCache(Cache.getCache(get()))
            .setUpstreamDataSourceFactory(DefaultHttpDataSource.Factory())
    }

    private fun buildPlayer(applicationContext: Context): ExoPlayer =
        ExoPlayer.Builder(applicationContext)
            .setMediaSourceFactory(DefaultMediaSourceFactory(getCacheDataSourceFactory()))
            .build()
            .apply {
                addListener(object : Player.Listener {
                    override fun onTimelineChanged(timeline: Timeline, reason: Int) {
                        super.onTimelineChanged(timeline, reason)
                        onDurations?.let { onDurations ->
                            val mapTrackDuration: MutableMap = mutableMapOf()
                            (0 until timeline.periodCount).forEach {
                                val mediaItem = getMediaItemAt(it)
                                mapMediaItemTrack[mediaItem]?.let { track ->
                                    val duration =
                                        timeline.getPeriod(it, Timeline.Period()).durationMs
                                    if (duration > 0) {
                                        mapTrackDuration[track] = duration
                                    }
                                }
                            }
                            onDurations(mapTrackDuration)
                        }

                    }


                    override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
                        super.onMediaItemTransition(mediaItem, reason)
                        updateState()

                    }

                    override fun onIsPlayingChanged(isPlaying: Boolean) {
                        if (isPlaying) {
                            startProgressListener()
                            _playing = true
                        } else {
                            stopProgressListener()
                            _playing = false
                        }
                        updateState()
                    }


                })
            }

    private var _playing: Boolean = false
    override var playing: Boolean
        get() = _playing
        set(value) {
            _playing = value
            if (value) {
                player.play()
            } else {
                player.pause()
            }

        }


    private var _media: List = emptyList()
    override var trackList: List
        get() = _media
        set(value) {
            _media = value
            player.clearMediaItems()
            mapMediaItemTrack.clear()
            setMediaListToPlayer(value)
            player.pause()
            player.seekTo(0, 0)
            _playing = false
            player.updateState()
        }

    override fun addTrack(track: SKAudioVC.Track) {
        if (trackList.isEmpty()) {
            trackList = listOf(track)
        } else {
            player.addMediaItem(
                track.toMediaItem().also { mapMediaItemTrack[it] = track }
            )
            _media = _media + track
        }
        player.updateState()
    }


    override fun hasNext():Boolean {
        return player.hasNextMediaItem()
    }

    override fun seekToLastTrack() {
        if (hasNext()) {
            player.seekTo(player.mediaItemCount - 1, 0)
            player.updateState()
        }
    }


    private fun setMediaListToPlayer(media: List) {
        player.addMediaItems(
            media.map { track ->
                track.toMediaItem()
                    .also {
                        mapMediaItemTrack[it] = track
                    }
            }
        )
        player.prepare()
        player.pause()
    }

    private fun SKAudioVC.Track.toMediaItem() = MediaItem.Builder()
        .setUri(url)
        .build()

    override fun setCurrentTrack(track: SKAudioVC.Track) {
        val index = trackList.indexOf(track)
        if (index != -1) {
            player.seekTo(index, 0)
            player.updateState()
        }
    }

    override var onState: ((state: SKAudioVC.State) -> Unit)? = null


    override var onDurations: ((durations: Map) -> Unit)? = null


    private var progressListenerJob: Job? = null
    private fun startProgressListener() {
        progressListenerJob?.cancel()
        progressListenerJob = CoroutineScope(Dispatchers.Main).launch {
            while (true) {
                delay(progressRefreshInterval)
                player.updateState()
            }

        }
    }

    private fun stopProgressListener() {
        progressListenerJob?.cancel()
    }


    private var savedPosition: Long = 0
    private var saveDuration: Long = 0
    override fun release() {
        savedPosition = _player?.currentPosition ?: 0L
        saveDuration = player.contentDuration
        _player?.release()
        _player = null
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy