diff --git a/android/app/src/main/kotlin/com/edde746/plezy/ExternalPlayerChannel.kt b/android/app/src/main/kotlin/com/edde746/plezy/ExternalPlayerChannel.kt index efb509bf5..291f65347 100644 --- a/android/app/src/main/kotlin/com/edde746/plezy/ExternalPlayerChannel.kt +++ b/android/app/src/main/kotlin/com/edde746/plezy/ExternalPlayerChannel.kt @@ -28,6 +28,12 @@ internal class ExternalPlayerChannel(private val activity: Activity) { private const val API_VLC_RESULT_POSITION = "extra_position" private const val API_VLC_RESULT_DURATION = "extra_duration" + // Honored by VLC and the native Zidoo player (com.android.gallery3d / + // com.zidoo.player). Without it, a launch with no resume point lets the + // player consult its own bookmark store, which on Zidoo collides across + // Plex items because every part URL ends in the same `file.` (#2223). + private const val API_VLC_FROM_START = "from_start" + private const val API_VIMU_TITLE = "forcename" private const val API_VIMU_SEEK_POSITION = "startfrom" private const val API_VIMU_RESUME = "forceresume" @@ -150,7 +156,7 @@ internal class ExternalPlayerChannel(private val activity: Activity) { } } - private data class Source(val uri: Uri, val grantRead: Boolean, val fileName: String?) + internal data class Source(val uri: Uri, val grantRead: Boolean, val fileName: String?) private fun resolveSource(filePath: String): Source { if (filePath.startsWith("http://") || filePath.startsWith("https://")) { @@ -168,7 +174,7 @@ internal class ExternalPlayerChannel(private val activity: Activity) { return Source(uri, grantRead = true, fileName = file.name) } - private fun buildIntent( + internal fun buildIntent( source: Source, packageName: String?, startPositionMs: Long, @@ -181,6 +187,9 @@ internal class ExternalPlayerChannel(private val activity: Activity) { if (startPosition > 0) { putExtra(API_MX_RESULT_POSITION, startPosition) putExtra(API_VIMU_SEEK_POSITION, startPosition) + putExtra(API_VLC_FROM_START, false) + } else { + putExtra(API_VLC_FROM_START, true) } putExtra(API_MX_RETURN_RESULT, true) putExtra(API_MX_SECURE_URI, true) diff --git a/android/app/src/test/kotlin/com/edde746/plezy/ExternalPlayerChannelTest.kt b/android/app/src/test/kotlin/com/edde746/plezy/ExternalPlayerChannelTest.kt index 3ee03bf54..d4668f2be 100644 --- a/android/app/src/test/kotlin/com/edde746/plezy/ExternalPlayerChannelTest.kt +++ b/android/app/src/test/kotlin/com/edde746/plezy/ExternalPlayerChannelTest.kt @@ -2,8 +2,10 @@ package com.edde746.plezy import android.app.Activity import android.content.Intent +import android.net.Uri import io.flutter.plugin.common.MethodChannel import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith @@ -30,6 +32,40 @@ class ExternalPlayerChannelTest { assertEquals(false, result["playbackError"]) } + @Test + fun freshLaunchTellsPlayerToStartFromBeginning() { + val activity = Robolectric.buildActivity(Activity::class.java).get() + val channel = ExternalPlayerChannel(activity) + val source = ExternalPlayerChannel.Source( + Uri.parse("http://plex:32400/library/parts/9808/1775431760/file.mkv?X-Plex-Token=tok"), + grantRead = false, + fileName = "file.mkv" + ) + + val intent = channel.buildIntent(source, packageName = null, startPositionMs = 0L, title = "Episode") + + assertTrue(intent.getBooleanExtra("from_start", false)) + assertFalse(intent.hasExtra("position")) + assertFalse(intent.hasExtra("startfrom")) + } + + @Test + fun resumeLaunchPassesPositionAndDisablesFromStart() { + val activity = Robolectric.buildActivity(Activity::class.java).get() + val channel = ExternalPlayerChannel(activity) + val source = ExternalPlayerChannel.Source( + Uri.parse("http://plex:32400/library/parts/9808/1775431760/file.mkv?X-Plex-Token=tok"), + grantRead = false, + fileName = "file.mkv" + ) + + val intent = channel.buildIntent(source, packageName = null, startPositionMs = 90_000L, title = null) + + assertFalse(intent.getBooleanExtra("from_start", true)) + assertEquals(90_000, intent.getIntExtra("position", -1)) + assertEquals(90_000, intent.getIntExtra("startfrom", -1)) + } + @Test fun activityDestroyCompletesPendingChannelCall() { val activity = Robolectric.buildActivity(Activity::class.java).get()