mirror of
https://github.com/zoriya/react-native-omni.git
synced 2026-08-15 18:33:50 +00:00
fix(cast): use custom notif instead of builtin one
This commit is contained in:
@@ -16,12 +16,5 @@
|
||||
<action android:name="androidx.media3.session.MediaSessionService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<activity
|
||||
android:name="dev.zoriya.omni.OmniCastTapActivity"
|
||||
android:exported="false"
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
android:taskAffinity=""
|
||||
android:theme="@android:style/Theme.NoDisplay" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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?) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-27
@@ -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 <memory>
|
||||
#include "HybridOmniPlayerSpec.hpp"
|
||||
#include "JHybridOmniPlayerSpec.hpp"
|
||||
#include "Source.hpp"
|
||||
#include <optional>
|
||||
#include "JSource.hpp"
|
||||
#include "VideoSrc.hpp"
|
||||
#include "JVideoSrc.hpp"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "Subtitle.hpp"
|
||||
#include <vector>
|
||||
#include "JSubtitle.hpp"
|
||||
#include "Metadata.hpp"
|
||||
#include "JMetadata.hpp"
|
||||
#include "MixAudioMode.hpp"
|
||||
#include "JMixAudioMode.hpp"
|
||||
#include "PlayerBackend.hpp"
|
||||
#include <optional>
|
||||
#include "JPlayerBackend.hpp"
|
||||
#include "AndroidBackend.hpp"
|
||||
#include "JAndroidBackend.hpp"
|
||||
#include "CastOptions.hpp"
|
||||
#include "JCastOptions.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace margelo::nitro::omni {
|
||||
|
||||
@@ -83,9 +61,9 @@ namespace margelo::nitro::omni {
|
||||
|
||||
|
||||
// Methods
|
||||
std::shared_ptr<HybridOmniPlayerSpec> JHybridOmniPlayerFactorySpec::createPlayer(const std::optional<Source>& props, const std::optional<PlayerBackend>& backend, const std::optional<CastOptions>& cast) {
|
||||
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JHybridOmniPlayerSpec::JavaPart>(jni::alias_ref<JSource> /* props */, jni::alias_ref<JPlayerBackend> /* backend */, jni::alias_ref<JCastOptions> /* 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<HybridOmniPlayerSpec> JHybridOmniPlayerFactorySpec::createPlayer(const std::optional<PlayerBackend>& backend, const std::optional<CastOptions>& cast) {
|
||||
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JHybridOmniPlayerSpec::JavaPart>(jni::alias_ref<JPlayerBackend> /* backend */, jni::alias_ref<JCastOptions> /* 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace margelo::nitro::omni {
|
||||
|
||||
public:
|
||||
// Methods
|
||||
std::shared_ptr<HybridOmniPlayerSpec> createPlayer(const std::optional<Source>& props, const std::optional<PlayerBackend>& backend, const std::optional<CastOptions>& cast) override;
|
||||
std::shared_ptr<HybridOmniPlayerSpec> createPlayer(const std::optional<PlayerBackend>& backend, const std::optional<CastOptions>& cast) override;
|
||||
|
||||
private:
|
||||
jni::global_ref<JHybridOmniPlayerFactorySpec::JavaPart> _javaPart;
|
||||
|
||||
+2
-2
@@ -241,8 +241,8 @@ namespace margelo::nitro::omni {
|
||||
}
|
||||
|
||||
// Methods
|
||||
void JHybridOmniPlayerSpec::release() {
|
||||
static const auto method = _javaPart->javaClassStatic()->getMethod<void()>("release");
|
||||
void JHybridOmniPlayerSpec::abandon() {
|
||||
static const auto method = _javaPart->javaClassStatic()->getMethod<void()>("abandon");
|
||||
method(_javaPart);
|
||||
}
|
||||
void JHybridOmniPlayerSpec::play() {
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
+1
-1
@@ -116,7 +116,7 @@ abstract class HybridOmniPlayerSpec: HybridObject() {
|
||||
// Methods
|
||||
@DoNotStrip
|
||||
@Keep
|
||||
abstract fun release(): Unit
|
||||
abstract fun abandon(): Unit
|
||||
|
||||
@DoNotStrip
|
||||
@Keep
|
||||
|
||||
@@ -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 <memory>
|
||||
#include "HybridOmniPlayerSpec.hpp"
|
||||
#include "Source.hpp"
|
||||
#include <optional>
|
||||
#include "PlayerBackend.hpp"
|
||||
#include <optional>
|
||||
#include "CastOptions.hpp"
|
||||
|
||||
namespace margelo::nitro::omni {
|
||||
@@ -60,7 +57,7 @@ namespace margelo::nitro::omni {
|
||||
|
||||
public:
|
||||
// Methods
|
||||
virtual std::shared_ptr<HybridOmniPlayerSpec> createPlayer(const std::optional<Source>& props, const std::optional<PlayerBackend>& backend, const std::optional<CastOptions>& cast) = 0;
|
||||
virtual std::shared_ptr<HybridOmniPlayerSpec> createPlayer(const std::optional<PlayerBackend>& backend, const std::optional<CastOptions>& cast) = 0;
|
||||
|
||||
protected:
|
||||
// Hybrid Setup
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+6
-3
@@ -35,7 +35,7 @@ export const OmniProvider = ({
|
||||
const [player, setPlayer] = useState<NativeOmniPlayer | null>(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(() => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user