@@ -35,6 +35,7 @@ import org.koin.android.ext.android.inject
3535import timber.log.Timber
3636import java.io.File
3737import java.time.Instant
38+ import java.util.UUID
3839import kotlin.time.Duration
3940import kotlin.time.Duration.Companion.milliseconds
4041
@@ -70,6 +71,10 @@ class ExternalPlayerActivity : FragmentActivity() {
7071
7172 // The extra keys used by various video players to read the end position
7273 private val resultPositionExtras = arrayOf(API_MX_RESULT_POSITION , API_VLC_RESULT_POSITION )
74+
75+ private const val STATE_ITEM_ID = " state_item_id"
76+ private const val STATE_MEDIA_SOURCE_ID = " state_media_source_id"
77+ private const val STATE_RUNTIME_TICKS = " state_runtime_ticks"
7378 }
7479
7580 private val videoQueueManager by inject<VideoQueueManager >()
@@ -96,13 +101,37 @@ class ExternalPlayerActivity : FragmentActivity() {
96101
97102 private var currentItem: Pair <BaseItemDto , MediaSourceInfo >? = null
98103
104+ private var savedItemId: UUID ? = null
105+ private var savedMediaSourceId: String? = null
106+ private var savedRuntimeTicks: Long? = null
107+
99108 override fun onCreate (savedInstanceState : Bundle ? ) {
100109 super .onCreate(savedInstanceState)
101110
111+ if (savedInstanceState != null ) {
112+ savedItemId = savedInstanceState.getString(STATE_ITEM_ID )?.toUUIDOrNull()
113+ savedMediaSourceId = savedInstanceState.getString(STATE_MEDIA_SOURCE_ID )
114+ savedRuntimeTicks = if (savedInstanceState.containsKey(STATE_RUNTIME_TICKS ))
115+ savedInstanceState.getLong(STATE_RUNTIME_TICKS ) else null
116+ Timber .i(" Restored external player state: itemId=$savedItemId " )
117+ return
118+ }
119+
102120 val position = intent.getLongExtra(EXTRA_POSITION , 0 ).milliseconds
103121 playNext(position)
104122 }
105123
124+ override fun onSaveInstanceState (outState : Bundle ) {
125+ super .onSaveInstanceState(outState)
126+
127+ val (item, mediaSource) = currentItem ? : return
128+ outState.putString(STATE_ITEM_ID , item.id.toString())
129+ outState.putString(STATE_MEDIA_SOURCE_ID , mediaSource.id)
130+ (mediaSource.runTimeTicks ? : item.runTimeTicks)?.let { ticks ->
131+ outState.putLong(STATE_RUNTIME_TICKS , ticks)
132+ }
133+ }
134+
106135 private fun playNext (position : Duration = Duration .ZERO ) {
107136 val currentPosition = videoQueueManager.getCurrentMediaPosition()
108137 val item = videoQueueManager.getCurrentVideoQueue().getOrNull(currentPosition) ? : return finish()
@@ -184,6 +213,9 @@ class ExternalPlayerActivity : FragmentActivity() {
184213
185214 try {
186215 currentItem = item to mediaSource
216+ savedItemId = item.id
217+ savedMediaSourceId = mediaSource.id
218+ savedRuntimeTicks = mediaSource.runTimeTicks ? : item.runTimeTicks
187219 playVideoLauncher.launch(playIntent)
188220 } catch (_: ActivityNotFoundException ) {
189221 Toast .makeText(this , R .string.no_player_message, Toast .LENGTH_LONG ).show()
@@ -193,13 +225,6 @@ class ExternalPlayerActivity : FragmentActivity() {
193225
194226
195227 private fun onItemFinished (result : Intent ? ) {
196- if (currentItem == null ) {
197- Toast .makeText(this @ExternalPlayerActivity, R .string.video_error_unknown_error, Toast .LENGTH_LONG ).show()
198- finish()
199- return
200- }
201-
202- val (item, mediaSource) = currentItem!!
203228 val extras = result?.extras ? : Bundle .EMPTY
204229
205230 val endPosition = resultPositionExtras.firstNotNullOfOrNull { key ->
@@ -208,16 +233,27 @@ class ExternalPlayerActivity : FragmentActivity() {
208233 else null
209234 }
210235
211- val runtime = (mediaSource.runTimeTicks ? : item.runTimeTicks)?.ticks
212- val shouldPlayNext = runtime != null && endPosition != null && endPosition >= (runtime * 0.9 )
236+ val itemId = currentItem?.first?.id ? : savedItemId
237+ val mediaSourceId = currentItem?.second?.id ? : savedMediaSourceId
238+ val runtimeTicks = currentItem?.second?.runTimeTicks ? : currentItem?.first?.runTimeTicks ? : savedRuntimeTicks
239+
240+ if (itemId == null || mediaSourceId == null ) {
241+ Timber .w(" Cannot report playback stop: no item data available" )
242+ Toast .makeText(this @ExternalPlayerActivity, R .string.video_error_unknown_error, Toast .LENGTH_LONG ).show()
243+ finish()
244+ return
245+ }
246+
247+ val runtime = runtimeTicks?.ticks
248+ val shouldPlayNext = currentItem != null && runtime != null && endPosition != null && endPosition >= (runtime * 0.9 )
213249
214250 lifecycleScope.launch {
215251 runCatching {
216252 withContext(Dispatchers .IO ) {
217253 api.playStateApi.reportPlaybackStopped(
218254 PlaybackStopInfo (
219- itemId = item.id ,
220- mediaSourceId = mediaSource.id ,
255+ itemId = itemId ,
256+ mediaSourceId = mediaSourceId ,
221257 positionTicks = endPosition?.inWholeTicks,
222258 failed = false ,
223259 )
@@ -228,10 +264,11 @@ class ExternalPlayerActivity : FragmentActivity() {
228264 Toast .makeText(this @ExternalPlayerActivity, R .string.video_error_unknown_error, Toast .LENGTH_LONG ).show()
229265 }
230266
231- dataRefreshService.lastPlayback = Instant .now()
232- when (item.type) {
233- BaseItemKind .MOVIE -> dataRefreshService.lastMoviePlayback = Instant .now()
234- BaseItemKind .EPISODE -> dataRefreshService.lastTvPlayback = Instant .now()
267+ val now = Instant .now()
268+ dataRefreshService.lastPlayback = now
269+ when (currentItem?.first?.type) {
270+ BaseItemKind .MOVIE -> dataRefreshService.lastMoviePlayback = now
271+ BaseItemKind .EPISODE -> dataRefreshService.lastTvPlayback = now
235272 else -> Unit
236273 }
237274
0 commit comments