expo-video 1.2.2 → 1.2.4
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 +28 -0
- package/android/build.gradle +2 -2
- package/android/src/main/AndroidManifest.xml +1 -1
- package/android/src/main/java/expo/modules/video/AudioFocusManager.kt +187 -0
- package/android/src/main/java/expo/modules/video/PlayerEvent.kt +54 -0
- package/android/src/main/java/expo/modules/video/VideoExceptions.kt +3 -0
- package/android/src/main/java/expo/modules/video/VideoManager.kt +11 -2
- package/android/src/main/java/expo/modules/video/VideoModule.kt +13 -6
- package/android/src/main/java/expo/modules/video/VideoPlayer.kt +59 -109
- package/android/src/main/java/expo/modules/video/VideoPlayerListener.kt +18 -0
- package/android/src/main/java/expo/modules/video/VideoView.kt +27 -5
- package/android/src/main/java/expo/modules/video/delegates/IgnoreSameSet.kt +24 -0
- package/android/src/main/java/expo/modules/video/drawing/OutlineProvider.kt +1 -1
- package/android/src/main/java/expo/modules/video/{ContentFit.kt → enums/ContentFit.kt} +1 -1
- package/android/src/main/java/expo/modules/video/{ExpoVideoPlaybackService.kt → playbackService/ExpoVideoPlaybackService.kt} +20 -1
- package/android/src/main/java/expo/modules/video/playbackService/PlaybackServiceConnection.kt +36 -0
- package/android/src/main/java/expo/modules/video/{VideoMediaSessionCallback.kt → playbackService/VideoMediaSessionCallback.kt} +1 -1
- package/android/src/main/java/expo/modules/video/records/VideoSource.kt +3 -2
- package/android/src/main/java/expo/modules/video/records/VolumeEvent.kt +1 -1
- package/android/src/main/java/expo/modules/video/{DataSourceUtils.kt → utils/DataSourceUtils.kt} +13 -2
- package/android/src/main/java/expo/modules/video/{YogaUtils.kt → utils/YogaUtils.kt} +1 -1
- package/build/VideoPlayer.types.d.ts +1 -1
- package/build/VideoPlayer.types.d.ts.map +1 -1
- package/build/VideoPlayer.types.js.map +1 -1
- package/build/VideoPlayer.web.d.ts +9 -1
- package/build/VideoPlayer.web.d.ts.map +1 -1
- package/build/VideoPlayer.web.js +61 -13
- package/build/VideoPlayer.web.js.map +1 -1
- package/build/VideoView.d.ts +3 -0
- package/build/VideoView.d.ts.map +1 -1
- package/build/VideoView.js +3 -0
- package/build/VideoView.js.map +1 -1
- package/build/VideoView.types.d.ts +13 -0
- package/build/VideoView.types.d.ts.map +1 -1
- package/build/VideoView.types.js.map +1 -1
- package/build/VideoView.web.d.ts.map +1 -1
- package/build/VideoView.web.js +42 -13
- package/build/VideoView.web.js.map +1 -1
- package/ios/NowPlayingManager.swift +6 -10
- package/ios/VideoModule.swift +19 -0
- package/ios/VideoPlayer.swift +7 -1
- package/package.json +2 -2
- package/plugin/build/withExpoVideo.d.ts +5 -1
- package/plugin/build/withExpoVideo.js +21 -3
- package/plugin/src/withExpoVideo.ts +35 -3
- package/src/VideoPlayer.types.ts +1 -1
- package/src/VideoPlayer.web.tsx +72 -13
- package/src/VideoView.tsx +3 -0
- package/src/VideoView.types.ts +14 -0
- package/src/VideoView.web.tsx +49 -14
- package/android/src/main/java/expo/modules/video/VideoPlayerAudioFocusManager.kt +0 -105
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,34 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 1.2.4 — 2024-07-30
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- [Android] Fix Audio Manager pausing player on the wrong thread and conflicts between players. ([#30453](https://github.com/expo/expo/pull/30453) by [@behenate](https://github.com/behenate))
|
|
18
|
+
- [Android] Fix Audio Manager pausing player on the wrong thread and conflicts between players. ([#30453](https://github.com/expo/expo/pull/30453) by [@behenate](https://github.com/behenate))
|
|
19
|
+
|
|
20
|
+
### 💡 Others
|
|
21
|
+
|
|
22
|
+
- [Android] Refactor `VideoPlayer.kt`, organize files ([#30452](https://github.com/expo/expo/pull/30452) by [@behenate](https://github.com/behenate))
|
|
23
|
+
|
|
24
|
+
## 1.2.3 — 2024-07-11
|
|
25
|
+
|
|
26
|
+
### 🛠 Breaking changes
|
|
27
|
+
|
|
28
|
+
- [Android][iOS] Now Picture in Picture has to be enabled via the config plugin to work. ([#30068](https://github.com/expo/expo/pull/30068) by [@behenate](https://github.com/behenate))
|
|
29
|
+
|
|
30
|
+
### 🎉 New features
|
|
31
|
+
|
|
32
|
+
- [Web] Add support for events. ([#29742](https://github.com/expo/expo/pull/29742) by [@behenate](https://github.com/behenate))
|
|
33
|
+
- [iOS] Add ability to disable live text interaction. ([#30093](https://github.com/expo/expo/pull/30093) by [@fobos531](https://github.com/fobos531))
|
|
34
|
+
|
|
35
|
+
### 🐛 Bug fixes
|
|
36
|
+
|
|
37
|
+
- [Web] Fix `AudioContext` being created before user interaction causing playback issues. ([#29695](https://github.com/expo/expo/pull/29695) by [@behenate](https://github.com/behenate))
|
|
38
|
+
- [iOS] Fix a race condition causing crashes when deallocating the player. ([#30022](https://github.com/expo/expo/pull/30022) by [@behenate](https://github.com/behenate))
|
|
39
|
+
- Add missing `react` and `react-native` peer dependencies for isolated modules. ([#30489](https://github.com/expo/expo/pull/30489) by [@byCedric](https://github.com/byCedric))
|
|
40
|
+
|
|
13
41
|
## 1.2.2 — 2024-07-03
|
|
14
42
|
|
|
15
43
|
### 🐛 Bug fixes
|
package/android/build.gradle
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
apply plugin: 'com.android.library'
|
|
2
2
|
|
|
3
3
|
group = 'host.exp.exponent'
|
|
4
|
-
version = '1.2.
|
|
4
|
+
version = '1.2.4'
|
|
5
5
|
|
|
6
6
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
7
7
|
apply from: expoModulesCorePlugin
|
|
@@ -14,7 +14,7 @@ android {
|
|
|
14
14
|
namespace "expo.modules.video"
|
|
15
15
|
defaultConfig {
|
|
16
16
|
versionCode 1
|
|
17
|
-
versionName '1.2.
|
|
17
|
+
versionName '1.2.4'
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<application>
|
|
7
7
|
<activity android:name=".FullscreenPlayerActivity" />
|
|
8
8
|
<service
|
|
9
|
-
android:name=".ExpoVideoPlaybackService"
|
|
9
|
+
android:name=".playbackService.ExpoVideoPlaybackService"
|
|
10
10
|
android:exported="false"
|
|
11
11
|
android:foregroundServiceType="mediaPlayback">
|
|
12
12
|
<intent-filter>
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
package expo.modules.video
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.media.AudioAttributes
|
|
5
|
+
import android.media.AudioFocusRequest
|
|
6
|
+
import android.media.AudioManager
|
|
7
|
+
import android.os.Build
|
|
8
|
+
import androidx.media3.common.util.UnstableApi
|
|
9
|
+
import expo.modules.kotlin.AppContext
|
|
10
|
+
import expo.modules.video.records.VolumeEvent
|
|
11
|
+
import kotlinx.coroutines.launch
|
|
12
|
+
import java.lang.ref.WeakReference
|
|
13
|
+
|
|
14
|
+
@UnstableApi
|
|
15
|
+
class AudioFocusManager(private val appContext: AppContext) : AudioManager.OnAudioFocusChangeListener, VideoPlayerListener {
|
|
16
|
+
private val audioManager by lazy {
|
|
17
|
+
appContext.reactContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: run {
|
|
18
|
+
throw FailedToGetAudioFocusManagerException()
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private var players: MutableList<WeakReference<VideoPlayer>> = mutableListOf()
|
|
23
|
+
private var currentFocusRequest: AudioFocusRequest? = null
|
|
24
|
+
private val anyPlayerRequiresFocus: Boolean
|
|
25
|
+
get() = players.any {
|
|
26
|
+
playerRequiresFocus(it)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private fun requestAudioFocus() {
|
|
30
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
31
|
+
// We already have audio focus
|
|
32
|
+
if (currentFocusRequest != null) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
val newFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run {
|
|
37
|
+
setAudioAttributes(
|
|
38
|
+
AudioAttributes.Builder().run {
|
|
39
|
+
setUsage(AudioAttributes.USAGE_MEDIA)
|
|
40
|
+
setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
|
|
41
|
+
setOnAudioFocusChangeListener(this@AudioFocusManager)
|
|
42
|
+
build()
|
|
43
|
+
}
|
|
44
|
+
).build()
|
|
45
|
+
}
|
|
46
|
+
this.currentFocusRequest = newFocusRequest
|
|
47
|
+
audioManager.requestAudioFocus(newFocusRequest)
|
|
48
|
+
} else {
|
|
49
|
+
@Suppress("DEPRECATION")
|
|
50
|
+
audioManager.requestAudioFocus(
|
|
51
|
+
this,
|
|
52
|
+
AudioManager.STREAM_MUSIC,
|
|
53
|
+
AudioManager.AUDIOFOCUS_GAIN
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private fun abandonAudioFocus() {
|
|
59
|
+
currentFocusRequest?.let {
|
|
60
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
61
|
+
audioManager.abandonAudioFocusRequest(it)
|
|
62
|
+
} else {
|
|
63
|
+
@Suppress("DEPRECATION")
|
|
64
|
+
audioManager.abandonAudioFocus(this)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
currentFocusRequest = null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fun registerPlayer(player: VideoPlayer) {
|
|
71
|
+
players.find { it.get() == player } ?: run {
|
|
72
|
+
players.add(WeakReference(player))
|
|
73
|
+
}
|
|
74
|
+
player.addListener(this)
|
|
75
|
+
updateAudioFocus()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
fun unregisterPlayer(player: VideoPlayer) {
|
|
79
|
+
player.removeListener(this)
|
|
80
|
+
players.removeAll { it.get() == player }
|
|
81
|
+
updateAudioFocus()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// VideoPlayerListener
|
|
85
|
+
|
|
86
|
+
override fun onIsPlayingChanged(player: VideoPlayer, isPlaying: Boolean, oldIsPlaying: Boolean?) {
|
|
87
|
+
// we can't use `updateAudioFocus`, because when losing focus the videos are paused sequentially,
|
|
88
|
+
// which can lead to unexpected results.
|
|
89
|
+
if (!isPlaying && !anyPlayerRequiresFocus) {
|
|
90
|
+
abandonAudioFocus()
|
|
91
|
+
} else if (isPlaying && anyPlayerRequiresFocus) {
|
|
92
|
+
requestAudioFocus()
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
override fun onVolumeChanged(player: VideoPlayer, newValue: VolumeEvent, oldVolume: VolumeEvent?) {
|
|
97
|
+
updateAudioFocus()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// AudioManager.OnAudioFocusChangeListener
|
|
101
|
+
|
|
102
|
+
override fun onAudioFocusChange(focusChange: Int) {
|
|
103
|
+
when (focusChange) {
|
|
104
|
+
AudioManager.AUDIOFOCUS_LOSS -> {
|
|
105
|
+
appContext.mainQueue.launch {
|
|
106
|
+
players.forEach {
|
|
107
|
+
pausePlayerIfUnmuted(it)
|
|
108
|
+
}
|
|
109
|
+
currentFocusRequest = null
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
|
|
114
|
+
appContext.mainQueue.launch {
|
|
115
|
+
players.forEach {
|
|
116
|
+
pausePlayerIfUnmuted(it)
|
|
117
|
+
}
|
|
118
|
+
currentFocusRequest = null
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
|
|
123
|
+
appContext.mainQueue.launch {
|
|
124
|
+
players.forEach {
|
|
125
|
+
duckPlayer(it)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
AudioManager.AUDIOFOCUS_GAIN -> {
|
|
131
|
+
// TODO: For now this behaves like iOS and doesn't resume playback automatically
|
|
132
|
+
// In future versions we can add a prop to control this behavior.
|
|
133
|
+
appContext.mainQueue.launch {
|
|
134
|
+
players.forEach {
|
|
135
|
+
unduckPlayer(it)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Utils
|
|
143
|
+
|
|
144
|
+
private fun playerRequiresFocus(weakPlayer: WeakReference<VideoPlayer>): Boolean {
|
|
145
|
+
return weakPlayer.get()?.let {
|
|
146
|
+
!it.muted && it.playing && it.volume > 0
|
|
147
|
+
} ?: run {
|
|
148
|
+
false
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private fun pausePlayerIfUnmuted(weakPlayer: WeakReference<VideoPlayer>) {
|
|
153
|
+
weakPlayer.get()?.let { videoPlayer ->
|
|
154
|
+
if (!videoPlayer.muted) {
|
|
155
|
+
appContext.mainQueue.launch {
|
|
156
|
+
videoPlayer.player.pause()
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private fun duckPlayer(weakPlayer: WeakReference<VideoPlayer>) {
|
|
163
|
+
weakPlayer.get()?.let { player ->
|
|
164
|
+
appContext.mainQueue.launch {
|
|
165
|
+
player.volume /= 2f
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private fun unduckPlayer(weakPlayer: WeakReference<VideoPlayer>) {
|
|
171
|
+
weakPlayer.get()?.let { player ->
|
|
172
|
+
if (!player.muted) {
|
|
173
|
+
appContext.mainQueue.launch {
|
|
174
|
+
player.volume = player.userVolume
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private fun updateAudioFocus() {
|
|
181
|
+
if (anyPlayerRequiresFocus) {
|
|
182
|
+
requestAudioFocus()
|
|
183
|
+
} else {
|
|
184
|
+
abandonAudioFocus()
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
package expo.modules.video
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.OptIn
|
|
4
|
+
import androidx.media3.common.util.UnstableApi
|
|
5
|
+
import expo.modules.video.enums.PlayerStatus
|
|
6
|
+
import expo.modules.video.records.PlaybackError
|
|
7
|
+
import expo.modules.video.records.VideoSource
|
|
8
|
+
import expo.modules.video.records.VolumeEvent
|
|
9
|
+
|
|
10
|
+
@OptIn(UnstableApi::class)
|
|
11
|
+
sealed class PlayerEvent {
|
|
12
|
+
open val name: String = ""
|
|
13
|
+
open val arguments: Array<out Any?> = arrayOf()
|
|
14
|
+
|
|
15
|
+
data class StatusChanged(val status: PlayerStatus, val oldStatus: PlayerStatus?, val error: PlaybackError?) : PlayerEvent() {
|
|
16
|
+
override val name = "statusChange"
|
|
17
|
+
override val arguments = arrayOf(status, oldStatus, error)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
data class IsPlayingChanged(val isPlaying: Boolean, val oldIsPlaying: Boolean?) : PlayerEvent() {
|
|
21
|
+
override val name = "playingChange"
|
|
22
|
+
override val arguments = arrayOf(isPlaying, oldIsPlaying)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
data class VolumeChanged(val newValue: VolumeEvent, val oldValue: VolumeEvent?) : PlayerEvent() {
|
|
26
|
+
override val name = "playingChange"
|
|
27
|
+
override val arguments = arrayOf(newValue, oldValue)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
data class SourceChanged(val source: VideoSource?, val oldSource: VideoSource?) : PlayerEvent() {
|
|
31
|
+
override val name = "sourceChange"
|
|
32
|
+
override val arguments = arrayOf(source, oldSource)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
data class PlaybackRateChanged(val rate: Float, val oldRate: Float?) : PlayerEvent() {
|
|
36
|
+
override val name = "playbackRateChange"
|
|
37
|
+
override val arguments = arrayOf(rate, oldRate)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class PlayedToEnd : PlayerEvent() {
|
|
41
|
+
override val name = "playToEnd"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fun emit(player: VideoPlayer, listeners: List<VideoPlayerListener>) {
|
|
45
|
+
when (this) {
|
|
46
|
+
is StatusChanged -> listeners.forEach { it.onStatusChanged(player, status, oldStatus, error) }
|
|
47
|
+
is IsPlayingChanged -> listeners.forEach { it.onIsPlayingChanged(player, isPlaying, oldIsPlaying) }
|
|
48
|
+
is VolumeChanged -> listeners.forEach { it.onVolumeChanged(player, newValue, oldValue) }
|
|
49
|
+
is SourceChanged -> listeners.forEach { it.onSourceChanged(player, source, oldSource) }
|
|
50
|
+
is PlaybackRateChanged -> listeners.forEach { it.onPlaybackRateChanged(player, rate, oldRate) }
|
|
51
|
+
is PlayedToEnd -> listeners.forEach { it.onPlayedToEnd(player) }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -15,6 +15,9 @@ internal class MethodUnsupportedException(methodName: String) :
|
|
|
15
15
|
internal class PictureInPictureEnterException(message: String?) :
|
|
16
16
|
CodedException("Failed to enter Picture in Picture mode${message?.let { ". $message" } ?: ""}")
|
|
17
17
|
|
|
18
|
+
internal class PictureInPictureConfigurationException :
|
|
19
|
+
CodedException("Current activity does not support picture-in-picture. Make sure you have configured the `expo-video` config plugin correctly.")
|
|
20
|
+
|
|
18
21
|
internal class PictureInPictureUnsupportedException :
|
|
19
22
|
CodedException("Picture in Picture mode is not supported on this device")
|
|
20
23
|
|
|
@@ -2,6 +2,7 @@ package expo.modules.video
|
|
|
2
2
|
|
|
3
3
|
import androidx.annotation.OptIn
|
|
4
4
|
import androidx.media3.common.util.UnstableApi
|
|
5
|
+
import expo.modules.kotlin.AppContext
|
|
5
6
|
|
|
6
7
|
// Helper class used to keep track of all existing VideoViews and VideoPlayers
|
|
7
8
|
@OptIn(UnstableApi::class)
|
|
@@ -14,6 +15,12 @@ object VideoManager {
|
|
|
14
15
|
// Keeps track of all existing VideoPlayers, and whether they are attached to a VideoView
|
|
15
16
|
private var videoPlayersToVideoViews = mutableMapOf<VideoPlayer, MutableList<VideoView>>()
|
|
16
17
|
|
|
18
|
+
private lateinit var audioFocusManager: AudioFocusManager
|
|
19
|
+
|
|
20
|
+
fun onModuleCreated(appContext: AppContext) {
|
|
21
|
+
audioFocusManager = AudioFocusManager(appContext)
|
|
22
|
+
}
|
|
23
|
+
|
|
17
24
|
fun registerVideoView(videoView: VideoView) {
|
|
18
25
|
videoViews[videoView.id] = videoView
|
|
19
26
|
}
|
|
@@ -28,10 +35,12 @@ object VideoManager {
|
|
|
28
35
|
|
|
29
36
|
fun registerVideoPlayer(videoPlayer: VideoPlayer) {
|
|
30
37
|
videoPlayersToVideoViews[videoPlayer] = videoPlayersToVideoViews[videoPlayer] ?: mutableListOf()
|
|
38
|
+
audioFocusManager.registerPlayer(videoPlayer)
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
fun unregisterVideoPlayer(videoPlayer: VideoPlayer) {
|
|
34
42
|
videoPlayersToVideoViews.remove(videoPlayer)
|
|
43
|
+
audioFocusManager.unregisterPlayer(videoPlayer)
|
|
35
44
|
}
|
|
36
45
|
|
|
37
46
|
fun onVideoPlayerAttachedToView(videoPlayer: VideoPlayer, videoView: VideoView) {
|
|
@@ -43,7 +52,7 @@ object VideoManager {
|
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
if (videoPlayersToVideoViews[videoPlayer]?.size == 1) {
|
|
46
|
-
videoPlayer.playbackServiceBinder?.service?.registerPlayer(videoPlayer.player)
|
|
55
|
+
videoPlayer.serviceConnection.playbackServiceBinder?.service?.registerPlayer(videoPlayer.player)
|
|
47
56
|
}
|
|
48
57
|
}
|
|
49
58
|
|
|
@@ -52,7 +61,7 @@ object VideoManager {
|
|
|
52
61
|
|
|
53
62
|
// Unregister disconnected VideoPlayers from the playback service
|
|
54
63
|
if (videoPlayersToVideoViews[videoPlayer] == null || videoPlayersToVideoViews[videoPlayer]?.size == 0) {
|
|
55
|
-
videoPlayer.playbackServiceBinder?.service?.unregisterPlayer(videoPlayer.player)
|
|
64
|
+
videoPlayer.serviceConnection.playbackServiceBinder?.service?.unregisterPlayer(videoPlayer.player)
|
|
56
65
|
}
|
|
57
66
|
}
|
|
58
67
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
package expo.modules.video
|
|
4
4
|
|
|
5
5
|
import android.app.Activity
|
|
6
|
-
import android.
|
|
6
|
+
import android.net.Uri
|
|
7
7
|
import androidx.media3.common.PlaybackParameters
|
|
8
8
|
import androidx.media3.common.Player.REPEAT_MODE_OFF
|
|
9
9
|
import androidx.media3.common.Player.REPEAT_MODE_ONE
|
|
@@ -16,7 +16,10 @@ import expo.modules.kotlin.exception.Exceptions
|
|
|
16
16
|
import expo.modules.kotlin.modules.Module
|
|
17
17
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
18
18
|
import expo.modules.kotlin.types.Either
|
|
19
|
+
import expo.modules.video.enums.ContentFit
|
|
19
20
|
import expo.modules.video.records.VideoSource
|
|
21
|
+
import expo.modules.video.utils.ifYogaDefinedUse
|
|
22
|
+
import expo.modules.video.utils.makeYogaUndefinedIfNegative
|
|
20
23
|
import kotlinx.coroutines.launch
|
|
21
24
|
import kotlinx.coroutines.runBlocking
|
|
22
25
|
|
|
@@ -25,12 +28,14 @@ import kotlinx.coroutines.runBlocking
|
|
|
25
28
|
class VideoModule : Module() {
|
|
26
29
|
private val activity: Activity
|
|
27
30
|
get() = appContext.activityProvider?.currentActivity ?: throw Exceptions.MissingActivity()
|
|
28
|
-
private val reactContext: Context
|
|
29
|
-
get() = appContext.reactContext ?: throw Exceptions.ReactContextLost()
|
|
30
31
|
|
|
31
32
|
override fun definition() = ModuleDefinition {
|
|
32
33
|
Name("ExpoVideo")
|
|
33
34
|
|
|
35
|
+
OnCreate {
|
|
36
|
+
VideoManager.onModuleCreated(appContext)
|
|
37
|
+
}
|
|
38
|
+
|
|
34
39
|
Function("isPictureInPictureSupported") {
|
|
35
40
|
return@Function VideoView.isPictureInPictureSupported(activity)
|
|
36
41
|
}
|
|
@@ -128,7 +133,9 @@ class VideoModule : Module() {
|
|
|
128
133
|
}
|
|
129
134
|
|
|
130
135
|
AsyncFunction("startPictureInPicture") { view: VideoView ->
|
|
131
|
-
view.
|
|
136
|
+
view.runWithPiPMisconfigurationSoftHandling(true) {
|
|
137
|
+
view.enterPictureInPicture()
|
|
138
|
+
}
|
|
132
139
|
}
|
|
133
140
|
|
|
134
141
|
AsyncFunction("stopPictureInPicture") {
|
|
@@ -266,12 +273,12 @@ class VideoModule : Module() {
|
|
|
266
273
|
}
|
|
267
274
|
}
|
|
268
275
|
|
|
269
|
-
Function("replace") { ref: VideoPlayer, source: Either<
|
|
276
|
+
Function("replace") { ref: VideoPlayer, source: Either<Uri, VideoSource>? ->
|
|
270
277
|
val videoSource = source?.let {
|
|
271
278
|
if (it.`is`(VideoSource::class)) {
|
|
272
279
|
it.get(VideoSource::class)
|
|
273
280
|
} else {
|
|
274
|
-
VideoSource(it.get(
|
|
281
|
+
VideoSource(it.get(Uri::class))
|
|
275
282
|
}
|
|
276
283
|
}
|
|
277
284
|
|