expo-libvlc-player 6.0.1 → 6.1.0

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.
Files changed (28) hide show
  1. package/README.md +61 -39
  2. package/android/src/main/java/expo/modules/libvlcplayer/LibVlcPlayerModule.kt +23 -0
  3. package/android/src/main/java/expo/modules/libvlcplayer/LibVlcPlayerView.kt +60 -15
  4. package/android/src/main/java/expo/modules/libvlcplayer/constants/MediaPlayerConstants.kt +9 -0
  5. package/android/src/main/java/expo/modules/libvlcplayer/managers/MediaPlayerManager.kt +8 -0
  6. package/android/src/main/java/expo/modules/libvlcplayer/managers/PictureInPictureManager.kt +335 -0
  7. package/android/src/main/java/expo/modules/libvlcplayer/utils/PictureInPictureFragment.kt +27 -0
  8. package/android/src/main/res/drawable/fast_forward_24px.xml +10 -0
  9. package/android/src/main/res/drawable/fast_rewind_24px.xml +10 -0
  10. package/android/src/main/res/drawable/pause_24px.xml +10 -0
  11. package/android/src/main/res/drawable/play_arrow_24px.xml +10 -0
  12. package/build/LibVlcPlayer.types.d.ts +41 -0
  13. package/build/LibVlcPlayer.types.d.ts.map +1 -1
  14. package/build/LibVlcPlayer.types.js.map +1 -1
  15. package/build/LibVlcPlayerModule.d.ts +6 -0
  16. package/build/LibVlcPlayerModule.d.ts.map +1 -1
  17. package/build/LibVlcPlayerModule.js.map +1 -1
  18. package/ios/LibVlcPlayerModule.swift +19 -0
  19. package/ios/LibVlcPlayerView.swift +74 -28
  20. package/ios/Managers/MediaPlayerManager.swift +3 -1
  21. package/ios/Utils/MediaPlayerDrawable.swift +12 -0
  22. package/ios/Utils/PictureInPictureDrawable.swift +111 -0
  23. package/package.json +1 -1
  24. package/plugin/build/withExpoLibVlcPlayer.d.ts +1 -0
  25. package/plugin/build/withExpoLibVlcPlayer.js +28 -1
  26. package/plugin/src/withExpoLibVlcPlayer.ts +44 -2
  27. package/src/LibVlcPlayer.types.ts +43 -0
  28. package/src/LibVlcPlayerModule.ts +6 -0
package/README.md CHANGED
@@ -68,7 +68,8 @@ You can configure `expo-libvlc-player` using its built-in config plugin if you u
68
68
  [
69
69
  "expo-libvlc-player",
70
70
  {
71
- "localNetworkPermission": "Allow $(PRODUCT_NAME) to access your local network"
71
+ "localNetworkPermission": "Allow $(PRODUCT_NAME) to access your local network",
72
+ "supportsPictureInPicture": true
72
73
  }
73
74
  ]
74
75
  ]
@@ -78,9 +79,10 @@ You can configure `expo-libvlc-player` using its built-in config plugin if you u
78
79
 
79
80
  #### Configurable properties
80
81
 
81
- | Name | Description | Default |
82
- | ------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------ |
83
- | `localNetworkPermission` | A string to set the `NSLocalNetworkUsageDescription` permission message on iOS | `"Allow $(PRODUCT_NAME) to access your local network"` |
82
+ | Name | Description | Default |
83
+ | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
84
+ | `localNetworkPermission` | A string to set the `NSLocalNetworkUsageDescription` permission message on iOS | `"Allow $(PRODUCT_NAME) to access your local network"` |
85
+ | `supportsPictureInPicture` | A boolean value to enable Picture-in-Picture (PiP) on Android and iOS. If `true`, it adds the `android:supportsPictureInPicture` property on Android and the `audio` key to the `UIBackgroundModes` array in the Info.plist file on iOS. If `false`, it removes the property on Android and the key on iOS. When `undefined`, the configuration is not modified | `undefined` |
84
86
 
85
87
  ## Usage
86
88
 
@@ -101,13 +103,22 @@ import LibVlcPlayerModule from "expo-libvlc-player";
101
103
  await LibVlcPlayerModule.triggerNetworkAlert();
