expo-video 3.0.8 → 3.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/android/build.gradle +2 -2
- package/android/src/main/AndroidManifest.xml +0 -8
- package/android/src/main/java/expo/modules/video/VideoExceptions.kt +8 -0
- package/android/src/main/java/expo/modules/video/playbackService/ExpoVideoPlaybackService.kt +125 -46
- package/android/src/main/java/expo/modules/video/playbackService/PlaybackServiceConnection.kt +35 -12
- package/android/src/main/java/expo/modules/video/player/VideoPlayer.kt +36 -4
- package/build/VideoPlayer.types.d.ts +5 -0
- package/build/VideoPlayer.types.d.ts.map +1 -1
- package/build/VideoPlayer.types.js.map +1 -1
- package/build/VideoView.web.d.ts.map +1 -1
- package/build/VideoView.web.js +43 -9
- package/build/VideoView.web.js.map +1 -1
- package/expo-module.config.json +1 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/{3.0.8/expo.modules.video-3.0.8-sources.jar → 3.0.10/expo.modules.video-3.0.10-sources.jar} +0 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10-sources.jar.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10-sources.jar.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10-sources.jar.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10-sources.jar.sha512 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10.aar +0 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10.aar.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10.aar.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10.aar.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10.aar.sha512 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/{3.0.8/expo.modules.video-3.0.8.module → 3.0.10/expo.modules.video-3.0.10.module} +22 -22
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10.module.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10.module.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10.module.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10.module.sha512 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/{3.0.8/expo.modules.video-3.0.8.pom → 3.0.10/expo.modules.video-3.0.10.pom} +1 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10.pom.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10.pom.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10.pom.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.10/expo.modules.video-3.0.10.pom.sha512 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml +4 -4
- package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.md5 +1 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha1 +1 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha256 +1 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/maven-metadata.xml.sha512 +1 -1
- package/package.json +2 -2
- package/plugin/build/withExpoVideo.js +44 -8
- package/plugin/src/withExpoVideo.ts +51 -7
- package/src/VideoPlayer.types.ts +5 -0
- package/src/VideoView.web.tsx +57 -9
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8-sources.jar.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8-sources.jar.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8-sources.jar.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8-sources.jar.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.aar +0 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.aar.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.aar.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.aar.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.aar.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.module.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.module.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.module.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.module.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.pom.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.pom.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.pom.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.video/3.0.8/expo.modules.video-3.0.8.pom.sha512 +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,22 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 3.0.10 — 2025-09-04
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- [Web] Fix fullscreen enter/exit methods and listeners not working in Safari on iOS. ([#39320](https://github.com/expo/expo/pull/39320) by [@behenate](https://github.com/behenate))
|
|
18
|
+
|
|
19
|
+
## 3.0.9 — 2025-09-03
|
|
20
|
+
|
|
21
|
+
### 🛠 Breaking changes
|
|
22
|
+
|
|
23
|
+
- [Android] In order to show the now playing notification, the `supportsBackgroundPlayground` property of the config plugin has to be `true`. ([#38980](https://github.com/expo/expo/pull/38980) by [@kerwanp](https://github.com/kerwanp))
|
|
24
|
+
|
|
25
|
+
### 🐛 Bug fixes
|
|
26
|
+
|
|
27
|
+
- [Android] Fix video player stopping playback a few minutes after locking the device. ([#38980](https://github.com/expo/expo/pull/38980) by [@kerwanp](https://github.com/kerwanp))
|
|
28
|
+
|
|
13
29
|
## 3.0.8 — 2025-09-02
|
|
14
30
|
|
|
15
31
|
_This version does not introduce any user-facing changes._
|
package/android/build.gradle
CHANGED
|
@@ -8,13 +8,5 @@
|
|
|
8
8
|
android:supportsPictureInPicture="true"
|
|
9
9
|
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|navigation"
|
|
10
10
|
android:theme="@style/Fullscreen" />
|
|
11
|
-
<service
|
|
12
|
-
android:name=".playbackService.ExpoVideoPlaybackService"
|
|
13
|
-
android:exported="false"
|
|
14
|
-
android:foregroundServiceType="mediaPlayback">
|
|
15
|
-
<intent-filter>
|
|
16
|
-
<action android:name="androidx.media3.session.MediaSessionService" />
|
|
17
|
-
</intent-filter>
|
|
18
|
-
</service>
|
|
19
11
|
</application>
|
|
20
12
|
</manifest>
|
|
@@ -3,6 +3,8 @@ package expo.modules.video
|
|
|
3
3
|
import expo.modules.kotlin.exception.CodedException
|
|
4
4
|
import expo.modules.video.enums.DRMType
|
|
5
5
|
|
|
6
|
+
private const val defaultServiceBindingTip = "Make sure that the expo-video config plugin is properly configured to avoid issues with displaying the now playing notification and sustaining background playback."
|
|
7
|
+
|
|
6
8
|
internal class FullScreenVideoViewNotFoundException :
|
|
7
9
|
CodedException("VideoView id wasn't passed to the activity")
|
|
8
10
|
|
|
@@ -35,3 +37,9 @@ internal class FailedToGetAudioFocusManagerException :
|
|
|
35
37
|
|
|
36
38
|
internal class VideoCacheException(message: String?, cause: Throwable? = null) :
|
|
37
39
|
CodedException(message ?: "Unexpected expo-video cache error", cause)
|
|
40
|
+
|
|
41
|
+
internal class NowPlayingException(message: String?, cause: Throwable? = null) :
|
|
42
|
+
CodedException(message ?: "Unexpected expo-video now playing exception", cause)
|
|
43
|
+
|
|
44
|
+
internal fun getPlaybackServiceErrorMessage(message: String?, tip: String = defaultServiceBindingTip) =
|
|
45
|
+
(message ?: "Expo-video playback service binder error") + ". $tip"
|
package/android/src/main/java/expo/modules/video/playbackService/ExpoVideoPlaybackService.kt
CHANGED
|
@@ -2,12 +2,14 @@ package expo.modules.video.playbackService
|
|
|
2
2
|
|
|
3
3
|
import android.app.NotificationChannel
|
|
4
4
|
import android.app.NotificationManager
|
|
5
|
+
import android.app.Service
|
|
5
6
|
import android.content.Context
|
|
6
7
|
import android.content.Intent
|
|
7
8
|
import android.os.Binder
|
|
8
9
|
import android.os.Build
|
|
9
10
|
import android.os.Bundle
|
|
10
11
|
import android.os.IBinder
|
|
12
|
+
import androidx.annotation.MainThread
|
|
11
13
|
import androidx.annotation.OptIn
|
|
12
14
|
import androidx.core.app.NotificationCompat
|
|
13
15
|
import androidx.media3.common.util.UnstableApi
|
|
@@ -19,15 +21,27 @@ import androidx.media3.session.MediaStyleNotificationHelper
|
|
|
19
21
|
import androidx.media3.session.SessionCommand
|
|
20
22
|
import com.google.common.collect.ImmutableList
|
|
21
23
|
import expo.modules.kotlin.AppContext
|
|
24
|
+
import expo.modules.kotlin.exception.Exceptions
|
|
22
25
|
import expo.modules.video.R
|
|
26
|
+
import expo.modules.video.getPlaybackServiceErrorMessage
|
|
23
27
|
import expo.modules.video.player.VideoPlayer
|
|
28
|
+
import kotlinx.coroutines.launch
|
|
29
|
+
import java.lang.ref.WeakReference
|
|
24
30
|
|
|
25
31
|
class PlaybackServiceBinder(val service: ExpoVideoPlaybackService) : Binder()
|
|
26
32
|
|
|
27
33
|
@OptIn(UnstableApi::class)
|
|
28
34
|
class ExpoVideoPlaybackService : MediaSessionService() {
|
|
35
|
+
private lateinit var weakContext: WeakReference<AppContext>
|
|
36
|
+
var appContext: AppContext
|
|
37
|
+
get() = weakContext.get() ?: throw Exceptions.AppContextLost()
|
|
38
|
+
set(value) {
|
|
39
|
+
weakContext = WeakReference(value)
|
|
40
|
+
}
|
|
29
41
|
private val mediaSessions = mutableMapOf<ExoPlayer, MediaSession>()
|
|
30
42
|
private val binder = PlaybackServiceBinder(this)
|
|
43
|
+
private var mostRecentInteractionSession: MediaSession? = null
|
|
44
|
+
private var isForeground: Boolean = false
|
|
31
45
|
|
|
32
46
|
private val commandSeekForward = SessionCommand(SEEK_FORWARD_COMMAND, Bundle.EMPTY)
|
|
33
47
|
private val commandSeekBackward = SessionCommand(SEEK_BACKWARD_COMMAND, Bundle.EMPTY)
|
|
@@ -44,42 +58,50 @@ class ExpoVideoPlaybackService : MediaSessionService() {
|
|
|
44
58
|
.build()
|
|
45
59
|
|
|
46
60
|
fun setShowNotification(showNotification: Boolean, player: ExoPlayer) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
appContext.mainQueue.launch {
|
|
62
|
+
val sessionExtras = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
63
|
+
mediaSessions[player]?.sessionExtras?.deepCopy() ?: Bundle()
|
|
64
|
+
} else {
|
|
65
|
+
Bundle()
|
|
66
|
+
}
|
|
67
|
+
sessionExtras.putBoolean(SESSION_SHOW_NOTIFICATION, showNotification)
|
|
68
|
+
mediaSessions[player]?.let {
|
|
69
|
+
it.sessionExtras = sessionExtras
|
|
70
|
+
onUpdateNotification(it, showNotification && player.playWhenReady)
|
|
71
|
+
}
|
|
56
72
|
}
|
|
57
73
|
}
|
|
58
74
|
|
|
59
75
|
fun registerPlayer(videoPlayer: VideoPlayer) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
76
|
+
appContext.mainQueue.launch {
|
|
77
|
+
val player = videoPlayer.player
|
|
78
|
+
if (mediaSessions[player] != null) {
|
|
79
|
+
return@launch
|
|
80
|
+
}
|
|
64
81
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
val mediaSession = MediaSession.Builder(this@ExpoVideoPlaybackService, player)
|
|
83
|
+
.setId("ExpoVideoPlaybackService_${player.hashCode()}")
|
|
84
|
+
.setCallback(VideoMediaSessionCallback())
|
|
85
|
+
.setCustomLayout(ImmutableList.of(seekBackwardButton, seekForwardButton))
|
|
86
|
+
.build()
|
|
70
87
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
88
|
+
mediaSessions[player] = mediaSession
|
|
89
|
+
addSession(mediaSession)
|
|
90
|
+
setShowNotification(videoPlayer.showNowPlayingNotification, player)
|
|
91
|
+
}
|
|
74
92
|
}
|
|
75
93
|
|
|
76
94
|
fun unregisterPlayer(player: ExoPlayer) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
95
|
+
appContext.mainQueue.launch {
|
|
96
|
+
hidePlayerNotification(player)
|
|
97
|
+
val session = mediaSessions.remove(player)
|
|
98
|
+
session?.release()
|
|
99
|
+
if (mediaSessions.isEmpty()) {
|
|
100
|
+
cleanup()
|
|
101
|
+
stopSelf()
|
|
102
|
+
} else {
|
|
103
|
+
setMostRecentInteractionSession(findMostRecentInteractionSession())
|
|
104
|
+
}
|
|
83
105
|
}
|
|
84
106
|
}
|
|
85
107
|
|
|
@@ -89,15 +111,48 @@ class ExpoVideoPlaybackService : MediaSessionService() {
|
|
|
89
111
|
}
|
|
90
112
|
|
|
91
113
|
override fun onUpdateNotification(session: MediaSession, startInForegroundRequired: Boolean) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
114
|
+
appContext.mainQueue.launch {
|
|
115
|
+
if (startInForegroundRequired && session.wantsToShowNotification()) {
|
|
116
|
+
setMostRecentInteractionSession(session)
|
|
117
|
+
} else {
|
|
118
|
+
setMostRecentInteractionSession(findMostRecentInteractionSession())
|
|
97
119
|
}
|
|
98
120
|
}
|
|
99
121
|
}
|
|
100
122
|
|
|
123
|
+
@MainThread
|
|
124
|
+
private fun setMostRecentInteractionSession(session: MediaSession?) {
|
|
125
|
+
if (session?.player?.playWhenReady == false) {
|
|
126
|
+
stopForeground(STOP_FOREGROUND_DETACH)
|
|
127
|
+
isForeground = false
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (mostRecentInteractionSession != session) {
|
|
131
|
+
hideAllNotifications()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
mostRecentInteractionSession = session
|
|
135
|
+
session?.let {
|
|
136
|
+
createNotification(it, it.player.playWhenReady)
|
|
137
|
+
} ?: run {
|
|
138
|
+
stopForeground(STOP_FOREGROUND_REMOVE)
|
|
139
|
+
isForeground = false
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Finds a session that is playing media. And wants to show a notification
|
|
145
|
+
* If none exists, returns a session that wants to show a notification.
|
|
146
|
+
* The current mostRecentInteractionSession always has priority to reduce the number of unnecessary notification changes.
|
|
147
|
+
*/
|
|
148
|
+
@MainThread
|
|
149
|
+
private fun findMostRecentInteractionSession(): MediaSession? {
|
|
150
|
+
val prioritizedSessions = (listOfNotNull(mostRecentInteractionSession) + mediaSessions.values.toList()).distinct()
|
|
151
|
+
|
|
152
|
+
return prioritizedSessions.firstOrNull { it.wantsToShowNotification() && it.player.playWhenReady }
|
|
153
|
+
?: prioritizedSessions.firstOrNull { it.wantsToShowNotification() }
|
|
154
|
+
}
|
|
155
|
+
|
|
101
156
|
override fun onTaskRemoved(rootIntent: Intent?) {
|
|
102
157
|
cleanup()
|
|
103
158
|
stopSelf()
|
|
@@ -112,7 +167,8 @@ class ExpoVideoPlaybackService : MediaSessionService() {
|
|
|
112
167
|
super.onDestroy()
|
|
113
168
|
}
|
|
114
169
|
|
|
115
|
-
|
|
170
|
+
@MainThread
|
|
171
|
+
private fun createNotification(session: MediaSession, startInForegroundRequired: Boolean = false) {
|
|
116
172
|
if (session.player.currentMediaItem == null) {
|
|
117
173
|
return
|
|
118
174
|
}
|
|
@@ -130,33 +186,55 @@ class ExpoVideoPlaybackService : MediaSessionService() {
|
|
|
130
186
|
.setStyle(MediaStyleNotificationHelper.MediaStyle(session))
|
|
131
187
|
.build()
|
|
132
188
|
|
|
133
|
-
|
|
134
|
-
|
|
189
|
+
val notificationId = session.player.hashCode()
|
|
190
|
+
|
|
191
|
+
if (startInForegroundRequired) {
|
|
192
|
+
try {
|
|
193
|
+
startForeground(notificationId, notificationCompat)
|
|
194
|
+
isForeground = true
|
|
195
|
+
} catch (e: Exception) {
|
|
196
|
+
appContext.jsLogger?.error(getPlaybackServiceErrorMessage("Failed to start the expo-video foreground service"), e)
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
notificationManager.notify(notificationId, notificationCompat)
|
|
200
|
+
}
|
|
135
201
|
}
|
|
136
202
|
|
|
137
203
|
private fun cleanup() {
|
|
138
|
-
|
|
204
|
+
appContext.mainQueue.launch {
|
|
205
|
+
stopForeground(Service.STOP_FOREGROUND_REMOVE)
|
|
206
|
+
isForeground = false
|
|
207
|
+
|
|
208
|
+
hideAllNotifications()
|
|
209
|
+
|
|
210
|
+
val sessionsToRelease = mediaSessions.values.toList()
|
|
211
|
+
mediaSessions.clear()
|
|
212
|
+
for (session in sessionsToRelease) {
|
|
213
|
+
session.release()
|
|
214
|
+
}
|
|
139
215
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
216
|
+
val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
217
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
218
|
+
notificationManager.deleteNotificationChannel(CHANNEL_ID)
|
|
219
|
+
}
|
|
144
220
|
}
|
|
145
221
|
}
|
|
146
222
|
|
|
223
|
+
@MainThread
|
|
147
224
|
private fun hidePlayerNotification(player: ExoPlayer) {
|
|
148
225
|
val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
149
226
|
notificationManager.cancel(player.hashCode())
|
|
150
227
|
}
|
|
151
228
|
|
|
229
|
+
@MainThread
|
|
152
230
|
private fun hideAllNotifications() {
|
|
153
231
|
val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
154
232
|
notificationManager.cancelAll()
|
|
155
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
156
|
-
notificationManager.deleteNotificationChannel(CHANNEL_ID)
|
|
157
|
-
}
|
|
158
233
|
}
|
|
159
234
|
|
|
235
|
+
private fun MediaSession.wantsToShowNotification(): Boolean =
|
|
236
|
+
this.sessionExtras.getBoolean(SESSION_SHOW_NOTIFICATION, false)
|
|
237
|
+
|
|
160
238
|
companion object {
|
|
161
239
|
const val SEEK_FORWARD_COMMAND = "SEEK_FORWARD"
|
|
162
240
|
const val SEEK_BACKWARD_COMMAND = "SEEK_REWIND"
|
|
@@ -164,7 +242,7 @@ class ExpoVideoPlaybackService : MediaSessionService() {
|
|
|
164
242
|
const val SESSION_SHOW_NOTIFICATION = "showNotification"
|
|
165
243
|
const val SEEK_INTERVAL_MS = 10000L
|
|
166
244
|
|
|
167
|
-
fun startService(appContext: AppContext, context: Context, serviceConnection: PlaybackServiceConnection) {
|
|
245
|
+
fun startService(appContext: AppContext, context: Context, serviceConnection: PlaybackServiceConnection): Boolean {
|
|
168
246
|
appContext.reactContext?.apply {
|
|
169
247
|
val intent = Intent(context, ExpoVideoPlaybackService::class.java)
|
|
170
248
|
intent.action = SERVICE_INTERFACE
|
|
@@ -172,13 +250,14 @@ class ExpoVideoPlaybackService : MediaSessionService() {
|
|
|
172
250
|
startService(intent)
|
|
173
251
|
|
|
174
252
|
val flags = if (Build.VERSION.SDK_INT >= 29) {
|
|
175
|
-
BIND_AUTO_CREATE or
|
|
253
|
+
BIND_AUTO_CREATE or BIND_INCLUDE_CAPABILITIES
|
|
176
254
|
} else {
|
|
177
255
|
BIND_AUTO_CREATE
|
|
178
256
|
}
|
|
179
257
|
|
|
180
|
-
bindService(intent, serviceConnection, flags)
|
|
258
|
+
return bindService(intent, serviceConnection, flags)
|
|
181
259
|
}
|
|
260
|
+
return false
|
|
182
261
|
}
|
|
183
262
|
}
|
|
184
263
|
}
|
package/android/src/main/java/expo/modules/video/playbackService/PlaybackServiceConnection.kt
CHANGED
|
@@ -3,37 +3,60 @@ package expo.modules.video.playbackService
|
|
|
3
3
|
import android.content.ComponentName
|
|
4
4
|
import android.content.ServiceConnection
|
|
5
5
|
import android.os.IBinder
|
|
6
|
-
import android.util.Log
|
|
7
6
|
import androidx.annotation.OptIn
|
|
8
7
|
import androidx.media3.common.util.UnstableApi
|
|
8
|
+
import expo.modules.kotlin.AppContext
|
|
9
|
+
import expo.modules.kotlin.exception.Exceptions
|
|
10
|
+
import expo.modules.video.getPlaybackServiceErrorMessage
|
|
9
11
|
import expo.modules.video.player.VideoPlayer
|
|
10
12
|
import java.lang.ref.WeakReference
|
|
11
13
|
|
|
12
14
|
@OptIn(UnstableApi::class)
|
|
13
|
-
class PlaybackServiceConnection(val player: WeakReference<VideoPlayer
|
|
15
|
+
class PlaybackServiceConnection(val player: WeakReference<VideoPlayer>, appContext: AppContext) : ServiceConnection {
|
|
14
16
|
var playbackServiceBinder: PlaybackServiceBinder? = null
|
|
17
|
+
private set
|
|
18
|
+
var isConnected = false
|
|
19
|
+
private set
|
|
20
|
+
private val _appContext = WeakReference(appContext)
|
|
21
|
+
private val appContext: AppContext
|
|
22
|
+
get() = _appContext.get() ?: throw Exceptions.AppContextLost()
|
|
15
23
|
|
|
16
24
|
override fun onServiceConnected(componentName: ComponentName, binder: IBinder) {
|
|
17
25
|
val player = player.get() ?: return
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"ExpoVideo",
|
|
22
|
-
"Expo Video could not bind to the playback service. " +
|
|
23
|
-
"This will cause issues with playback notifications and sustaining background playback."
|
|
26
|
+
val serviceBinder: PlaybackServiceBinder = binder as? PlaybackServiceBinder ?: run {
|
|
27
|
+
appContext.jsLogger?.error(
|
|
28
|
+
getPlaybackServiceErrorMessage("Expo-video could not bind to the playback service")
|
|
24
29
|
)
|
|
30
|
+
return
|
|
25
31
|
}
|
|
32
|
+
|
|
33
|
+
isConnected = true
|
|
34
|
+
playbackServiceBinder = serviceBinder
|
|
35
|
+
serviceBinder.service.appContext = appContext
|
|
36
|
+
serviceBinder.service.registerPlayer(player)
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
override fun onServiceDisconnected(componentName: ComponentName) {
|
|
29
40
|
playbackServiceBinder = null
|
|
41
|
+
isConnected = false
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override fun onBindingDied(name: ComponentName?) {
|
|
45
|
+
isConnected = false
|
|
46
|
+
appContext.jsLogger?.error(
|
|
47
|
+
getPlaybackServiceErrorMessage(
|
|
48
|
+
"Expo-video has lost connection to the playback service binder",
|
|
49
|
+
"This will cause issues with now playing notification and sustaining background playback."
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
super.onBindingDied(name)
|
|
30
53
|
}
|
|
31
54
|
|
|
32
55
|
override fun onNullBinding(componentName: ComponentName) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"Expo Video could not bind to the playback service
|
|
36
|
-
"This will cause issues with playback notifications and sustaining background playback."
|
|
56
|
+
isConnected = false
|
|
57
|
+
appContext.jsLogger?.error(
|
|
58
|
+
getPlaybackServiceErrorMessage("Expo Video could not bind to the playback service")
|
|
37
59
|
)
|
|
60
|
+
super.onNullBinding(componentName)
|
|
38
61
|
}
|
|
39
62
|
}
|
|
@@ -23,6 +23,7 @@ import androidx.media3.exoplayer.analytics.AnalyticsListener
|
|
|
23
23
|
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
|
|
24
24
|
import androidx.media3.ui.PlayerView
|
|
25
25
|
import expo.modules.kotlin.AppContext
|
|
26
|
+
import expo.modules.kotlin.exception.Exceptions
|
|
26
27
|
import expo.modules.kotlin.sharedobjects.SharedObject
|
|
27
28
|
import expo.modules.video.IntervalUpdateClock
|
|
28
29
|
import expo.modules.video.IntervalUpdateEmitter
|
|
@@ -31,6 +32,7 @@ import expo.modules.video.delegates.IgnoreSameSet
|
|
|
31
32
|
import expo.modules.video.enums.AudioMixingMode
|
|
32
33
|
import expo.modules.video.enums.PlayerStatus
|
|
33
34
|
import expo.modules.video.enums.PlayerStatus.*
|
|
35
|
+
import expo.modules.video.getPlaybackServiceErrorMessage
|
|
34
36
|
import expo.modules.video.playbackService.ExpoVideoPlaybackService
|
|
35
37
|
import expo.modules.video.playbackService.PlaybackServiceConnection
|
|
36
38
|
import expo.modules.video.records.BufferOptions
|
|
@@ -64,7 +66,7 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
|
|
|
64
66
|
.build()
|
|
65
67
|
|
|
66
68
|
private val firstFrameEventGenerator = createFirstFrameEventGenerator()
|
|
67
|
-
val serviceConnection = PlaybackServiceConnection(WeakReference(this))
|
|
69
|
+
val serviceConnection = PlaybackServiceConnection(WeakReference(this), appContext)
|
|
68
70
|
val intervalUpdateClock = IntervalUpdateClock(this)
|
|
69
71
|
|
|
70
72
|
var playing by IgnoreSameSet(false) { new, old ->
|
|
@@ -81,6 +83,12 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
|
|
|
81
83
|
var status: PlayerStatus = IDLE
|
|
82
84
|
var requiresLinearPlayback = false
|
|
83
85
|
var staysActiveInBackground = false
|
|
86
|
+
set(value) {
|
|
87
|
+
field = value
|
|
88
|
+
if (value) {
|
|
89
|
+
startPlaybackService()
|
|
90
|
+
}
|
|
91
|
+
}
|
|
84
92
|
var preservesPitch = false
|
|
85
93
|
set(preservesPitch) {
|
|
86
94
|
field = preservesPitch
|
|
@@ -89,7 +97,7 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
|
|
|
89
97
|
var showNowPlayingNotification = false
|
|
90
98
|
set(value) {
|
|
91
99
|
field = value
|
|
92
|
-
|
|
100
|
+
serviceSetShowNotification(value)
|
|
93
101
|
}
|
|
94
102
|
var duration = 0f
|
|
95
103
|
var isLive = false
|
|
@@ -291,7 +299,6 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
|
|
|
291
299
|
}
|
|
292
300
|
|
|
293
301
|
init {
|
|
294
|
-
ExpoVideoPlaybackService.startService(appContext, context, serviceConnection)
|
|
295
302
|
player.addListener(playerListener)
|
|
296
303
|
player.addAnalyticsListener(analyticsListener)
|
|
297
304
|
VideoManager.registerVideoPlayer(this)
|
|
@@ -303,7 +310,9 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
|
|
|
303
310
|
}
|
|
304
311
|
|
|
305
312
|
override fun close() {
|
|
306
|
-
|
|
313
|
+
if (serviceConnection.isConnected) {
|
|
314
|
+
appContext?.reactContext?.unbindService(serviceConnection)
|
|
315
|
+
}
|
|
307
316
|
serviceConnection.playbackServiceBinder?.service?.unregisterPlayer(player)
|
|
308
317
|
VideoManager.unregisterVideoPlayer(this@VideoPlayer)
|
|
309
318
|
|
|
@@ -398,6 +407,29 @@ class VideoPlayer(val context: Context, appContext: AppContext, source: VideoSou
|
|
|
398
407
|
isLive = false
|
|
399
408
|
}
|
|
400
409
|
|
|
410
|
+
private fun startPlaybackService(): Boolean {
|
|
411
|
+
if (serviceConnection.playbackServiceBinder?.service != null) {
|
|
412
|
+
// PlaybackService already running.
|
|
413
|
+
return true
|
|
414
|
+
}
|
|
415
|
+
val appContext = appContext ?: throw Exceptions.AppContextLost()
|
|
416
|
+
val serviceStarted = ExpoVideoPlaybackService.startService(appContext, context, serviceConnection)
|
|
417
|
+
|
|
418
|
+
if (!serviceStarted) {
|
|
419
|
+
appContext.jsLogger?.error(
|
|
420
|
+
getPlaybackServiceErrorMessage("Expo-video has failed to bind with the playback service binder")
|
|
421
|
+
)
|
|
422
|
+
}
|
|
423
|
+
return serviceStarted
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
private fun serviceSetShowNotification(showNotification: Boolean) {
|
|
427
|
+
if (showNotification) {
|
|
428
|
+
startPlaybackService()
|
|
429
|
+
}
|
|
430
|
+
serviceConnection.playbackServiceBinder?.service?.setShowNotification(showNotification, this.player)
|
|
431
|
+
}
|
|
432
|
+
|
|
401
433
|
fun addListener(videoPlayerListener: VideoPlayerListener) {
|
|
402
434
|
if (listeners.all { it.get() != videoPlayerListener }) {
|
|
403
435
|
listeners.add(WeakReference(videoPlayerListener))
|
|
@@ -115,6 +115,8 @@ export declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {
|
|
|
115
115
|
/**
|
|
116
116
|
* Boolean value determining whether the player should show the now playing notification.
|
|
117
117
|
*
|
|
118
|
+
* > **Note**: On Android, `supportsBackgroundPlayback` property of the [config plugin](#configuration-in-app-config)
|
|
119
|
+
* > has to be `true` for the now playing notification to work.
|
|
118
120
|
* @default false
|
|
119
121
|
* @platform android
|
|
120
122
|
* @platform ios
|
|
@@ -122,6 +124,9 @@ export declare class VideoPlayer extends SharedObject<VideoPlayerEvents> {
|
|
|
122
124
|
showNowPlayingNotification: boolean;
|
|
123
125
|
/**
|
|
124
126
|
* Determines whether the player should continue playing after the app enters the background.
|
|
127
|
+
*
|
|
128
|
+
* > **Note**: The `supportsBackgroundPlayback` property of the [config plugin](#configuration-in-app-config)
|
|
129
|
+
* > has to be `true` for the background playback to work.
|
|
125
130
|
* @default false
|
|
126
131
|
* @platform ios
|
|
127
132
|
* @platform android
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoPlayer.types.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,YAAY,CAAC,iBAAiB,CAAC;IACtE;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,IAAI,EAAE,OAAO,CAAC;IAEd;;;;OAIG;IACH,sBAAsB,EAAE,OAAO,CAAC;IAEhC;;;;;;OAMG;IACH,eAAe,EAAE,eAAe,CAAC;IAEjC;;;;OAIG;IACH,KAAK,EAAE,OAAO,CAAC;IAEf;;;;;;;;;OASG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;OAMG;IACH,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7C;;;;;OAKG;IACH,QAAQ,CAAC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9C;;;OAGG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAE7B;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,cAAc,EAAE,OAAO,CAAC;IAExB;;;;;OAKG;IACH,uBAAuB,EAAE,MAAM,CAAC;IAEhC;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;;;;;;OAQG;IACH,wBAAwB,EAAE,OAAO,CAAC;IAElC;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAEzB;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IAEnC
|
|
1
|
+
{"version":3,"file":"VideoPlayer.types.d.ts","sourceRoot":"","sources":["../src/VideoPlayer.types.ts"],"names":[],"mappings":"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"}
|