bitmovin-player-react-native 0.3.1 → 0.4.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 (35) hide show
  1. package/README.md +238 -24
  2. package/RNBitmovinPlayer.podspec +3 -1
  3. package/android/build.gradle +7 -5
  4. package/android/src/main/java/com/bitmovin/player/reactnative/DrmModule.kt +4 -5
  5. package/android/src/main/java/com/bitmovin/player/reactnative/PlayerModule.kt +41 -5
  6. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt +273 -2
  7. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt +45 -5
  8. package/android/src/main/java/com/bitmovin/player/reactnative/SourceModule.kt +4 -5
  9. package/android/src/main/java/com/bitmovin/player/reactnative/UuidModule.kt +3 -1
  10. package/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt +217 -0
  11. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReadableArray.kt +19 -2
  12. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/WritableMap.kt +19 -0
  13. package/android/src/main/java/com/bitmovin/player/reactnative/ui/RNPictureInPictureHandler.kt +191 -0
  14. package/ios/AudioSessionModule.m +10 -0
  15. package/ios/AudioSessionModule.swift +65 -0
  16. package/ios/Event+JSON.swift +123 -0
  17. package/ios/PlayerModule.m +6 -0
  18. package/ios/PlayerModule.swift +44 -0
  19. package/ios/RCTConvert+BitmovinPlayer.swift +208 -0
  20. package/ios/RNPlayerView+PlayerListener.swift +52 -0
  21. package/ios/RNPlayerView+UserInterfaceListener.swift +19 -0
  22. package/ios/RNPlayerView.swift +17 -0
  23. package/ios/RNPlayerViewManager.m +18 -1
  24. package/ios/RNPlayerViewManager.swift +2 -1
  25. package/lib/index.d.ts +419 -3
  26. package/lib/index.js +92 -33
  27. package/lib/index.mjs +75 -19
  28. package/package.json +1 -1
  29. package/src/advertising.ts +155 -0
  30. package/src/audioSession.ts +47 -0
  31. package/src/components/PlayerView/events.ts +39 -3
  32. package/src/components/PlayerView/index.tsx +31 -11
  33. package/src/events.ts +212 -0
  34. package/src/index.ts +2 -0
  35. package/src/player.ts +36 -1
@@ -1,6 +1,9 @@
1
1
  package com.bitmovin.player.reactnative
2
2
 
3
3
  import android.annotation.SuppressLint
4
+ import android.content.res.Configuration
5
+ import android.graphics.Rect
6
+ import android.view.View
4
7
  import android.view.ViewGroup
5
8
  import android.widget.LinearLayout
6
9
  import com.bitmovin.player.PlayerView
@@ -10,6 +13,9 @@ import com.bitmovin.player.api.event.PlayerEvent
10
13
  import com.bitmovin.player.api.event.SourceEvent
11
14
  import com.bitmovin.player.api.event.on
12
15
  import com.bitmovin.player.reactnative.converter.JsonConverter
16
+ import com.bitmovin.player.reactnative.ui.RNPictureInPictureDelegate
17
+ import com.bitmovin.player.reactnative.ui.RNPictureInPictureHandler
18
+ import com.facebook.react.bridge.LifecycleEventListener
13
19
  import com.facebook.react.bridge.ReactApplicationContext
14
20
  import com.facebook.react.bridge.ReactContext
15
21
  import com.facebook.react.uimanager.events.RCTEventEmitter
@@ -20,9 +26,10 @@ import com.facebook.react.uimanager.events.RCTEventEmitter
20
26
  * exposes player events as bubbling events.
21
27
  */
22
28
  @SuppressLint("ViewConstructor")
