From 709264ec1c3eb93d4c5fa2ddffa99f90c488aa10 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Sat, 15 Aug 2026 01:30:13 +0200 Subject: [PATCH] fix(cast): use custom notif instead of builtin one --- android/src/main/AndroidManifest.xml | 7 -- .../zoriya/omni/OmniCastOptionsProvider.kt | 20 +--- .../dev/zoriya/omni/OmniCastTapActivity.kt | 30 ------ .../main/java/dev/zoriya/omni/OmniPlayer.kt | 101 +++++++++--------- .../java/dev/zoriya/omni/OmniPlayerFactory.kt | 6 +- .../c++/JHybridOmniPlayerFactorySpec.cpp | 32 +----- .../c++/JHybridOmniPlayerFactorySpec.hpp | 2 +- .../android/c++/JHybridOmniPlayerSpec.cpp | 4 +- .../android/c++/JHybridOmniPlayerSpec.hpp | 2 +- .../nitro/omni/HybridOmniPlayerFactorySpec.kt | 2 +- .../nitro/omni/HybridOmniPlayerSpec.kt | 2 +- .../c++/HybridOmniPlayerFactorySpec.hpp | 7 +- .../shared/c++/HybridOmniPlayerSpec.cpp | 2 +- .../shared/c++/HybridOmniPlayerSpec.hpp | 2 +- src/provider.tsx | 9 +- src/specs/omni-player.nitro.ts | 9 +- 16 files changed, 80 insertions(+), 157 deletions(-) delete mode 100644 android/src/main/java/dev/zoriya/omni/OmniCastTapActivity.kt diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 7db277c..de57c05 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -16,12 +16,5 @@ - diff --git a/android/src/main/java/dev/zoriya/omni/OmniCastOptionsProvider.kt b/android/src/main/java/dev/zoriya/omni/OmniCastOptionsProvider.kt index 2e5a2b9..a29d095 100644 --- a/android/src/main/java/dev/zoriya/omni/OmniCastOptionsProvider.kt +++ b/android/src/main/java/dev/zoriya/omni/OmniCastOptionsProvider.kt @@ -4,8 +4,6 @@ import android.content.Context import com.google.android.gms.cast.CastMediaControlIntent import com.google.android.gms.cast.framework.CastOptions import com.google.android.gms.cast.framework.media.CastMediaOptions -import com.google.android.gms.cast.framework.media.MediaIntentReceiver -import com.google.android.gms.cast.framework.media.NotificationOptions import com.google.android.gms.cast.framework.OptionsProvider import com.google.android.gms.cast.framework.SessionProvider @@ -14,23 +12,9 @@ class OmniCastOptionsProvider : OptionsProvider { val appId = OmniPlayer.receiverApplicationId ?: CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID - val notificationOptions = NotificationOptions.Builder() - .setActions( - listOf( - MediaIntentReceiver.ACTION_SKIP_PREV, - MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK, - MediaIntentReceiver.ACTION_SKIP_NEXT, - ), - intArrayOf(0, 1, 2), - ) - .setSmallIconDrawableResId( - androidx.media3.session.R.drawable.media3_notification_small_icon - ) - .setTargetActivityClassName(OmniCastTapActivity::class.java.name) - .build() - val mediaOptions = CastMediaOptions.Builder() - .setNotificationOptions(notificationOptions) + .setMediaSessionEnabled(false) + .setNotificationOptions(null) .build() return CastOptions.Builder() diff --git a/android/src/main/java/dev/zoriya/omni/OmniCastTapActivity.kt b/android/src/main/java/dev/zoriya/omni/OmniCastTapActivity.kt deleted file mode 100644 index 2fc3719..0000000 --- a/android/src/main/java/dev/zoriya/omni/OmniCastTapActivity.kt +++ /dev/null @@ -1,30 +0,0 @@ -package dev.zoriya.omni - -import android.app.Activity -import android.content.Context -import android.content.Intent -import android.os.Bundle -import androidx.core.net.toUri - -class OmniCastTapActivity : Activity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val url = getSharedPreferences(OmniPlayer.OMNI_PREFS, Context.MODE_PRIVATE) - .getString(OmniPlayer.NOTIFICATION_URL_PREF, null) - val intent = url - ?.let { Intent(Intent.ACTION_VIEW, it.toUri()).setPackage(packageName) } - ?: packageManager.getLaunchIntentForPackage(packageName) - - try { - startActivity( - intent?.apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP) - } - ) - } catch (e: Throwable) { - android.util.Log.w("OmniPlayer", "could not open $url", e) - } - finish() - } -} diff --git a/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt b/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt index b9eea17..c4b6f14 100644 --- a/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt +++ b/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt @@ -104,7 +104,8 @@ class OmniPlayer( private val castStateListener = CastStateListener { eventMap.emitCastStatus(computeCastStatus()) listenToReceiver() - syncNotificationService() + // we were only kept alive to hold this cast, it is over now + if (abandoned && !isCasting) release() } // receivers have no queue to skip in (a queue would make them play things on @@ -128,12 +129,7 @@ class OmniPlayer( val player: Player = runOnMainThreadSync { castOptions?.receiverApplicationId?.let { receiverApplicationId = it } - // the cast notification outlives the app, so OmniCastTapActivity has to find the - // url back even when nothing of ours ran in that process yet. - ctx.getSharedPreferences(OMNI_PREFS, Context.MODE_PRIVATE) - .edit() - .putString(NOTIFICATION_URL_PREF, castOptions?.notificationUrl) - .apply() + notificationUrl = castOptions?.notificationUrl val cc = try { CastContext.getSharedInstance(ctx) } catch (_: Throwable) { @@ -172,24 +168,26 @@ class OmniPlayer( private var serviceRunning = false private fun syncNotificationService() { - val shouldShow = showNotification == true && source != null && - !runOnMainThreadSync { isCasting } + if (abandoned) return + val shouldShow = showNotification == true && source != null when { shouldShow && !serviceRunning -> { val otherIsPlaying = notificationPlayer?.let { other -> - other !== player && runOnMainThreadSync { other.isPlaying } + // an abandoned one only holds it until the cast it kept alive ends + other !== this && !other.abandoned && + runOnMainThreadSync { other.player.isPlaying } } == true if (otherIsPlaying) { throw Error("Two players can't display notifications at the same time.") } - notificationPlayer = player + notificationPlayer = this ctx.startForegroundService(Intent(ctx, OmniPlayerService::class.java)) serviceRunning = true } !shouldShow && serviceRunning -> { ctx.stopService(Intent(ctx, OmniPlayerService::class.java)) - if (notificationPlayer == player) notificationPlayer = null + if (notificationPlayer === this) notificationPlayer = null serviceRunning = false } } @@ -229,22 +227,36 @@ class OmniPlayer( } } + // the app let go of us. while casting we stay alive anyway: OmniPlayerService holds the + // process (and thus the session) up, and its notification is the only remote left - we + // keep owning both until castStateListener sees the cast end. @Volatile - private var released = false + private var abandoned = false - override fun release() { - if (released) return - released = true - showNotification = false + override fun abandon() { + if (abandoned) return + abandoned = true + if (!runOnMainThreadSync { isCasting }) release() + } + + // drop everything. this ends the cast session, so it only runs once there is no cast + // left to hold (either there was none, or it just ended). + private fun release() { runOnMainThread { - eventMap.dispose() castContext?.removeCastStateListener(castStateListener) - if (!isCasting) player.release() + if (notificationPlayer === this) { + // syncNotificationService ignores us now, hand the service back ourselves + ctx.stopService(Intent(ctx, OmniPlayerService::class.java)) + notificationPlayer = null + serviceRunning = false + } + eventMap.dispose() + player.release() } } override fun dispose() { - release() + abandon() super.dispose() } @@ -641,7 +653,7 @@ class OmniPlayer( } companion object { - var notificationPlayer: Player? = null + var notificationPlayer: OmniPlayer? = null // MediaItem RequestMetadata extras keys carrying cast-only data. const val CAST_ID_EXTRA = "dev.zoriya.omni.castId" @@ -655,9 +667,8 @@ class OmniPlayer( // prop; read by OmniCastOptionsProvider when the Cast SDK initializes. var receiverApplicationId: String? = null - // `cast.notificationUrl`, read back by OmniCastTapActivity. - const val OMNI_PREFS = "dev.zoriya.omni" - const val NOTIFICATION_URL_PREF = "notificationUrl" + // `cast.notificationUrl`, opened when our notification is tapped. + var notificationUrl: String? = null } } @@ -668,24 +679,14 @@ class OmniPlayerService : MediaSessionService() { override fun onCreate() { super.onCreate() - val available = OmniPlayer.notificationPlayer + val available = OmniPlayer.notificationPlayer?.player if (available == null) { startForeground(1, createImmediateNotification()) stopSelf() return } player = available - val launchIntent = packageManager.getLaunchIntentForPackage(packageName)?.apply { - addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) - } - val sessionActivity = launchIntent?.let { - PendingIntent.getActivity( - this, - 0, - it, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, - ) - } + val sessionActivity = openIntent() mediaSession = MediaSession.Builder(this, player) .apply { sessionActivity?.let { setSessionActivity(it) } @@ -701,7 +702,7 @@ class OmniPlayerService : MediaSessionService() { } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - val current = OmniPlayer.notificationPlayer + val current = OmniPlayer.notificationPlayer?.player if (current != null && ::mediaSession.isInitialized && mediaSession.player !== current) { player = current mediaSession.player = current @@ -721,25 +722,29 @@ class OmniPlayerService : MediaSessionService() { manager.createNotificationChannel(channel) } - val launchIntent = packageManager.getLaunchIntentForPackage(packageName)?.apply { - addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) - } - val pendingIntent = PendingIntent.getActivity( - this, - 0, - launchIntent, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - ) - return NotificationCompat.Builder(this, "omni_playback") .setSmallIcon(androidx.media3.session.R.drawable.media3_notification_small_icon) .setContentTitle("Omni Player") .setContentText("Preparing playback...") - .setContentIntent(pendingIntent) + .setContentIntent(openIntent()) .setOngoing(true) .build() } + private fun openIntent(): PendingIntent? { + val intent = OmniPlayer.notificationUrl + ?.let { Intent(Intent.ACTION_VIEW, it.toUri()).setPackage(packageName) } + ?: packageManager.getLaunchIntentForPackage(packageName) + ?: return null + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) + return PendingIntent.getActivity( + this, + 0, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, + ) + } + override fun onGetSession(controllerInfo: MediaSession.ControllerInfo) = mediaSession override fun onTaskRemoved(rootIntent: Intent?) { diff --git a/android/src/main/java/dev/zoriya/omni/OmniPlayerFactory.kt b/android/src/main/java/dev/zoriya/omni/OmniPlayerFactory.kt index b5f9d62..22e58dc 100755 --- a/android/src/main/java/dev/zoriya/omni/OmniPlayerFactory.kt +++ b/android/src/main/java/dev/zoriya/omni/OmniPlayerFactory.kt @@ -6,16 +6,12 @@ import com.margelo.nitro.omni.CastOptions import com.margelo.nitro.omni.HybridOmniPlayerFactorySpec import com.margelo.nitro.omni.HybridOmniPlayerSpec import com.margelo.nitro.omni.PlayerBackend -import com.margelo.nitro.omni.Source class OmniPlayerFactory(val context: ThemedReactContext) : HybridOmniPlayerFactorySpec() { override fun createPlayer( - props: Source?, backend: PlayerBackend?, cast: CastOptions?, ): HybridOmniPlayerSpec { - return OmniPlayer(backend?.android ?: AndroidBackend.VLC, cast).apply { - source = props - } + return OmniPlayer(backend?.android ?: AndroidBackend.VLC, cast) } } diff --git a/nitrogen/generated/android/c++/JHybridOmniPlayerFactorySpec.cpp b/nitrogen/generated/android/c++/JHybridOmniPlayerFactorySpec.cpp index 914bcdf..709ec72 100644 --- a/nitrogen/generated/android/c++/JHybridOmniPlayerFactorySpec.cpp +++ b/nitrogen/generated/android/c++/JHybridOmniPlayerFactorySpec.cpp @@ -9,16 +9,6 @@ // Forward declaration of `HybridOmniPlayerSpec` to properly resolve imports. namespace margelo::nitro::omni { class HybridOmniPlayerSpec; } -// Forward declaration of `Source` to properly resolve imports. -namespace margelo::nitro::omni { struct Source; } -// Forward declaration of `VideoSrc` to properly resolve imports. -namespace margelo::nitro::omni { struct VideoSrc; } -// Forward declaration of `Subtitle` to properly resolve imports. -namespace margelo::nitro::omni { struct Subtitle; } -// Forward declaration of `Metadata` to properly resolve imports. -namespace margelo::nitro::omni { struct Metadata; } -// Forward declaration of `MixAudioMode` to properly resolve imports. -namespace margelo::nitro::omni { enum class MixAudioMode; } // Forward declaration of `PlayerBackend` to properly resolve imports. namespace margelo::nitro::omni { struct PlayerBackend; } // Forward declaration of `AndroidBackend` to properly resolve imports. @@ -29,26 +19,14 @@ namespace margelo::nitro::omni { struct CastOptions; } #include #include "HybridOmniPlayerSpec.hpp" #include "JHybridOmniPlayerSpec.hpp" -#include "Source.hpp" -#include -#include "JSource.hpp" -#include "VideoSrc.hpp" -#include "JVideoSrc.hpp" -#include -#include -#include "Subtitle.hpp" -#include -#include "JSubtitle.hpp" -#include "Metadata.hpp" -#include "JMetadata.hpp" -#include "MixAudioMode.hpp" -#include "JMixAudioMode.hpp" #include "PlayerBackend.hpp" +#include #include "JPlayerBackend.hpp" #include "AndroidBackend.hpp" #include "JAndroidBackend.hpp" #include "CastOptions.hpp" #include "JCastOptions.hpp" +#include namespace margelo::nitro::omni { @@ -83,9 +61,9 @@ namespace margelo::nitro::omni { // Methods - std::shared_ptr JHybridOmniPlayerFactorySpec::createPlayer(const std::optional& props, const std::optional& backend, const std::optional& cast) { - static const auto method = _javaPart->javaClassStatic()->getMethod(jni::alias_ref /* props */, jni::alias_ref /* backend */, jni::alias_ref /* cast */)>("createPlayer"); - auto __result = method(_javaPart, props.has_value() ? JSource::fromCpp(props.value()) : nullptr, backend.has_value() ? JPlayerBackend::fromCpp(backend.value()) : nullptr, cast.has_value() ? JCastOptions::fromCpp(cast.value()) : nullptr); + std::shared_ptr JHybridOmniPlayerFactorySpec::createPlayer(const std::optional& backend, const std::optional& cast) { + static const auto method = _javaPart->javaClassStatic()->getMethod(jni::alias_ref /* backend */, jni::alias_ref /* cast */)>("createPlayer"); + auto __result = method(_javaPart, backend.has_value() ? JPlayerBackend::fromCpp(backend.value()) : nullptr, cast.has_value() ? JCastOptions::fromCpp(cast.value()) : nullptr); return __result->getJHybridOmniPlayerSpec(); } diff --git a/nitrogen/generated/android/c++/JHybridOmniPlayerFactorySpec.hpp b/nitrogen/generated/android/c++/JHybridOmniPlayerFactorySpec.hpp index 151ef7a..ec6d2f4 100644 --- a/nitrogen/generated/android/c++/JHybridOmniPlayerFactorySpec.hpp +++ b/nitrogen/generated/android/c++/JHybridOmniPlayerFactorySpec.hpp @@ -54,7 +54,7 @@ namespace margelo::nitro::omni { public: // Methods - std::shared_ptr createPlayer(const std::optional& props, const std::optional& backend, const std::optional& cast) override; + std::shared_ptr createPlayer(const std::optional& backend, const std::optional& cast) override; private: jni::global_ref _javaPart; diff --git a/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.cpp b/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.cpp index c25b88f..94371f8 100644 --- a/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.cpp +++ b/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.cpp @@ -241,8 +241,8 @@ namespace margelo::nitro::omni { } // Methods - void JHybridOmniPlayerSpec::release() { - static const auto method = _javaPart->javaClassStatic()->getMethod("release"); + void JHybridOmniPlayerSpec::abandon() { + static const auto method = _javaPart->javaClassStatic()->getMethod("abandon"); method(_javaPart); } void JHybridOmniPlayerSpec::play() { diff --git a/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.hpp b/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.hpp index 1198e01..6e4d09e 100644 --- a/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.hpp +++ b/nitrogen/generated/android/c++/JHybridOmniPlayerSpec.hpp @@ -78,7 +78,7 @@ namespace margelo::nitro::omni { public: // Methods - void release() override; + void abandon() override; void play() override; void pause() override; void seekBy(double offset) override; diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniPlayerFactorySpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniPlayerFactorySpec.kt index 34c5206..4d80005 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniPlayerFactorySpec.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniPlayerFactorySpec.kt @@ -30,7 +30,7 @@ abstract class HybridOmniPlayerFactorySpec: HybridObject() { // Methods @DoNotStrip @Keep - abstract fun createPlayer(props: Source?, backend: PlayerBackend?, cast: CastOptions?): HybridOmniPlayerSpec + abstract fun createPlayer(backend: PlayerBackend?, cast: CastOptions?): HybridOmniPlayerSpec // Default implementation of `HybridObject.toString()` override fun toString(): String { diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniPlayerSpec.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniPlayerSpec.kt index 2896a89..d3289bd 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniPlayerSpec.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/omni/HybridOmniPlayerSpec.kt @@ -116,7 +116,7 @@ abstract class HybridOmniPlayerSpec: HybridObject() { // Methods @DoNotStrip @Keep - abstract fun release(): Unit + abstract fun abandon(): Unit @DoNotStrip @Keep diff --git a/nitrogen/generated/shared/c++/HybridOmniPlayerFactorySpec.hpp b/nitrogen/generated/shared/c++/HybridOmniPlayerFactorySpec.hpp index d192a0b..b787ac5 100644 --- a/nitrogen/generated/shared/c++/HybridOmniPlayerFactorySpec.hpp +++ b/nitrogen/generated/shared/c++/HybridOmniPlayerFactorySpec.hpp @@ -15,8 +15,6 @@ // Forward declaration of `HybridOmniPlayerSpec` to properly resolve imports. namespace margelo::nitro::omni { class HybridOmniPlayerSpec; } -// Forward declaration of `Source` to properly resolve imports. -namespace margelo::nitro::omni { struct Source; } // Forward declaration of `PlayerBackend` to properly resolve imports. namespace margelo::nitro::omni { struct PlayerBackend; } // Forward declaration of `CastOptions` to properly resolve imports. @@ -24,9 +22,8 @@ namespace margelo::nitro::omni { struct CastOptions; } #include #include "HybridOmniPlayerSpec.hpp" -#include "Source.hpp" -#include #include "PlayerBackend.hpp" +#include #include "CastOptions.hpp" namespace margelo::nitro::omni { @@ -60,7 +57,7 @@ namespace margelo::nitro::omni { public: // Methods - virtual std::shared_ptr createPlayer(const std::optional& props, const std::optional& backend, const std::optional& cast) = 0; + virtual std::shared_ptr createPlayer(const std::optional& backend, const std::optional& cast) = 0; protected: // Hybrid Setup diff --git a/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.cpp b/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.cpp index 40ff406..e2d4746 100644 --- a/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.cpp +++ b/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.cpp @@ -39,7 +39,7 @@ namespace margelo::nitro::omni { prototype.registerHybridGetter("audios", &HybridOmniPlayerSpec::getAudios); prototype.registerHybridGetter("subtitles", &HybridOmniPlayerSpec::getSubtitles); prototype.registerHybridGetter("renditions", &HybridOmniPlayerSpec::getRenditions); - prototype.registerHybridMethod("release", &HybridOmniPlayerSpec::release); + prototype.registerHybridMethod("abandon", &HybridOmniPlayerSpec::abandon); prototype.registerHybridMethod("play", &HybridOmniPlayerSpec::play); prototype.registerHybridMethod("pause", &HybridOmniPlayerSpec::pause); prototype.registerHybridMethod("seekBy", &HybridOmniPlayerSpec::seekBy); diff --git a/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.hpp b/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.hpp index 9b727e0..bfab3ec 100644 --- a/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.hpp +++ b/nitrogen/generated/shared/c++/HybridOmniPlayerSpec.hpp @@ -91,7 +91,7 @@ namespace margelo::nitro::omni { public: // Methods - virtual void release() = 0; + virtual void abandon() = 0; virtual void play() = 0; virtual void pause() = 0; virtual void seekBy(double offset) = 0; diff --git a/src/provider.tsx b/src/provider.tsx index d47bf74..80c16ab 100755 --- a/src/provider.tsx +++ b/src/provider.tsx @@ -35,7 +35,7 @@ export const OmniProvider = ({ const [player, setPlayer] = useState(null); const createPlayer = useEffectEvent((aBackend: AndroidBackend) => - ProviderFactory.createPlayer(source, { android: aBackend }, cast), + ProviderFactory.createPlayer({ android: aBackend }, cast), ); useEffect(() => { @@ -44,11 +44,14 @@ export const OmniProvider = ({ useEffect(() => { if (!player) return; - return () => player.release(); + return () => player.abandon(); }, [player]); useEffect(() => { - if (player) player.source = source; + if (!player) return; + // early return on init to keep running cast sessions active + if (source == null && player.source == null) return; + player.source = source; }, [player, source]); useEffect(() => { diff --git a/src/specs/omni-player.nitro.ts b/src/specs/omni-player.nitro.ts index 693ec9e..0ea4a60 100644 --- a/src/specs/omni-player.nitro.ts +++ b/src/specs/omni-player.nitro.ts @@ -81,13 +81,10 @@ export interface OmniPlayer OmniPlayerT { readonly eventMap: OmniEventMap; - release(): void; + // let go of the player. it stays alive as long as it holds a cast session (android). + abandon(): void; } export interface OmniPlayerFactory extends HybridObject<{ android: "kotlin" }> { - createPlayer( - props?: Source, - backend?: PlayerBackend, - cast?: CastOptions, - ): OmniPlayer; + createPlayer(backend?: PlayerBackend, cast?: CastOptions): OmniPlayer; }