Compare commits

..
1 Commits
Author SHA1 Message Date
zoriya e5615cbc0c fix(source): allow no source on init 2026-07-29 23:44:56 +02:00
3 changed files with 56 additions and 12 deletions
@@ -106,21 +106,34 @@ class OmniPlayer(
override var showNotification: Boolean? = false override var showNotification: Boolean? = false
set(value) { set(value) {
if (value == true) { field = value
syncNotificationService()
}
private var serviceRunning = false
private fun syncNotificationService() {
val shouldShow = showNotification == true && source != null
when {
shouldShow && !serviceRunning -> {
val otherIsPlaying = notificationPlayer?.let { other -> val otherIsPlaying = notificationPlayer?.let { other ->
runOnMainThreadSync { other.isPlaying } other !== localPlayer && runOnMainThreadSync { other.isPlaying }
} == true } == true
if (otherIsPlaying) { if (otherIsPlaying) {
throw Error("Two players can't display notifications at the same time.") throw Error("Two players can't display notifications at the same time.")
} }
notificationPlayer = localPlayer notificationPlayer = localPlayer
ctx.startForegroundService(Intent(ctx, OmniPlayerService::class.java)) ctx.startForegroundService(Intent(ctx, OmniPlayerService::class.java))
} else if (field == true && notificationPlayer == localPlayer) { serviceRunning = true
}
!shouldShow && serviceRunning -> {
ctx.stopService(Intent(ctx, OmniPlayerService::class.java)) ctx.stopService(Intent(ctx, OmniPlayerService::class.java))
notificationPlayer = null if (notificationPlayer == localPlayer) notificationPlayer = null
serviceRunning = false
} }
field = value
} }
}
override val castStatus: CastStatus override val castStatus: CastStatus
get() = runOnMainThreadSync { computeCastStatus() } get() = runOnMainThreadSync { computeCastStatus() }
@@ -297,6 +310,7 @@ class OmniPlayer(
runOnMainThreadSync { runOnMainThreadSync {
player.clearMediaItems() player.clearMediaItems()
} }
syncNotificationService()
return return
} }
val handleAudioFocus = val handleAudioFocus =
@@ -329,6 +343,7 @@ class OmniPlayer(
player.setMediaItems(mediaItems, startIndex, startPositionMs.toLong()) player.setMediaItems(mediaItems, startIndex, startPositionMs.toLong())
player.prepare() player.prepare()
} }
syncNotificationService()
} }
override fun play() { override fun play() {
@@ -504,7 +519,13 @@ class OmniPlayerService : MediaSessionService() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
player = OmniPlayer.notificationPlayer ?: throw Error("No player available") val available = OmniPlayer.notificationPlayer
if (available == null) {
startForeground(1, createImmediateNotification())
stopSelf()
return
}
player = available
val launchIntent = packageManager.getLaunchIntentForPackage(packageName)?.apply { val launchIntent = packageManager.getLaunchIntentForPackage(packageName)?.apply {
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
} }
@@ -540,7 +561,8 @@ class OmniPlayerService : MediaSessionService() {
player = current player = current
mediaSession.player = current mediaSession.player = current
} }
return super.onStartCommand(intent, flags, startId) super.onStartCommand(intent, flags, startId)
return START_NOT_STICKY
} }
private fun createImmediateNotification(): Notification { private fun createImmediateNotification(): Notification {
@@ -581,7 +603,7 @@ class OmniPlayerService : MediaSessionService() {
} }
override fun onDestroy() { override fun onDestroy() {
mediaSession.release() if (::mediaSession.isInitialized) mediaSession.release()
super.onDestroy() super.onDestroy()
} }
} }
@@ -742,9 +742,9 @@ class VlcPlayer(ctx: Context) :
} }
} }
override fun getCurrentPeriodIndex() = currentMediaItemIndex override fun getCurrentPeriodIndex() = currentMediaItemIndex.coerceAtLeast(0)
override fun getCurrentMediaItemIndex() = currentMediaItemIndex override fun getCurrentMediaItemIndex() = currentMediaItemIndex.coerceAtLeast(0)
override fun getDuration(): Long = player.length.takeIf { it > 0 } ?: TIME_UNSET override fun getDuration(): Long = player.length.takeIf { it > 0 } ?: TIME_UNSET
+24 -2
View File
@@ -57,12 +57,16 @@ function PlayerExample({
trackLabel, trackLabel,
backend, backend,
onSwitchBackend, onSwitchBackend,
hasSource,
onLoad,
}: { }: {
onPrev: () => void; onPrev: () => void;
onNext: () => void; onNext: () => void;
trackLabel: string; trackLabel: string;
backend: AndroidBackend; backend: AndroidBackend;
onSwitchBackend: (backend: AndroidBackend) => void; onSwitchBackend: (backend: AndroidBackend) => void;
hasSource: boolean;
onLoad: () => void;
}): React.JSX.Element { }): React.JSX.Element {
const player = usePlayer(); const player = usePlayer();
const status = usePlayerState("status"); const status = usePlayerState("status");
@@ -233,7 +237,20 @@ function PlayerExample({
return ( return (
<ScrollView style={styles.container}> <ScrollView style={styles.container}>
<Text style={styles.heading}>react-native-omni</Text> <Text style={styles.heading}>react-native-omni</Text>
<Text style={styles.subheading}>{trackLabel}</Text> <Text style={styles.subheading}>
{hasSource ? trackLabel : "No media loaded"}
</Text>
<View style={styles.row}>
<Pressable
style={[styles.button, hasSource && styles.selectedTrackButton]}
onPress={onLoad}
>
<Text style={styles.buttonText}>
{hasSource ? "Unload media" : "Load media"}
</Text>
</Pressable>
</View>
<View style={styles.row}> <View style={styles.row}>
<Pressable <Pressable
@@ -497,6 +514,9 @@ function PlayerExample({
function App(): React.JSX.Element { function App(): React.JSX.Element {
const [currentIndex, setCurrentIndex] = useState(0); const [currentIndex, setCurrentIndex] = useState(0);
// Start with no media loaded so we can verify the app boots (and the media
// notification stays hidden) before any source is set.
const [hasSource, setHasSource] = useState(false);
const [backend, setBackend] = useState<AndroidBackend>("vlc"); const [backend, setBackend] = useState<AndroidBackend>("vlc");
const [pendingBackend, setPendingBackend] = useState<AndroidBackend | null>( const [pendingBackend, setPendingBackend] = useState<AndroidBackend | null>(
null, null,
@@ -574,7 +594,7 @@ function App(): React.JSX.Element {
return ( return (
<OmniProvider <OmniProvider
key={backend} key={backend}
source={source} source={hasSource ? source : undefined}
backend={{ android: backend }} backend={{ android: backend }}
cast={{receiverApplicationId: "D8FB0FC1"}} cast={{receiverApplicationId: "D8FB0FC1"}}
showNotification showNotification
@@ -585,6 +605,8 @@ function App(): React.JSX.Element {
trackLabel={PLAYLIST[currentIndex].title} trackLabel={PLAYLIST[currentIndex].title}
backend={backend} backend={backend}
onSwitchBackend={handleSwitchBackend} onSwitchBackend={handleSwitchBackend}
hasSource={hasSource}
onLoad={() => setHasSource((v) => !v)}
/> />
</OmniProvider> </OmniProvider>
); );