102
104
  ```
103
105
 
106
+ Check for Picture-in-Picture (PiP) support:
107
+
108
+ ```tsx
109
+ import LibVlcPlayerModule from "expo-libvlc-player";
110
+
111
+ await LibVlcPlayerModule.isPictureInPictureSupported();
112
+ ```
113
+
104
114
  See the [Example App](example/App.tsx) for additional usage.
105
115
 
106
116
  ### Module methods
107
117
 
108
- | Method | Description | Returns |
109
- | ----------------------- | ---------------------------------------------------------- | --------------- |
110
- | `triggerNetworkAlert()` | Attempts to trigger the local network privacy alert on iOS | `Promise<void>` |
118
+ | Method | Description | Returns |
119
+ | ------------------------------- | ----------------------------------------------------------- | --------------- |
120
+ | `triggerNetworkAlert()` | Attempts to trigger the local network privacy alert on iOS | `Promise<void>` |
121
+ | `isPictureInPictureSupported()` | Checks whether the device supports Picture-in-Picture (PiP) | `boolean` |
111
122
 
112
123
  ### View methods
113
124
 
@@ -121,46 +132,51 @@ See the [Example App](example/App.tsx) for additional usage.
121
132
  | `snapshot(path: string)` | Takes a snapshot of the current media. Must be a valid path string | `Promise<void>` |
122
133
  | `postAction(action: number)` | Posts an answer to a [`Dialog`](#dialog). Must be an integer of `1` or `2` | `Promise<void>` |
123
134
  | `dismiss()` | Dismisses a [`Dialog`](#dialog) | `Promise<void>` |
135
+ | `startPictureInPicture()` | Enters Picture-in-Picture (PiP) mode. Config plugin has to be configured for Picture-in-Picture (PiP) to work | `Promise<void>` |
136
+ | `stopPictureInPicture()` | Exits Picture-in-Picture (PiP) mode on iOS | `Promise<void>` |
124
137
 
125
138
  ### View props
126
139
 
127
140
  The `LibVlcPlayerView` extends React Native `ViewProps` and implements the following:
128
141
 
129
- | Prop | Description | Default |
130
- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------- | ----------- |
131
- | `source` | Sets the source of the media to be played. Set to `null` to release the player | |
132
- | `options` | Sets the VLC options to initialize the player with. See the [VLC Wiki](https://wiki.videolan.org/VLC_command-line_help/) for more | `[]` |
133
- | `tracks` | Sets the player audio, video and subtitle tracks object. See [`Tracks`](#tracks) for more | `undefined` |
134
- | `slaves` | Sets the player audio and subtitle slaves array. See [`Slave`](#slave) for more | `[]` |
135
- | `scale` | Sets the player scaling factor. Must be a float equal or greater than `0` | `0` |
136
- | `aspectRatio` | Sets the container aspect ratio. Must be a valid ratio string, number or `"auto"` | `undefined` |
137
- | `contentFit` | Sets how the video should be scaled to fit in the container | `"contain"` |
138
- | `rate` | Sets the player rate. Must be a float equal or greater than `1` | `1` |
139
- | `time` | Sets the initial player time. Must be an integer in milliseconds | `0` |
140
- | `volume` | Sets the player volume. Must be an integer between `0` and `100` | `100` |
141
- | `mute` | Sets the player volume to `0` when `true`. Previous value is set when `false` | `false` |
142
- | `audioMixingMode` | Determines how the player will interact with other audio in the system | `"auto"` |
143
- | `repeat` | Determines whether the media should repeat once ended | `false` |
144
- | `autoplay` | Determines whether the media should autoplay once created | `true` |
142
+ | Prop | Description | Default |
143
+ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------- | ----------- |
144
+ | `source` | Sets the source of the media to be played. Set to `null` to release the player | |
145
+ | `options` | Sets the VLC options to initialize the player with. See the [VLC Wiki](https://wiki.videolan.org/VLC_command-line_help/) for more | `[]` |
146
+ | `tracks` | Sets the player audio, video and subtitle tracks object. See [`Tracks`](#tracks) for more | `undefined` |
147
+ | `slaves` | Sets the player audio and subtitle slaves array. See [`Slave`](#slave) for more | `[]` |
148
+ | `scale` | Sets the player scaling factor. Must be a float equal or greater than `0` | `0` |
149
+ | `aspectRatio` | Sets the container aspect ratio. Must be a valid ratio string, number or `"auto"` | `undefined` |
150
+ | `contentFit` | Sets how the video should be scaled to fit in the container | `"contain"` |
151
+ | `rate` | Sets the player rate. Must be a float equal or greater than `1` | `1` |
152
+ | `time` | Sets the initial player time. Must be an integer in milliseconds | `0` |
153
+ | `volume` | Sets the player volume. Must be an integer between `0` and `100` | `100` |
154
+ | `mute` | Sets the player volume to `0` when `true`. Previous value is set when `false` | `false` |
155
+ | `audioMixingMode` | Determines how the player will interact with other audio in the system | `"auto"` |
156
+ | `repeat` | Determines whether the media should repeat once ended | `false` |
157
+ | `autoplay` | Determines whether the media should autoplay once created | `true` |
158
+ | `pictureInPicture` | Determines whether the player should allow Picture-in-Picture (PiP) mode | `false` |
145
159
 
146
160
  #### Callbacks
147
161
 
148
- | Prop | Description | Payload |
149
- | -------------------- | ------------------------------------------------ | ----------------------------- |
150
- | `onBuffering` | Called after the `Buffering` player event | |
151
- | `onPlaying` | Called after the `Playing` player event | |
152
- | `onPaused` | Called after the `Paused` player event | |
153
- | `onStopped` | Called after the `Stopped` player event | |
154
- | `onEncounteredError` | Called after the `EncounteredError` player event | `{ error: string }` |
155
- | `onDialogDisplay` | Called after a `Dialog` needs to be displayed | [`Dialog`](#dialog) |
156
- | `onTimeChanged` | Called after the `TimeChanged` player event | `{ time: number }` |
157
- | `onPositionChanged` | Called after the `PositionChanged` player event | `{ position: number }` |
158
- | `onESAdded` | Called after the `ESAdded` player event | [`MediaTracks`](#mediatracks) |
159
- | `onRecordChanged` | Called after the `RecordChanged` player event | [`Recording`](#recording) |
160
- | `onSnapshotTaken` | Called after a media snapshot is taken | `{ path: string }` |
161
- | `onFirstPlay` | Called after the player first playing event | [`MediaInfo`](#mediainfo) |
162
- | `onForeground` | Called after the player enters the foreground | |
163
- | `onBackground` | Called after the player enters the background | |
162
+ | Prop | Description | Payload |
163
+ | ------------------------- | ------------------------------------------------------------ | ----------------------------- |
164
+ | `onBuffering` | Called after the `Buffering` player event | |
165
+ | `onPlaying` | Called after the `Playing` player event | |
166
+ | `onPaused` | Called after the `Paused` player event | |
167
+ | `onStopped` | Called after the `Stopped` player event | |
168
+ | `onEncounteredError` | Called after the `EncounteredError` player event | `{ error: string }` |
169
+ | `onDialogDisplay` | Called after a `Dialog` needs to be displayed | [`Dialog`](#dialog) |
170
+ | `onTimeChanged` | Called after the `TimeChanged` player event | `{ time: number }` |
171
+ | `onPositionChanged` | Called after the `PositionChanged` player event | `{ position: number }` |
172
+ | `onESAdded` | Called after the `ESAdded` player event | [`MediaTracks`](#mediatracks) |
173
+ | `onRecordChanged` | Called after the `RecordChanged` player event | [`Recording`](#recording) |
174
+ | `onSnapshotTaken` | Called after a media snapshot is taken | `{ path: string }` |
175
+ | `onFirstPlay` | Called after the player first playing event | [`MediaInfo`](#mediainfo) |
176
+ | `onForeground` | Called after the player enters the foreground | |
177
+ | `onBackground` | Called after the player enters the background | |
178
+ | `onStartPictureInPicture` | Called after the player enters Picture-in-Picture (PiP) mode | |
179
+ | `onStopPictureInPicture` | Called after the player exits Picture-in-Picture (PiP) mode | |
164
180
 
165
181
  ### Types
166
182
 
@@ -245,6 +261,12 @@ The current workaround attaches the View once a surface is created but this caus
245
261
 
246
262
  https://code.videolan.org/videolan/vlc-android/-/issues/1495
247
263
 
264
+ On iOS, the `VLCKit` player deallocates from the UIView when closing the Picture-in-Picture (PiP) window.
265
+
266
+ The current workaround exclusively selects the current video track but this causes a brief black screen.
267
+
268
+ https://code.videolan.org/videolan/VLCKit/-/issues/743
269
+
248
270
  #### Audio delay
249
271
 
250
272
  On iOS, the `VLCKit` player experiences a small audio delay when resuming or muting media playback.
@@ -1,5 +1,8 @@
1
1
  package expo.modules.libvlcplayer
2
2
 
3
+ import android.app.Activity
4
+ import android.os.Build
5
+ import expo.modules.kotlin.exception.Exceptions
3
6
  import expo.modules.kotlin.modules.Module
4
7
  import expo.modules.kotlin.modules.ModuleDefinition
5
8
  import expo.modules.libvlcplayer.constants.MediaPlayerConstants
@@ -25,13 +28,25 @@ private val PLAYER_EVENTS =
25
28
  "onFirstPlay",
26
29
  "onForeground",
27
30
  "onBackground",
31
+ "onPictureInPictureStart",
32
+ "onPictureInPictureStop",
28
33
  )
29
34
 
30
35
  class LibVlcPlayerModule : Module() {
36
+ private val activity: Activity
37
+ get() = appContext.currentActivity ?: throw Exceptions.MissingActivity()
38
+
31
39
  override fun definition() =
32
40
  ModuleDefinition {
33
41
  Name("ExpoLibVlcPlayer")
34
42
 
43
+ Function("isPictureInPictureSupported") {
44
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
45
+ activity.packageManager.hasSystemFeature(
46
+ android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE,
47
+ )
48
+ }
49
+
35
50
  OnCreate {
36
51
  MediaPlayerManager.onModuleCreate(appContext)
37
52
  }
@@ -105,6 +120,10 @@ class LibVlcPlayerModule : Module() {
105
120
  view.autoplay = autoplay
106
121
  }
107
122
 
123
+ Prop("pictureInPicture", false) { view: LibVlcPlayerView, pictureInPicture: Boolean ->
124
+ view.pictureInPicture = pictureInPicture
125
+ }
126
+
108
127
  OnViewDidUpdateProps { view: LibVlcPlayerView ->
109
128
  view.initPlayer()
110
129
  }
@@ -145,6 +164,10 @@ class LibVlcPlayerModule : Module() {
145
164
  AsyncFunction("dismiss") { view: LibVlcPlayerView ->
146
165
  view.dismiss()
147
166
  }
167
+
168
+ AsyncFunction("startPictureInPicture") { view: LibVlcPlayerView ->
169
+ view.startPictureInPicture()
170
+ }
148
171
  }
149
172
  }
150
173
  }
@@ -25,6 +25,11 @@ import expo.modules.libvlcplayer.records.Recording
25
25
  import expo.modules.libvlcplayer.records.Slave
26
26
  import expo.modules.libvlcplayer.records.Track
27
27
  import expo.modules.libvlcplayer.records.Tracks
28
+ import kotlinx.coroutines.CoroutineScope
29
+ import kotlinx.coroutines.Dispatchers
30
+ import kotlinx.coroutines.Job
31
+ import kotlinx.coroutines.delay
32
+ import kotlinx.coroutines.launch
28
33
  import org.videolan.libvlc.LibVLC
29
34
  import org.videolan.libvlc.Media
30
35
  import org.videolan.libvlc.MediaPlayer
@@ -48,7 +53,9 @@ class LibVlcPlayerView(
48
53
  context: Context,
49
54
  appContext: AppContext,
50
55
  ) : ExpoView(context, appContext) {
51
- private val playerView = VLCVideoLayout(context)
56
+ val playerView: VLCVideoLayout = VLCVideoLayout(context)
57
+ val pictureView: VLCVideoLayout = VLCVideoLayout(context)
58
+ private var pauseIfJob: Job? = null
52
59
 
53
60
  var libVLC: LibVLC? = null
54
61
  var mediaPlayer: MediaPlayer? = null
@@ -56,6 +63,7 @@ class LibVlcPlayerView(
56
63
 
57
64
  var firstPlay: Boolean = true
58
65
  private var shouldInit: Boolean = true
66
+ var isInBackground: Boolean = false
59
67
 
60
68
  val onBuffering by EventDispatcher<Unit>()
61
69
  val onPlaying by EventDispatcher<Unit>()
@@ -71,22 +79,23 @@ class LibVlcPlayerView(
71
79
  val onFirstPlay by EventDispatcher<MediaInfo>()
72
80
  val onForeground by EventDispatcher<Unit>()
73
81
  val onBackground by EventDispatcher<Unit>()
82
+ val onPictureInPictureStart by EventDispatcher<Unit>()
83
+ val onPictureInPictureStop by EventDispatcher<Unit>()
74
84
 
75
85
  init {
76
86
  MediaPlayerManager.registerExpoView(this)
77
- addPlayerView()
78
87
  }
79
88
 
80
89
  override fun onAttachedToWindow() {
81
90
  super.onAttachedToWindow()
82
91
 
83
- attachPlayer()
92
+ attachPlayerView(playerView)
84
93
  }
85
94
 
86
95
  override fun onDetachedFromWindow() {
87
96
  super.onDetachedFromWindow()
88
97
 
89
- detachPlayer()
98
+ detachPlayerView()
90
99
  }
91
100
 
92
101
  override fun onSizeChanged(
@@ -116,12 +125,14 @@ class LibVlcPlayerView(
116
125
  var args = options
117
126
  args.toggleStartPausedOption(autoplay)
118
127
 
128
+ MediaPlayerManager.pictureInPictureManager.setupPipManager(this)
129
+
119
130
  libVLC = LibVLC(context, args)
120
131
  setDialogCallbacks(libVLC!!)
121
132
 
122
133
  mediaPlayer = MediaPlayer(libVLC!!)
123
134
  setPlayerListener(mediaPlayer!!)
124
- attachPlayerView()
135
+ attachPlayerView(playerView)
125
136
 
126
137
  try {
127
138
  URI(source)
@@ -137,6 +148,8 @@ class LibVlcPlayerView(
137
148
 
138
149
  firstPlay = true
139
150
  shouldInit = false
151
+
152
+ addPlayerView(playerView)
140
153
  }
141
154
 
142
155
  fun resetPlayer() {
@@ -145,8 +158,8 @@ class LibVlcPlayerView(
145
158
  }
146
159
 
147
160
  fun attachPlayer() {
148
- attachPlayerView()
149
- addPlayerView()
161
+ attachPlayerView(playerView)
162
+ addPlayerView(playerView)
150
163
  }
151
164
 
152
165
  fun detachPlayer() {
@@ -154,12 +167,12 @@ class LibVlcPlayerView(
154
167
  removePlayerView()
155
168
  }
156
169
 
157
- fun attachPlayerView() {
170
+ fun attachPlayerView(view: VLCVideoLayout) {
158
171
  mediaPlayer?.let { player ->
159
172
  val attached = player.getVLCVout().areViewsAttached()
160
173
 
161
174
  if (!attached) {
162
- player.attachViews(playerView, DISPLAY_MANAGER, ENABLE_SUBTITLES, USE_TEXTURE_VIEW)
175
+ player.attachViews(view, DISPLAY_MANAGER, ENABLE_SUBTITLES, USE_TEXTURE_VIEW)
163
176
  }
164
177
  }
165
178
  }
@@ -174,11 +187,11 @@ class LibVlcPlayerView(
174
187
  }
175
188
  }
176
189
 
177
- fun addPlayerView() {
190
+ fun addPlayerView(view: VLCVideoLayout) {
178
191
  val parent = playerView.parent as? ViewGroup
179
192
 
180
193
  if (parent == null) {
181
- addView(playerView)
194
+ addView(view)
182
195
  }
183
196
  }
184
197
 
@@ -514,6 +527,12 @@ class LibVlcPlayerView(
514
527
  field = value
515
528
  }
516
529
 
530
+ var pictureInPicture: Boolean = false
531
+ set(value) {
532
+ field = value
533
+ shouldInit = true
534
+ }
535
+
517
536
  fun play() {
518
537
  mediaPlayer?.let { player ->
519
538
  if (!autoplay) {
@@ -529,10 +548,21 @@ class LibVlcPlayerView(
529
548
  }
530
549
 
531
550
  fun pauseIf(condition: Boolean? = true) {
532
- mediaPlayer?.let { player ->
533
- val shouldPause = condition == true && player.isPlaying()
534
- if (shouldPause) player.pause()
535
- }
551
+ cancelPauseIf()
552
+
553
+ pauseIfJob =
554
+ CoroutineScope(Dispatchers.Main).launch {
555
+ delay(MediaPlayerConstants.COROUTINE_DELAY_MS)
556
+
557
+ mediaPlayer?.let { player ->
558
+ val shouldPause = condition == true && player.isPlaying()
559
+ if (shouldPause) player.pause()
560
+ }
561
+ }
562
+ }
563
+
564
+ fun cancelPauseIf() {
565
+ pauseIfJob?.cancel()
536
566
  }
537
567
 
538
568
  fun stop() {
@@ -630,6 +660,20 @@ class LibVlcPlayerView(
630
660
  }
631
661
  }
632
662
 
663
+ fun startPictureInPicture() {
664
+ MediaPlayerManager.pictureInPictureManager.startPictureInPicture(this)
665
+ }
666
+
667
+ fun onStartPictureInPicture() {
668
+ MediaPlayerManager.pictureInPictureManager.layoutForPipEnter()
669
+ onPictureInPictureStart(Unit)
670
+ }
671
+
672
+ fun onStopPictureInPicture() {
673
+ MediaPlayerManager.pictureInPictureManager.layoutForPipExit()
674
+ onPictureInPictureStop(Unit)
675
+ }
676
+
633
677
  fun retryUntil(
634
678
  maxRetries: Int = MediaPlayerConstants.MAX_RETRY_COUNT,
635
679
  retry: Int = 0,
@@ -707,6 +751,7 @@ fun LibVlcPlayerView.setPlayerListener(mediaPlayer: MediaPlayer?) {
707
751
  }
708
752
  }
709
753
 
754
+ MediaPlayerManager.pictureInPictureManager.setPipActions()
710
755
  MediaPlayerManager.keepAwakeManager.toggleKeepAwake()
711
756
 
712
757
  retryUntil { isLastAttempt ->
@@ -7,6 +7,15 @@ object MediaPlayerConstants {
7
7
  const val MIN_PLAYER_VOLUME: Int = 0
8
8
  const val MAX_PLAYER_VOLUME: Int = 100
9
9
 
10
+ const val ACTION_PIP_CONTROL: String = "pip_control"
11
+ const val EXTRA_CONTROL_TYPE: String = "control_type"
12
+ const val EXTRA_CONTROL_REWIND: Int = 1
13
+ const val EXTRA_CONTROL_PLAY: Int = 2
14
+ const val EXTRA_CONTROL_PAUSE: Int = 3
15
+ const val EXTRA_CONTROL_FORWARD: Int = 4
16
+ const val SEEK_STEP_MS: Long = 10_000L
17
+
18
+ const val COROUTINE_DELAY_MS: Long = 1_000L
10
19
  const val RETRY_DELAY_MS: Long = 200L
11
20
  const val MAX_RETRY_COUNT: Int = 5
12
21
  }
@@ -8,6 +8,7 @@ import java.util.WeakHashMap
8
8
  object MediaPlayerManager {
9
9
  lateinit var audioFocusManager: AudioFocusManager
10
10
  lateinit var keepAwakeManager: KeepAwakeManager
11
+ lateinit var pictureInPictureManager: PictureInPictureManager
11
12
 
12
13
  val expoViews: MutableSet<LibVlcPlayerView> = Collections.newSetFromMap(WeakHashMap())
13
14
 
@@ -27,6 +28,10 @@ object MediaPlayerManager {
27
28
  if (!this::keepAwakeManager.isInitialized) {
28
29
  keepAwakeManager = KeepAwakeManager(appContext)
29
30
  }
31
+
32
+ if (!this::pictureInPictureManager.isInitialized) {
33
+ pictureInPictureManager = PictureInPictureManager(appContext)
34
+ }
30
35
  }
31
36
 
32
37
  fun onModuleDestroy() {
@@ -38,6 +43,8 @@ object MediaPlayerManager {
38
43
  fun onModuleForeground() {
39
44
  expoViews.forEach { view ->
40
45
  view.onForeground(Unit)
46
+ view.cancelPauseIf()
47
+ view.isInBackground = false
41
48
  }
42
49
  }
43
50
 
@@ -45,6 +52,7 @@ object MediaPlayerManager {
45
52
  expoViews.forEach { view ->
46
53
  view.onBackground(Unit)
47
54
  view.pauseIf()
55
+ view.isInBackground = true
48
56
  }
49
57
  }
50
58
  }