fix(player): playback position reset on process death and playback continues playing when the device is locked

Closes #389 #390
This commit is contained in:
Jarne Demeulemeester 2023-07-09 15:15:15 +02:00
parent 58cbd9cf9d
commit 7a3b67f64b
No known key found for this signature in database
GPG key ID: 1E5C6AFBD622E9F5
4 changed files with 22 additions and 8 deletions

View file

@ -24,6 +24,7 @@
<activity <activity
android:name=".PlayerActivity" android:name=".PlayerActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:screenOrientation="sensorLandscape" /> android:screenOrientation="sensorLandscape" />
<activity <activity

View file

@ -30,6 +30,7 @@ abstract class BasePlayerActivity : AppCompatActivity() {
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
viewModel.playWhenReady = viewModel.player.playWhenReady == true viewModel.playWhenReady = viewModel.player.playWhenReady == true
viewModel.player.playWhenReady = false viewModel.player.playWhenReady = false
} }

View file

@ -27,7 +27,6 @@ import dev.jdtech.jellyfin.mpv.TrackType
import dev.jdtech.jellyfin.utils.PlayerGestureHelper import dev.jdtech.jellyfin.utils.PlayerGestureHelper
import dev.jdtech.jellyfin.utils.PreviewScrubListener import dev.jdtech.jellyfin.utils.PreviewScrubListener
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import dev.jdtech.jellyfin.player.video.R as PlayerVideoR import dev.jdtech.jellyfin.player.video.R as PlayerVideoR
@ -46,7 +45,6 @@ class PlayerActivity : BasePlayerActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Timber.d("Creating player activity")
binding = ActivityPlayerBinding.inflate(layoutInflater) binding = ActivityPlayerBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)

View file

@ -5,6 +5,7 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.media3.common.AudioAttributes import androidx.media3.common.AudioAttributes
@ -44,6 +45,7 @@ constructor(
private val application: Application, private val application: Application,
private val jellyfinRepository: JellyfinRepository, private val jellyfinRepository: JellyfinRepository,
private val appPreferences: AppPreferences, private val appPreferences: AppPreferences,
private val savedStateHandle: SavedStateHandle,
) : ViewModel(), Player.Listener { ) : ViewModel(), Player.Listener {
val player: Player val player: Player
@ -71,8 +73,8 @@ constructor(
val trackSelector = DefaultTrackSelector(application) val trackSelector = DefaultTrackSelector(application)
var playWhenReady = true var playWhenReady = true
private var currentMediaItemIndex = 0 private var currentMediaItemIndex = savedStateHandle["mediaItemIndex"] ?: 0
private var playbackPosition: Long = 0 private var playbackPosition: Long = savedStateHandle["position"] ?: 0
var playbackSpeed: Float = 1f var playbackSpeed: Float = 1f
var disableSubtitle: Boolean = false var disableSubtitle: Boolean = false
@ -116,6 +118,10 @@ constructor(
fun initializePlayer( fun initializePlayer(
items: Array<PlayerItem>, items: Array<PlayerItem>,
) { ) {
// Skip initialization when there are already items
if (this.items.isNotEmpty()) {
return
}
this.items = items this.items = items
player.addListener(this) player.addListener(this)
@ -156,10 +162,16 @@ constructor(
Timber.e(e) Timber.e(e)
} }
val startPosition = if (playbackPosition == 0L) {
items.getOrNull(currentMediaItemIndex)?.playbackPosition ?: C.TIME_UNSET
} else {
playbackPosition
}
player.setMediaItems( player.setMediaItems(
mediaItems, mediaItems,
currentMediaItemIndex, currentMediaItemIndex,
items.getOrNull(currentMediaItemIndex)?.playbackPosition ?: C.TIME_UNSET, startPosition,
) )
if (appPreferences.playerMpv) { // For some reason, adding a 1ms delay between these two lines fixes a crash when playing with mpv from downloads if (appPreferences.playerMpv) { // For some reason, adding a 1ms delay between these two lines fixes a crash when playing with mpv from downloads
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
@ -191,9 +203,9 @@ constructor(
} }
_currentTrickPlay.value = null _currentTrickPlay.value = null
playWhenReady = player.playWhenReady playWhenReady = false
playbackPosition = position playbackPosition = 0L
currentMediaItemIndex = player.currentMediaItemIndex currentMediaItemIndex = 0
player.removeListener(this) player.removeListener(this)
player.release() player.release()
} }
@ -201,6 +213,7 @@ constructor(
private fun pollPosition(player: Player) { private fun pollPosition(player: Player) {
val playbackProgressRunnable = object : Runnable { val playbackProgressRunnable = object : Runnable {
override fun run() { override fun run() {
savedStateHandle["position"] = player.currentPosition
viewModelScope.launch { viewModelScope.launch {
if (player.currentMediaItem != null && player.currentMediaItem!!.mediaId.isNotEmpty()) { if (player.currentMediaItem != null && player.currentMediaItem!!.mediaId.isNotEmpty()) {
val itemId = UUID.fromString(player.currentMediaItem!!.mediaId) val itemId = UUID.fromString(player.currentMediaItem!!.mediaId)
@ -240,6 +253,7 @@ constructor(
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) { override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
Timber.d("Playing MediaItem: ${mediaItem?.mediaId}") Timber.d("Playing MediaItem: ${mediaItem?.mediaId}")
savedStateHandle["mediaItemIndex"] = player.currentMediaItemIndex
viewModelScope.launch { viewModelScope.launch {
try { try {
items.first { it.itemId.toString() == player.currentMediaItem?.mediaId } items.first { it.itemId.toString() == player.currentMediaItem?.mediaId }