23
- class RNPlayerView(context: ReactApplicationContext) : LinearLayout(context) {
29
+ class RNPlayerView(val context: ReactApplicationContext) : LinearLayout(context),
30
+ LifecycleEventListener, View.OnLayoutChangeListener, RNPictureInPictureDelegate {
24
31
  /**
25
- * Reference to the shared player view set as child.
32
+ * Associated bitmovin's `PlayerView`.
26
33
  */
27
34
  var playerView: PlayerView? = null
28
35
 
@@ -35,6 +42,57 @@ class RNPlayerView(context: ReactApplicationContext) : LinearLayout(context) {
35
42
  playerView?.player = value
36
43
  }
37
44
 
45
+ /**
46
+ * Object that handles PiP mode changes in React Native.
47
+ */
48
+ var pictureInPictureHandler: RNPictureInPictureHandler? = null
49
+
50
+ /**
51
+ * Whether this view should pause video playback when activity's onPause is called.
52
+ * By default, `shouldPausePlaybackOnActivityPause` is set to false when entering PiP mode.
53
+ */
54
+ private var shouldPausePlaybackOnActivityPause = true
55
+
56
+ /**
57
+ * Register this view as an activity lifecycle listener on initialization.
58
+ */
59
+ init {
60
+ context.addLifecycleEventListener(this)
61
+ }
62
+
63
+ /**
64
+ * Cleans up the resources and listeners produced by this view.
65
+ */
66
+ fun dispose() {
67
+ stopBubblingEvents()
68
+ context.removeLifecycleEventListener(this)
69
+ playerView?.removeOnLayoutChangeListener(this)
70
+ }
71
+
72
+ /**
73
+ * Activity's onResume
74
+ */
75
+ override fun onHostResume() {
76
+ playerView?.onResume()
77
+ }
78
+
79
+ /**
80
+ * Activity's onPause
81
+ */
82
+ override fun onHostPause() {
83
+ if (shouldPausePlaybackOnActivityPause) {
84
+ playerView?.onPause()
85
+ }
86
+ shouldPausePlaybackOnActivityPause = true
87
+ }
88
+
89
+ /**
90
+ * Activity's onDestroy
91
+ */
92
+ override fun onHostDestroy() {
93
+ playerView?.onDestroy()
94
+ }
95
+
38
96
  /**
39
97
  * Set the given `playerView` as child and start bubbling events.
40
98
  * @param playerView Shared player view instance.
@@ -44,6 +102,71 @@ class RNPlayerView(context: ReactApplicationContext) : LinearLayout(context) {
44
102
  if (playerView.parent != this) {
45
103
  (playerView.parent as ViewGroup?)?.removeView(playerView)
46
104
  addView(playerView)
105
+ startBubblingEvents()
106
+ }
107
+ pictureInPictureHandler?.let {
108
+ it.setDelegate(this)
109
+ playerView.setPictureInPictureHandler(it)
110
+ playerView.addOnLayoutChangeListener(this)
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Called whenever this view's activity configuration changes.
116
+ */
117
+ override fun onConfigurationChanged(newConfig: Configuration?) {
118
+ super.onConfigurationChanged(newConfig)
119
+ pictureInPictureHandler?.onConfigurationChanged(newConfig)
120
+ }
121
+
122
+ /**
123
+ * Called when the player has just entered PiP mode.
124
+ */
125
+ override fun onEnterPictureInPicture() {
126
+ // Playback shouldn't be paused when entering PiP mode.
127
+ shouldPausePlaybackOnActivityPause = false
128
+ }
129
+
130
+ /**
131
+ * Called when the player has just exited PiP mode.
132
+ */
133
+ override fun onExitPictureInPicture() {
134
+ // Explicitly call `exitPictureInPicture()` on PlayerView when exiting PiP state, otherwise
135
+ // the `PictureInPictureExit` event won't get dispatched.
136
+ playerView?.exitPictureInPicture()
137
+ }
138
+
139
+ /**
140
+ * Called when the player's PiP mode changes with a new configuration object.
141
+ */
142
+ override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration?) {
143
+ playerView?.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
144
+ }
145
+
146
+ /**
147
+ * Called whenever the PiP handler needs to compute the PlayerView's global visible rect.
148
+ */
149
+ override fun setSourceRectHint(sourceRectHint: Rect) {
150
+ playerView?.getGlobalVisibleRect(sourceRectHint)
151
+ }
152
+
153
+ /**
154
+ * Called whenever PlayerView's layout changes.
155
+ */
156
+ override fun onLayoutChange(
157
+ view: View?,
158
+ left: Int,
159
+ top: Int,
160
+ right: Int,
161
+ bottom: Int,
162
+ oldLeft: Int,
163
+ oldTop: Int,
164
+ oldRight: Int,
165
+ oldBottom: Int,
166
+ ) {
167
+ if (left != oldLeft || right != oldRight || top != oldTop || bottom != oldBottom) {
168
+ // Update source rect hint whenever the player's layout change
169
+ pictureInPictureHandler?.updateSourceRectHint()
47
170
  }
48
171
  }
49
172
 
@@ -153,6 +276,20 @@ class RNPlayerView(context: ReactApplicationContext) : LinearLayout(context) {
153
276
  emitEvent("seeked", it)
154
277
  }
155
278
 
279
+ /**
280
+ * `onStallStarted` event callback.
281
+ */
282
+ private val onStallStarted: (PlayerEvent.StallStarted) -> Unit = {
283
+ emitEvent("stallStarted", it)
284
+ }
285
+
286
+ /**
287
+ * `onStallEnded` event callback.
288
+ */
289
+ private val onStallEnded: (PlayerEvent.StallEnded) -> Unit = {
290
+ emitEvent("stallEnded", it)
291
+ }
292
+
156
293
  /**
157
294
  * `onTimeChanged` event callback.
158
295
  */
@@ -216,6 +353,104 @@ class RNPlayerView(context: ReactApplicationContext) : LinearLayout(context) {
216
353
  emitEvent("subtitleRemoved", it)
217
354
  }
218
355
 
356
+ /**
357
+ * `onPictureInPictureAvailabilityChanged` event callback.
358
+ */
359
+ private val onPictureInPictureAvailabilityChanged: (PlayerEvent.PictureInPictureAvailabilityChanged) -> Unit = {
360
+ emitEvent("pictureInPictureAvailabilityChanged", it)
361
+ }
362
+
363
+ /**
364
+ * `onPictureInPictureEnter` event callback.
365
+ */
366
+ private val onPictureInPictureEnter: (PlayerEvent.PictureInPictureEnter) -> Unit = {
367
+ emitEvent("pictureInPictureEnter", it)
368
+ }
369
+
370
+ /**
371
+ * `onPictureInPictureExit` event callback.
372
+ */
373
+ private val onPictureInPictureExit: (PlayerEvent.PictureInPictureExit) -> Unit = {
374
+ emitEvent("pictureInPictureExit", it)
375
+ }
376
+
377
+ /**
378
+ * `onAdBreakFinished` event callback.
379
+ */
380
+ private val onAdBreakFinished: (PlayerEvent.AdBreakFinished) -> Unit = {
381
+ emitEvent("adBreakFinished", it)
382
+ }
383
+
384
+ /**
385
+ * `onAdBreakStarted` event callback.
386
+ */
387
+ private val onAdBreakStarted: (PlayerEvent.AdBreakStarted) -> Unit = {
388
+ emitEvent("adBreakStarted", it)
389
+ }
390
+
391
+ /**
392
+ * `onAdClicked` event callback.
393
+ */
394
+ private val onAdClicked: (PlayerEvent.AdClicked) -> Unit = {
395
+ emitEvent("adClicked", it)
396
+ }
397
+
398
+ /**
399
+ * `onAdError` event callback.
400
+ */
401
+ private val onAdError: (PlayerEvent.AdError) -> Unit = {
402
+ emitEvent("adError", it)
403
+ }
404
+
405
+ /**
406
+ * `onAdFinished` event callback.
407
+ */
408
+ private val onAdFinished: (PlayerEvent.AdFinished) -> Unit = {
409
+ emitEvent("adFinished", it)
410
+ }
411
+
412
+ /**
413
+ * `onAdManifestLoad` event callback.
414
+ */
415
+ private val onAdManifestLoad: (PlayerEvent.AdManifestLoad) -> Unit = {
416
+ emitEvent("adManifestLoad", it)
417
+ }
418
+
419
+ /**
420
+ * `onAdManifestLoaded` event callback.
421
+ */
422
+ private val onAdManifestLoaded: (PlayerEvent.AdManifestLoaded) -> Unit = {
423
+ emitEvent("adManifestLoaded", it)
424
+ }
425
+
426
+ /**
427
+ * `onAdQuartile` event callback.
428
+ */
429
+ private val onAdQuartile: (PlayerEvent.AdQuartile) -> Unit = {
430
+ emitEvent("adQuartile", it)
431
+ }
432
+
433
+ /**
434
+ * `onAdScheduled` event callback.
435
+ */
436
+ private val onAdScheduled: (PlayerEvent.AdScheduled) -> Unit = {
437
+ emitEvent("adScheduled", it)
438
+ }
439
+
440
+ /**
441
+ * `onAdSkipped` event callback.
442
+ */
443
+ private val onAdSkipped: (PlayerEvent.AdSkipped) -> Unit = {
444
+ emitEvent("adSkipped", it)
445
+ }
446
+
447
+ /**
448
+ * `onAdStarted` event callback.
449
+ */
450
+ private val onAdStarted: (PlayerEvent.AdStarted) -> Unit = {
451
+ emitEvent("adStarted", it)
452
+ }
453
+
219
454
  /**
220
455
  * Start listening and emitting player events as bubbling events to the js side.
221
456
  */
@@ -234,6 +469,8 @@ class RNPlayerView(context: ReactApplicationContext) : LinearLayout(context) {
234
469
  on(onPlaybackFinished)
235
470
  on(onSeek)
236
471
  on(onSeeked)
472
+ on(onStallStarted)
473
+ on(onStallEnded)
237
474
  on(onTimeChanged)
238
475
  on(onSourceLoad)
239
476
  on(onSourceLoaded)
@@ -243,6 +480,22 @@ class RNPlayerView(context: ReactApplicationContext) : LinearLayout(context) {
243
480
  on(onSubtitleAdded)
244
481
  on(onSubtitleChanged)
245
482
  on(onSubtitleRemoved)
483
+ on(onAdBreakFinished)
484
+ on(onAdBreakStarted)
485
+ on(onAdClicked)
486
+ on(onAdError)
487
+ on(onAdFinished)
488
+ on(onAdManifestLoad)
489
+ on(onAdManifestLoaded)
490
+ on(onAdQuartile)
491
+ on(onAdScheduled)
492
+ on(onAdSkipped)
493
+ on(onAdStarted)
494
+ }
495
+ playerView?.apply {
496
+ on(onPictureInPictureAvailabilityChanged)
497
+ on(onPictureInPictureEnter)
498
+ on(onPictureInPictureExit)
246
499
  }
247
500
  }
248
501
 
@@ -264,6 +517,8 @@ class RNPlayerView(context: ReactApplicationContext) : LinearLayout(context) {
264
517
  off(onPlaybackFinished)
265
518
  off(onSeek)
266
519
  off(onSeeked)
520
+ off(onStallStarted)
521
+ off(onStallEnded)
267
522
  off(onTimeChanged)
268
523
  off(onSourceLoad)
269
524
  off(onSourceLoaded)
@@ -273,6 +528,22 @@ class RNPlayerView(context: ReactApplicationContext) : LinearLayout(context) {
273
528
  off(onSubtitleAdded)
274
529
  off(onSubtitleChanged)
275
530
  off(onSubtitleRemoved)
531
+ off(onAdBreakFinished)
532
+ off(onAdBreakStarted)
533
+ off(onAdClicked)
534
+ off(onAdError)
535
+ off(onAdFinished)
536
+ off(onAdManifestLoad)
537
+ off(onAdManifestLoaded)
538
+ off(onAdQuartile)
539
+ off(onAdScheduled)
540
+ off(onAdSkipped)
541
+ off(onAdStarted)
542
+ }
543
+ playerView?.apply {
544
+ off(onPictureInPictureAvailabilityChanged)
545
+ off(onPictureInPictureEnter)
546
+ off(onPictureInPictureExit)
276
547
  }
277
548
  }
278
549
 
@@ -4,10 +4,15 @@ import android.os.Handler
4
4
  import android.os.Looper
5
5
  import android.view.ViewGroup.LayoutParams
6
6
  import com.bitmovin.player.PlayerView
7
+ import com.bitmovin.player.reactnative.ui.RNPictureInPictureHandler
7
8
  import com.facebook.react.bridge.*
9
+ import com.facebook.react.module.annotations.ReactModule
8
10
  import com.facebook.react.uimanager.SimpleViewManager
9
11
  import com.facebook.react.uimanager.ThemedReactContext
10
12
 
13
+ private const val MODULE_NAME = "NativePlayerView"
14
+
15
+ @ReactModule(name = MODULE_NAME)
11
16
  class RNPlayerViewManager(private val context: ReactApplicationContext) : SimpleViewManager<RNPlayerView>() {
12
17
  /**
13
18
  * Native component functions.
@@ -19,14 +24,30 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
19
24
  /**
20
25
  * Exported module name to JS.
21
26
  */
22
- override fun getName() = "NativePlayerView"
27
+ override fun getName() = MODULE_NAME
23
28
 
24
29
  /**
25
- * The component's native view factory. RN calls this method multiple times
30
+ * React Native PiP handler instance. It can be subclassed, then set from other native
31
+ * modules in case a full-custom implementation is needed. A default implementation is provided
32
+ * out-of-the-box.
33
+ */
34
+ var pictureInPictureHandler = RNPictureInPictureHandler(context)
35
+
36
+ /**
37
+ * The component's native view factory. RN may call this method multiple times
26
38
  * for each component instance.
27
39
  */
28
40
  override fun createViewInstance(reactContext: ThemedReactContext) = RNPlayerView(context)
29
41
 
42
+ /**
43
+ * Called when the component's view gets detached from the view hierarchy. Useful to perform
44
+ * cleanups.
45
+ */
46
+ override fun onDropViewInstance(view: RNPlayerView) {
47
+ super.onDropViewInstance(view)
48
+ view.dispose()
49
+ }
50
+
30
51
  /**
31
52
  * A mapping between a event native identifier and its bubbled version that will
32
53
  * be accessed from React.
@@ -45,6 +66,8 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
45
66
  "playbackFinished" to "onPlaybackFinished",
46
67
  "seek" to "onSeek",
47
68
  "seeked" to "onSeeked",
69
+ "stallStarted" to "onStallStarted",
70
+ "stallEnded" to "onStallEnded",
48
71
  "timeChanged" to "onTimeChanged",
49
72
  "sourceLoad" to "onSourceLoad",
50
73
  "sourceLoaded" to "onSourceLoaded",
@@ -54,6 +77,20 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
54
77
  "subtitleAdded" to "onSubtitleAdded",
55
78
  "subtitleChanged" to "onSubtitleChanged",
56
79
  "subtitleRemoved" to "onSubtitleRemoved",
80
+ "pictureInPictureAvailabilityChanged" to "onPictureInPictureAvailabilityChanged",
81
+ "pictureInPictureEnter" to "onPictureInPictureEnter",
82
+ "pictureInPictureExit" to "onPictureInPictureExit",
83
+ "adBreakFinished" to "onAdBreakFinished",
84
+ "adBreakStarted" to "onAdBreakStarted",
85
+ "adClicked" to "onAdClicked",
86
+ "adError" to "onAdError",
87
+ "adFinished" to "onAdFinished",
88
+ "adManifestLoad" to "onAdManifestLoad",
89
+ "adManifestLoaded" to "onAdManifestLoaded",
90
+ "adQuartile" to "onAdQuartile",
91
+ "adScheduled" to "onAdScheduled",
92
+ "adSkipped" to "onAdSkipped",
93
+ "adStarted" to "onAdStarted",
57
94
  )
58
95
 
59
96
  /**
@@ -88,7 +125,7 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
88
125
  super.receiveCommand(view, commandId, args)
89
126
  commandId?.toInt()?.let {
90
127
  when (it) {
91
- Commands.ATTACH_PLAYER.ordinal -> attachPlayer(view, args?.getString(1))
128
+ Commands.ATTACH_PLAYER.ordinal -> attachPlayer(view, args?.getString(1), args?.getMap(2))
92
129
  else -> {}
93
130
  }
94
131
  }
@@ -99,9 +136,13 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
99
136
  * @param view Target `RNPlayerView`.
100
137
  * @param playerId `Player` instance id inside `PlayerModule`'s registry.
101
138
  */
102
- private fun attachPlayer(view: RNPlayerView, playerId: NativeId?) {
139
+ private fun attachPlayer(view: RNPlayerView, playerId: NativeId?, playerConfig: ReadableMap?) {
103
140
  Handler(Looper.getMainLooper()).post {
104
141
  val player = getPlayerModule()?.getPlayer(playerId)
142
+ playerConfig?.getMap("playbackConfig")?.getBoolean("isPictureInPictureEnabled")?.let {
143
+ pictureInPictureHandler.isPictureInPictureEnabled = it
144
+ view.pictureInPictureHandler = pictureInPictureHandler
145
+ }
105
146
  if (view.playerView != null) {
106
147
  view.player = player
107
148
  } else {
@@ -111,7 +152,6 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
111
152
  LayoutParams.MATCH_PARENT)
112
153
  view.addPlayerView(playerView)
113
154
  }
114
- view.startBubblingEvents()
115
155
  }
116
156
  }
117
157
 
@@ -6,7 +6,9 @@ import com.facebook.react.bridge.*
6
6
  import com.facebook.react.module.annotations.ReactModule
7
7
  import com.facebook.react.uimanager.UIManagerModule
8
8
 
9
- @ReactModule(name = SourceModule.name)
9
+ private const val MODULE_NAME = "SourceModule"
10
+
11
+ @ReactModule(name = MODULE_NAME)
10
12
  class SourceModule(private val context: ReactApplicationContext) : ReactContextBaseJavaModule(context) {
11
13
  /**
12
14
  * In-memory mapping from `nativeId`s to `Source` instances.
@@ -16,10 +18,7 @@ class SourceModule(private val context: ReactApplicationContext) : ReactContextB
16
18
  /**
17
19
  * JS exported module name.
18
20
  */
19
- companion object {
20
- const val name = "SourceModule"
21
- }
22
- override fun getName() = SourceModule.name
21
+ override fun getName() = MODULE_NAME
23
22
 
24
23
  /**
25
24
  * Fetches the `Source` instance associated with `nativeId` from internal sources.
@@ -5,11 +5,13 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule
5
5
  import com.facebook.react.bridge.ReactMethod
6
6
  import java.util.*
7
7
 
8
+ private const val MODULE_NAME = "UuidModule"
9
+
8
10
  class UuidModule(context: ReactApplicationContext) : ReactContextBaseJavaModule(context) {
9
11
  /**
10
12
  * Exported JS module name.
11
13
  */
12
- override fun getName() = "UuidModule"
14
+ override fun getName() = MODULE_NAME
13
15
 
14
16
  /**
15
17
  * Synchronously generate a random UUIDv4.