diff --git a/android/src/main/java/dev/zoriya/omni/EventMap.kt b/android/src/main/java/dev/zoriya/omni/EventMap.kt
index daed677..b6921d4 100644
--- a/android/src/main/java/dev/zoriya/omni/EventMap.kt
+++ b/android/src/main/java/dev/zoriya/omni/EventMap.kt
@@ -72,7 +72,7 @@ class EventMap(private val player: Player) : HybridOmniEventMapSpec(), Player.Li
}
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
- onIsPlayingChanged(playWhenReady)
+ onIsPlayingChanged(player.isPlaying)
}
override fun onIsPlayingChanged(isPlaying: Boolean) {
diff --git a/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt b/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt
index c2f0c51..5392675 100644
--- a/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt
+++ b/android/src/main/java/dev/zoriya/omni/OmniPlayer.kt
@@ -1,10 +1,11 @@
package dev.zoriya.omni
+import android.R
import android.annotation.SuppressLint
+import android.app.PendingIntent
import android.content.Intent
import android.util.Log
import android.view.SurfaceHolder
-import androidx.core.content.ContextCompat
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
@@ -36,15 +37,16 @@ class OmniPlayer : HybridOmniPlayerSpec() {
override var showNotification: Boolean? = false
set(value) {
- Log.e("omni", "Toggle show notif, old: ${field}, new: ${value}")
+ Log.e("omni", "Toggle show notif, old: ${field}, new: $value")
if (value == true) {
if (notificationPlayer != null) {
throw Error("Two players can't display notifications at the same time.")
}
notificationPlayer = player
ctx.startForegroundService(Intent(ctx, OmniPlayerService::class.java))
- } else if (field == true) {
+ } else if (field == true && notificationPlayer == player) {
ctx.stopService(Intent(ctx, OmniPlayerService::class.java))
+ notificationPlayer = null
}
field = value
}
@@ -62,6 +64,7 @@ class OmniPlayer : HybridOmniPlayerSpec() {
get() = currentSource
?: throw IllegalStateException("source should be initialized before get")
set(value) {
+ Log.e("omni", "update source")
currentSource = value
val src = source.src.firstOrNull() ?: return player.setMediaItem(MediaItem.EMPTY)
// val headers = Bundle().apply {
@@ -106,7 +109,10 @@ class OmniPlayer : HybridOmniPlayerSpec() {
.build()
)
.build()
- runOnMainThreadSync { player.setMediaItem(item) }
+ runOnMainThreadSync {
+ player.setMediaItem(item)
+ player.prepare()
+ }
}
fun setSurface(holder: SurfaceHolder?) {
@@ -157,7 +163,7 @@ class OmniPlayer : HybridOmniPlayerSpec() {
)
override var volume by mainThreadProperty(
- get = {player.volume.toDouble()},
+ get = { player.volume.toDouble() },
set = { value -> player.volume = value.toFloat().coerceIn(0f, 1f) }
)
@@ -168,9 +174,6 @@ class OmniPlayer : HybridOmniPlayerSpec() {
override fun play() {
runOnMainThreadSync { player.play() }
- if (showNotification == true) {
- ContextCompat.startForegroundService(ctx, Intent(ctx, OmniPlayerService::class.java))
- }
}
override fun pause() {
@@ -262,8 +265,8 @@ class OmniPlayer : HybridOmniPlayerSpec() {
@SuppressLint("UnsafeOptInUsageError")
class OmniPlayerService : MediaSessionService() {
- private val player = OmniPlayer.notificationPlayer ?: throw Error("No player available")
- var mediaSession: MediaSession = MediaSession.Builder(this, player).build()
+ lateinit var player: Player
+ lateinit var mediaSession: MediaSession
init {
Log.e("omni", "service inited")
@@ -272,17 +275,36 @@ class OmniPlayerService : MediaSessionService() {
override fun onCreate() {
Log.e("omni", "service created")
super.onCreate()
- setMediaNotificationProvider(
- DefaultMediaNotificationProvider.Builder(this).build()
- )
+ player = OmniPlayer.notificationPlayer ?: throw Error("No 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,
+ )
+ }
+ mediaSession = MediaSession.Builder(this, player)
+ .apply {
+ sessionActivity?.let { setSessionActivity(it) }
+ }
+ .build()
+
+ setMediaNotificationProvider(DefaultMediaNotificationProvider.Builder(this).build().apply {
+ setSmallIcon(applicationInfo.icon.takeIf { it != 0 } ?: R.drawable.ic_media_play)
+ })
+ addSession(mediaSession)
+ setShowNotificationForIdlePlayer(SHOW_NOTIFICATION_FOR_IDLE_PLAYER_ALWAYS)
+ triggerNotificationUpdate()
}
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo) = mediaSession
override fun onTaskRemoved(rootIntent: Intent?) {
- if (!isPlaybackOngoing) {
- pauseAllPlayersAndStopSelf()
- }
+ pauseAllPlayersAndStopSelf()
}
override fun onDestroy() {
diff --git a/android/src/main/java/dev/zoriya/omni/OmniView.kt b/android/src/main/java/dev/zoriya/omni/OmniView.kt
index ca49b8d..533c38d 100755
--- a/android/src/main/java/dev/zoriya/omni/OmniView.kt
+++ b/android/src/main/java/dev/zoriya/omni/OmniView.kt
@@ -29,24 +29,15 @@ class OmniView(val context: ThemedReactContext) : HybridOmniViewSpec(), SurfaceH
override fun afterUpdate() {
Log.e("omniView", "After update called")
if (!::player.isInitialized) {
- Log.w("omniView", "Skipping update because player is not set yet")
return
}
- val omniPlayer = player as? OmniPlayer
- ?: run {
- Log.w(
- "omniView",
- "Skipping update because player has unexpected type: ${player::class.java.name}"
- )
- return
- }
+ val omniPlayer = player as? OmniPlayer ?: return
if (boundPlayer === omniPlayer) {
- return;
+ return
}
- Log.e("omniView", "Resetting old player")
boundPlayer?.setSurface(null)
boundPlayer = omniPlayer
diff --git a/example/App.tsx b/example/App.tsx
index 1b1cdec..908843f 100644
--- a/example/App.tsx
+++ b/example/App.tsx
@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useMemo, useState } from "react";
+import { memo, useCallback, useEffect, useMemo, useState } from "react";
import type React from "react";
import { Pressable, ScrollView, StyleSheet, Text, View } from "react-native";
import {
@@ -12,14 +12,23 @@ import {
const PLAYLIST = [
{
title: "Big Buck Bunny (HLS)",
+ artist: "Blender Foundation",
+ album: "Open Movie Project",
+ artwork:
+ "https://peach.blender.org/wp-content/uploads/title_anouncement.jpg",
uri: "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8",
},
{
title: "Sintel Trailer (MP4)",
+ artist: "Blender Foundation",
+ album: "Sintel",
+ artwork:
+ "https://download.blender.org/durian/trailer/sintel_trailer-480p.jpg",
uri: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4",
},
] as const;
+
function formatTime(seconds: number): string {
if (!Number.isFinite(seconds) || seconds < 0) {
return "00:00";
@@ -261,9 +270,9 @@ function PlayerExample({
{tracks.videos.length === 0 ? (
No video tracks
) : (
- tracks.videos.map((video) => (
+ tracks.videos.map((video, index) => (
No audio tracks
) : (
- tracks.audios.map((audio) => (
+ tracks.audios.map((audio, index) => (
No subtitles
) : (
- tracks.subtitles.map((subtitle) => (
+ tracks.subtitles.map((subtitle, index) => (
No renditions
) : (
- tracks.renditions.map((rendition) => (
+ tracks.renditions.map((rendition, index) => (
(
useEffect(() => {
if (!refresh || refresh <= 0) return;
const int = setInterval(() => {
- setState(player[key])
- }, refresh);
+ setState(player[key]);
+ }, refresh * 1000);
return () => clearInterval(int);
}, [refresh, key, player]);