bitmovin-player-react-native 0.4.0 → 0.5.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 (43) hide show
  1. package/README.md +249 -1
  2. package/RNBitmovinPlayer.podspec +3 -1
  3. package/android/build.gradle +3 -2
  4. package/android/src/main/java/com/bitmovin/player/reactnative/AnalyticsModule.kt +154 -0
  5. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt +45 -0
  6. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt +25 -4
  7. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewPackage.kt +3 -0
  8. package/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt +172 -0
  9. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/Any.kt +27 -0
  10. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReactContextExtension.kt +8 -0
  11. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/String.kt +8 -0
  12. package/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerBridge.kt +37 -0
  13. package/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerModule.kt +73 -0
  14. package/ios/AnalyticsModule.m +14 -0
  15. package/ios/AnalyticsModule.swift +180 -0
  16. package/ios/Event+JSON.swift +11 -0
  17. package/ios/FullscreenHandlerBridge.swift +33 -0
  18. package/ios/FullscreenHandlerModule.m +9 -0
  19. package/ios/FullscreenHandlerModule.swift +71 -0
  20. package/ios/RCTConvert+BitmovinPlayer.swift +174 -0
  21. package/ios/RNPlayerView+PlayerListener.swift +5 -1
  22. package/ios/RNPlayerView+UserInterfaceListener.swift +16 -0
  23. package/ios/RNPlayerView.swift +5 -0
  24. package/ios/RNPlayerViewManager.m +6 -0
  25. package/ios/RNPlayerViewManager.swift +21 -0
  26. package/lib/index.d.ts +498 -51
  27. package/lib/index.js +186 -42
  28. package/lib/index.mjs +167 -26
  29. package/package.json +1 -1
  30. package/src/analytics/collector.ts +97 -0
  31. package/src/analytics/config.ts +218 -0
  32. package/src/analytics/index.ts +2 -0
  33. package/src/components/PlayerView/events.ts +10 -0
  34. package/src/components/PlayerView/index.tsx +38 -1
  35. package/src/components/PlayerView/native.ts +4 -1
  36. package/src/events.ts +43 -0
  37. package/src/index.ts +2 -0
  38. package/src/media.ts +33 -0
  39. package/src/player.ts +21 -0
  40. package/src/source.ts +4 -0
  41. package/src/styleConfig.ts +87 -0
  42. package/src/ui/fullscreenhandler.ts +19 -0
  43. package/src/ui/fullscreenhandlerbridge.ts +59 -0
@@ -1,5 +1,7 @@
1
1
  package com.bitmovin.player.reactnative.converter
2
2
 
3
+ import com.bitmovin.analytics.BitmovinAnalyticsConfig
4
+ import com.bitmovin.analytics.data.CustomData
3
5
  import com.bitmovin.player.api.DeviceDescription.DeviceName
4
6
  import com.bitmovin.player.api.DeviceDescription.ModelName
5
7
  import com.bitmovin.player.api.PlaybackConfig
@@ -11,14 +13,20 @@ import com.bitmovin.player.api.event.PlayerEvent
11
13
  import com.bitmovin.player.api.event.SourceEvent
12
14
  import com.bitmovin.player.api.event.data.SeekPosition
13
15
  import com.bitmovin.player.api.media.subtitle.SubtitleTrack
16
+ import com.bitmovin.player.api.media.thumbnail.ThumbnailTrack
17
+ import com.bitmovin.player.api.media.video.quality.VideoQuality
14
18
  import com.bitmovin.player.api.source.Source
15
19
  import com.bitmovin.player.api.source.SourceConfig
16
20
  import com.bitmovin.player.api.source.SourceType
21
+ import com.bitmovin.player.api.ui.ScalingMode
22
+ import com.bitmovin.player.api.ui.StyleConfig
17
23
  import com.bitmovin.player.reactnative.extensions.getName
18
24
  import com.bitmovin.player.reactnative.extensions.putInt
19
25
  import com.bitmovin.player.reactnative.extensions.putDouble
20
26
  import com.bitmovin.player.reactnative.extensions.toList
21
27
  import com.bitmovin.player.reactnative.extensions.toReadableArray
28
+ import com.bitmovin.player.reactnative.extensions.getProperty
29
+ import com.bitmovin.player.reactnative.extensions.setProperty
22
30
  import com.facebook.react.bridge.*
23
31
  import java.util.UUID
24
32
 
@@ -45,6 +53,11 @@ class JsonConverter {
45
53
  playerConfig.playbackConfig = it
46
54
  }
47
55
  }
56
+ if (json.hasKey("styleConfig")) {
57
+ toStyleConfig(json.getMap("styleConfig"))?.let {
58
+ playerConfig.styleConfig = it
59
+ }
60
+ }
48
61
  if (json.hasKey("tweaksConfig")) {
49
62
  toTweaksConfig(json.getMap("tweaksConfig"))?.let {
50
63
  playerConfig.tweaksConfig = it
@@ -81,6 +94,47 @@ class JsonConverter {
81
94
  return playbackConfig
82
95
  }
83
96
 
97
+ /**
98
+ * Converts any JS object into a `StyleConfig` object.
99
+ * @param json JS object representing the `StyleConfig`.
100
+ * @return The generated `StyleConfig` if successful, `null` otherwise.
101
+ */
102
+ @JvmStatic
103
+ fun toStyleConfig(json: ReadableMap?): StyleConfig? {
104
+ if (json == null) {
105
+ return null
106
+ }
107
+ val styleConfig = StyleConfig()
108
+ if (json.hasKey("isUiEnabled")) {
109
+ styleConfig.isUiEnabled = json.getBoolean("isUiEnabled")
110
+ }
111
+ if (json.hasKey("playerUiCss")) {
112
+ val playerUiCss = json.getString("playerUiCss")
113
+ if (!playerUiCss.isNullOrEmpty()) {
114
+ styleConfig.playerUiCss = playerUiCss
115
+ }
116
+ }
117
+ if (json.hasKey("supplementalPlayerUiCss")) {
118
+ val supplementalPlayerUiCss = json.getString("supplementalPlayerUiCss")
119
+ if (!supplementalPlayerUiCss.isNullOrEmpty()) {
120
+ styleConfig.supplementalPlayerUiCss = supplementalPlayerUiCss
121
+ }
122
+ }
123
+ if (json.hasKey("playerUiJs")) {
124
+ val playerUiJs = json.getString("playerUiJs")
125
+ if (!playerUiJs.isNullOrEmpty()) {
126
+ styleConfig.playerUiJs = playerUiJs
127
+ }
128
+ }
129
+ if (json.hasKey("scalingMode")) {
130
+ val scalingMode = json.getString("scalingMode")
131
+ if (!scalingMode.isNullOrEmpty()) {
132
+ styleConfig.scalingMode = ScalingMode.valueOf(scalingMode)
133
+ }
134
+ }
135
+ return styleConfig
136
+ }
137
+
84
138
  /**
85
139
  * Converts any JS object into a `TweaksConfig` object.
86
140
  * @param json JS object representing the `TweaksConfig`.
@@ -207,6 +261,9 @@ class JsonConverter {
207
261
  }
208
262
  }
209
263
  }
264
+ if (json.hasKey("thumbnailTrack")) {
265
+ config.thumbnailTrack = toThumbnailTrack(json.getString("thumbnailTrack"))
266
+ }
210
267
  return config
211
268
  }
212
269
 
@@ -376,6 +433,10 @@ class JsonConverter {
376
433
  json.putDouble("skipOffset", event.skipOffset)
377
434
  json.putDouble("timeOffset", event.timeOffset)
378
435
  }
436
+ if (event is PlayerEvent.VideoPlaybackQualityChanged) {
437
+ json.putMap("newVideoQuality", fromVideoQuality(event.newVideoQuality))
438
+ json.putMap("oldVideoQuality", fromVideoQuality(event.oldVideoQuality))
439
+ }
379
440
  return json
380
441
  }
381
442
 
@@ -393,6 +454,19 @@ class JsonConverter {
393
454
  widevineConfig
394
455
  }
395
456
 
457
+ /**
458
+ * Converts an `url` string into a `ThumbnailsTrack`.
459
+ * @param url JS object representing the `ThumbnailsTrack`.
460
+ * @return The generated `ThumbnailsTrack` if successful, `null` otherwise.
461
+ */
462
+ @JvmStatic
463
+ fun toThumbnailTrack(url: String?): ThumbnailTrack? {
464
+ if (url == null) {
465
+ return null
466
+ }
467
+ return ThumbnailTrack(url);
468
+ }
469
+
396
470
  /**
397
471
  * Converts an arbitrary `json` into a `SubtitleTrack`.
398
472
  * @param json JS object representing the `SubtitleTrack`.
@@ -595,5 +669,103 @@ class JsonConverter {
595
669
  AdQuartile.ThirdQuartile -> "third"
596
670
  else -> null
597
671
  }
672
+
673
+ /**
674
+ * Converts an arbitrary json object into a `BitmovinAnalyticsConfig`.
675
+ * @param json JS object representing the `BitmovinAnalyticsConfig`.
676
+ * @return The produced `BitmovinAnalyticsConfig` or null.
677
+ */
678
+ @JvmStatic
679
+ fun toAnalyticsConfig(json: ReadableMap?): BitmovinAnalyticsConfig? = json?.let {
680
+ var config: BitmovinAnalyticsConfig? = null
681
+ it.getString("key")?.let { key ->
682
+ config = it.getString("playerKey")
683
+ ?.let { playerKey -> BitmovinAnalyticsConfig(key, playerKey) }
684
+ ?: BitmovinAnalyticsConfig(key)
685
+ }
686
+ it.getString("cdnProvider")?.let { cdnProvider ->
687
+ config?.cdnProvider = cdnProvider
688
+ }
689
+ it.getString("customUserId")?.let { customUserId ->
690
+ config?.customUserId = customUserId
691
+ }
692
+ it.getString("experimentName")?.let { experimentName ->
693
+ config?.experimentName = experimentName
694
+ }
695
+ it.getString("videoId")?.let { videoId ->
696
+ config?.videoId = videoId
697
+ }
698
+ it.getString("title")?.let { title ->
699
+ config?.title = title
700
+ }
701
+ it.getString("path")?.let { path ->
702
+ config?.path = path
703
+ }
704
+ if (it.hasKey("isLive")) {
705
+ config?.isLive = it.getBoolean("isLive")
706
+ }
707
+ if (it.hasKey("ads")) {
708
+ config?.ads = it.getBoolean("ads")
709
+ }
710
+ if (it.hasKey("randomizeUserId")) {
711
+ config?.randomizeUserId = it.getBoolean("randomizeUserId")
712
+ }
713
+ for (n in 1..30) {
714
+ it.getString("customData${n}")?.let { customDataN ->
715
+ config?.setProperty("customData${n}", customDataN)
716
+ }
717
+ }
718
+ config
719
+ }
720
+
721
+ /**
722
+ * Converts an arbitrary json object into an analytics `CustomData`.
723
+ * @param json JS object representing the `CustomData`.
724
+ * @return The produced `CustomData` or null.
725
+ */
726
+ @JvmStatic
727
+ fun toAnalyticsCustomData(json: ReadableMap?): CustomData? = json?.let {
728
+ val customData = CustomData()
729
+ for (n in 1..30) {
730
+ it.getString("customData${n}")?.let { customDataN ->
731
+ customData.setProperty("customData${n}", customDataN)
732
+ }
733
+ }
734
+ customData
735
+ }
736
+
737
+ /**
738
+ * Converts an arbitrary analytics `CustomData` object into a JS value.
739
+ * @param customData `CustomData` to be converted.
740
+ * @return The produced JS value or null.
741
+ */
742
+ @JvmStatic
743
+ fun fromAnalyticsCustomData(customData: CustomData?): ReadableMap? = customData?.let {
744
+ val json = Arguments.createMap()
745
+ for (n in 1..30) {
746
+ it.getProperty<String>("customData${n}")?.let { customDataN ->
747
+ json.putString("customData${n}", customDataN)
748
+ }
749
+ }
750
+ json
751
+ }
752
+
753
+ /**
754
+ * Converts any `VideoQuality` value into its json representation.
755
+ * @param videoQuality `VideoQuality` value.
756
+ * @return The produced JS string.
757
+ */
758
+ @JvmStatic
759
+ fun fromVideoQuality(videoQuality: VideoQuality?): WritableMap? = videoQuality?.let {
760
+ Arguments.createMap().apply {
761
+ putString("id", videoQuality.id)
762
+ putString("label", videoQuality.label)
763
+ putInt("bitrate", videoQuality.bitrate)
764
+ putString("codec", videoQuality.codec)
765
+ putDouble("frameRate", videoQuality.frameRate.toDouble())
766
+ putInt("height", videoQuality.height)
767
+ putInt("width", videoQuality.width)
768
+ }
769
+ }
598
770
  }
599
771
  }
@@ -0,0 +1,27 @@
1
+ package com.bitmovin.player.reactnative.extensions
2
+
3
+ /**
4
+ * Reflection helper for dynamically getting a property by name from a java object.
5
+ * @param propertyName Property name.
6
+ * @return A mutable property reference that can be used to get/set the prop's value.
7
+ */
8
+ @Suppress("UNCHECKED_CAST")
9
+ inline fun <reified T> Any?.getProperty(propertyName: String): T? = this?.let {
10
+ val getter = it::class.java.methods.firstOrNull { method ->
11
+ method.name == "get${propertyName.capitalized()}"
12
+ }
13
+ getter?.invoke(it) as? T
14
+ }
15
+
16
+ /**
17
+ * Reflection helper for dynamically setting a property value by name to a java object.
18
+ * @param propertyName Property name.
19
+ * @param value Value that will be set for the specified `propertyName`.
20
+ */
21
+ @Suppress("UNCHECKED_CAST")
22
+ inline fun <reified T> Any?.setProperty(propertyName: String, value: T) = this?.let {
23
+ val setter = it::class.java.methods.firstOrNull { method ->
24
+ method.name == "set${propertyName.capitalized()}"
25
+ }
26
+ setter?.invoke(it, value)
27
+ }
@@ -0,0 +1,8 @@
1
+ package com.bitmovin.player.reactnative.extensions
2
+
3
+ import com.facebook.react.bridge.ReactContext
4
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
5
+
6
+ inline fun <reified T : ReactContextBaseJavaModule> ReactContext.getModule(): T? {
7
+ return getNativeModule(T::class.java)
8
+ }
@@ -0,0 +1,8 @@
1
+ package com.bitmovin.player.reactnative.extensions
2
+
3
+ /**
4
+ * Returns a copy of this string with its first letter capitalized.
5
+ */
6
+ fun String.capitalized(): String = this.replaceFirstChar {
7
+ it.uppercase()
8
+ }
@@ -0,0 +1,37 @@
1
+ package com.bitmovin.player.reactnative.ui
2
+
3
+ import com.bitmovin.player.api.ui.FullscreenHandler
4
+ import com.bitmovin.player.reactnative.NativeId
5
+ import com.bitmovin.player.reactnative.extensions.getModule
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+
8
+ class FullscreenHandlerBridge(
9
+ val context: ReactApplicationContext,
10
+ private val nativeId: NativeId
11
+ ) : FullscreenHandler {
12
+ override var isFullscreen = false
13
+
14
+ override fun onDestroy() {
15
+ // Do nothing
16
+ }
17
+
18
+ override fun onFullscreenExitRequested() {
19
+ context
20
+ .getModule<FullscreenHandlerModule>()
21
+ ?.requestExitFullscreen(nativeId)
22
+ }
23
+
24
+ override fun onFullscreenRequested() {
25
+ context
26
+ .getModule<FullscreenHandlerModule>()
27
+ ?.requestEnterFullscreen(nativeId)
28
+ }
29
+
30
+ override fun onPause() {
31
+ // Do nothing
32
+ }
33
+
34
+ override fun onResume() {
35
+ // Do nothing
36
+ }
37
+ }
@@ -0,0 +1,73 @@
1
+ package com.bitmovin.player.reactnative.ui
2
+
3
+ import com.bitmovin.player.reactnative.NativeId
4
+ import com.bitmovin.player.reactnative.Registry
5
+ import com.facebook.react.bridge.*
6
+ import com.facebook.react.module.annotations.ReactModule
7
+ import java.util.concurrent.locks.ReentrantLock
8
+ import kotlin.concurrent.withLock
9
+
10
+ private const val MODULE_NAME = "FullscreenHandlerModule"
11
+
12
+ @ReactModule(name = MODULE_NAME)
13
+ class FullscreenHandlerModule(private val context: ReactApplicationContext) : ReactContextBaseJavaModule(context) {
14
+ override fun getName() = MODULE_NAME
15
+
16
+ /**
17
+ * In-memory mapping from `nativeId`s to `FullscreenHandler` instances.
18
+ */
19
+ private val fullscreenHandler: Registry<FullscreenHandlerBridge> = mutableMapOf()
20
+
21
+ /**
22
+ * Module's local lock object used to sync calls between Kotlin and JS.
23
+ */
24
+ private val lock = ReentrantLock()
25
+
26
+ /**
27
+ * Lock condition used to sync operations on the fullscreen handler.
28
+ */
29
+ private val fullscreenChangedCondition = lock.newCondition()
30
+
31
+ fun getInstance(nativeId: NativeId?): FullscreenHandlerBridge? = fullscreenHandler[nativeId]
32
+
33
+ fun requestEnterFullscreen(nativeId: NativeId) {
34
+ context.catalystInstance.callFunction(
35
+ "FullscreenBridge-${nativeId}",
36
+ "enterFullscreen",
37
+ Arguments.createArray() as NativeArray
38
+ )
39
+ lock.withLock {
40
+ fullscreenChangedCondition.await()
41
+ }
42
+ }
43
+
44
+ fun requestExitFullscreen(nativeId: NativeId) {
45
+ context.catalystInstance.callFunction(
46
+ "FullscreenBridge-${nativeId}",
47
+ "exitFullscreen",
48
+ Arguments.createArray() as NativeArray
49
+ )
50
+ lock.withLock {
51
+ fullscreenChangedCondition.await()
52
+ }
53
+ }
54
+
55
+ @ReactMethod(isBlockingSynchronousMethod = true)
56
+ fun onFullscreenChanged(nativeId: NativeId, isFullscreenEnabled: Boolean) {
57
+ fullscreenHandler[nativeId]?.isFullscreen = isFullscreenEnabled
58
+ lock.withLock {
59
+ fullscreenChangedCondition.signal()
60
+ }
61
+ }
62
+
63
+ @ReactMethod
64
+ fun registerHandler(nativeId: NativeId) {
65
+ val fullscreenHandler = fullscreenHandler[nativeId] ?: FullscreenHandlerBridge(context, nativeId)
66
+ this.fullscreenHandler[nativeId] = fullscreenHandler
67
+ }
68
+
69
+ @ReactMethod
70
+ fun destroy(nativeId: NativeId) {
71
+ fullscreenHandler.remove(nativeId)
72
+ }
73
+ }
@@ -0,0 +1,14 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ @interface RCT_EXTERN_REMAP_MODULE(AnalyticsModule, AnalyticsModule, NSObject)
4
+
5
+ RCT_EXTERN_METHOD(initWithConfig:(NSString *)nativeId config:(nullable id)config)
6
+ RCT_EXTERN_METHOD(destroy:(NSString *)nativeId)
7
+ RCT_EXTERN_METHOD(attach:(NSString *)nativeId playerId:(NSString *)playerId)
8
+ RCT_EXTERN_METHOD(detach:(NSString *)nativeId)
9
+ RCT_EXTERN_METHOD(setCustomDataOnce:(NSString *)nativeId json:(nullable id)json)
10
+ RCT_EXTERN_METHOD(setCustomData:(NSString *)nativeId json:(nullable id)json)
11
+ RCT_EXTERN_METHOD(getCustomData:(NSString *)nativeId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
12
+ RCT_EXTERN_METHOD(getUserId:(NSString *)nativeId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
13
+
14
+ @end
@@ -0,0 +1,180 @@
1
+ import BitmovinPlayer
2
+ import BitmovinAnalyticsCollector
3
+
4
+ @objc(AnalyticsModule)
5
+ class AnalyticsModule: NSObject, RCTBridgeModule {
6
+ /// React bridge reference.
7
+ @objc var bridge: RCTBridge!
8
+
9
+ /// PlayerModule instance fetched from the bridge's registry
10
+ @objc var playerModule: PlayerModule? {
11
+ bridge.module(for: PlayerModule.self) as? PlayerModule
12
+ }
13
+
14
+ /// In-memory mapping from `nativeId`s to `BitmovinPlayerCollector` instances.
15
+ private var collectors: Registry<BitmovinPlayerCollector> = [:]
16
+
17
+ /// JS module name.
18
+ static func moduleName() -> String! {
19
+ "AnalyticsModule"
20
+ }
21
+
22
+ /// Module requires main thread initialization.
23
+ static func requiresMainQueueSetup() -> Bool {
24
+ true
25
+ }
26
+
27
+ /// Use `UIManager.addBlock` to enqueue module methods on UI thread.
28
+ var methodQueue: DispatchQueue! {
29
+ bridge.uiManager.methodQueue
30
+ }
31
+
32
+ /**
33
+ Retrieves a `BitmovinPlayerCollector` instance from the internal registry for the given `nativeId`.
34
+ - Parameter nativeId: Native Id of the collector instance.
35
+ - Returns: Collector instance associated with the `nativeId` or `nil`.
36
+ */
37
+ @objc func retrieve(_ nativeId: NativeId) -> BitmovinPlayerCollector? {
38
+ collectors[nativeId]
39
+ }
40
+
41
+ /**
42
+ Creates a new `BitmovinPlayerCollector` instance inside the internal registry using the provided `config` object.
43
+ - Parameter nativeId: ID to associate with the `BitmovinPlayerCollector` instance.
44
+ - Parameter config: `BitmovinAnalyticsConfig` object received from JS.
45
+ */
46
+ @objc(initWithConfig:config:)
47
+ func initWithConfig(_ nativeId: NativeId, config: Any?) {
48
+ bridge.uiManager.addUIBlock { [weak self] _, _ in
49
+ guard let analyticsConfig = RCTConvert.analyticsConfig(config) else {
50
+ return
51
+ }
52
+ self?.collectors[nativeId] = BitmovinPlayerCollector(config: analyticsConfig)
53
+ }
54
+ }
55
+
56
+ /**
57
+ Detaches and removes the given `BitmovinPlayerCollector` from the internal registry.
58
+ - Parameter nativeId: Native Id of the collector instance.
59
+ */
60
+ @objc(destroy:)
61
+ func destroy(_ nativeId: NativeId) {
62
+ bridge.uiManager.addUIBlock { [weak self] _, _ in
63
+ self?.collectors[nativeId]?.detachPlayer()
64
+ self?.collectors[nativeId] = nil
65
+ }
66
+ }
67
+
68
+ /**
69
+ Attaches a `BitmovinPlayerCollector` to the `Player` instance with native Id equal to `playerId`.
70
+ - Parameter nativeId: Native Id of the collector instance.
71
+ - Parameter playerId: Native Id of the player instance.
72
+ */
73
+ @objc(attach:playerId:)
74
+ func attach(_ nativeId: NativeId, playerId: NativeId) {
75
+ bridge.uiManager.addUIBlock { [weak self] _, _ in
76
+ guard
77
+ let collector = self?.collectors[nativeId],
78
+ let player = self?.playerModule?.retrieve(playerId)
79
+ else {
80
+ return
81
+ }
82
+ collector.attachPlayer(player: player)
83
+ }
84
+ }
85
+
86
+ /**
87
+ Detaches the player object from a `BitmovinPlayerCollector` instance.
88
+ - Parameter nativeId: Native Id of the collector instance.
89
+ */
90
+ @objc(detach:)
91
+ func detach(_ nativeId: NativeId) {
92
+ bridge.uiManager.addUIBlock { [weak self] _, _ in
93
+ guard let collector = self?.collectors[nativeId] else {
94
+ return
95
+ }
96
+ collector.detachPlayer()
97
+ }
98
+ }
99
+
100
+ /**
101
+ Updates the custom data config for a `BitmovinPlayerCollector` instance.
102
+ - Parameter nativeId: Native Id of the collector instance.
103
+ - Parameter json: Custom data config json.
104
+ */
105
+ @objc(setCustomDataOnce:json:)
106
+ func setCustomDataOnce(_ nativeId: NativeId, json: Any?) {
107
+ bridge.uiManager.addUIBlock { [weak self] _, _ in
108
+ guard
109
+ let collector = self?.collectors[nativeId],
110
+ let customData = RCTConvert.analyticsCustomData(json)
111
+ else {
112
+ return
113
+ }
114
+ collector.setCustomDataOnce(customData: customData)
115
+ }
116
+ }
117
+
118
+ /**
119
+ Sets the custom data config for a `BitmovinPlayerCollector` instance.
120
+ - Parameter nativeId: Native Id of the collector instance.
121
+ - Parameter json: Custom data config json.
122
+ */
123
+ @objc(setCustomData:json:)
124
+ func setCustomData(_ nativeId: NativeId, json: Any?) {
125
+ bridge.uiManager.addUIBlock { [weak self] _, _ in
126
+ guard
127
+ let collector = self?.collectors[nativeId],
128
+ let customData = RCTConvert.analyticsCustomData(json)
129
+ else {
130
+ return
131
+ }
132
+ collector.setCustomData(customData: customData)
133
+ }
134
+ }
135
+
136
+ /**
137
+ Gets the current custom data config for a `BitmovinPlayerCollector` instance.
138
+ - Parameter nativeId: Native Id of the the collector instance.
139
+ - Parameter resolver: JS promise resolver.
140
+ - Parameter rejecter: JS promise rejecter.
141
+ */
142
+ @objc(getCustomData:resolver:rejecter:)
143
+ func getCustomData(
144
+ _ nativeId: NativeId,
145
+ resolver resolve: @escaping RCTPromiseResolveBlock,
146
+ rejecter reject: @escaping RCTPromiseRejectBlock
147
+ ) {
148
+ bridge.uiManager.addUIBlock { [weak self] _, _ in
149
+ guard
150
+ let collector = self?.collectors[nativeId],
151
+ let customData = RCTConvert.toJson(analyticsCustomData: collector.getCustomData())
152
+ else {
153
+ reject("[AnalyticsModule]", "Could not find analytics collector with ID (\(nativeId))", nil)
154
+ return
155
+ }
156
+ resolve(customData)
157
+ }
158
+ }
159
+
160
+ /**
161
+ Gets the current user Id for a `BitmovinPlayerCollector` instance.
162
+ - Parameter nativeId: Native Id of the the collector instance.
163
+ - Parameter resolver: JS promise resolver.
164
+ - Parameter rejecter: JS promise rejecter.
165
+ */
166
+ @objc(getUserId:resolver:rejecter:)
167
+ func getUserId(
168
+ _ nativeId: NativeId,
169
+ resolver resolve: @escaping RCTPromiseResolveBlock,
170
+ rejecter reject: @escaping RCTPromiseRejectBlock
171
+ ) {
172
+ bridge.uiManager.addUIBlock { [weak self] _, _ in
173
+ guard let collector = self?.collectors[nativeId] else {
174
+ reject("[AnalyticsModule]", "Could not find analytics collector with ID (\(nativeId))", nil)
175
+ return
176
+ }
177
+ resolve(collector.getUserId())
178
+ }
179
+ }
180
+ }
@@ -284,3 +284,14 @@ extension AdStartedEvent {
284
284
  ]
285
285
  }
286
286
  }
287
+
288
+ extension VideoDownloadQualityChangedEvent {
289
+ func toJSON() -> [AnyHashable: Any] {
290
+ [
291
+ "newVideoQuality": RCTConvert.toJson(videoQuality: videoQualityNew),
292
+ "oldVideoQuality": RCTConvert.toJson(videoQuality: videoQualityOld),
293
+ "name": name,
294
+ "timestamp": timestamp
295
+ ]
296
+ }
297
+ }
@@ -0,0 +1,33 @@
1
+ import BitmovinPlayer
2
+
3
+ class FullscreenHandlerBridge: NSObject, FullscreenHandler {
4
+ var isFullscreen = false
5
+
6
+ private let nativeId: NativeId
7
+ private let bridge: RCTBridge
8
+
9
+ init(_ nativeId: NativeId, bridge: RCTBridge) {
10
+ self.nativeId = nativeId
11
+ self.bridge = bridge
12
+ super.init()
13
+ }
14
+
15
+ func onFullscreenRequested() {
16
+ guard let fullscreenHandlerModule = getFullscreenHandlerModule() else {
17
+ return
18
+ }
19
+ fullscreenHandlerModule.onFullscreenRequested(nativeId: nativeId)
20
+ }
21
+
22
+ func onFullscreenExitRequested() {
23
+ guard let fullscreenHandlerModule = getFullscreenHandlerModule() else {
24
+ return
25
+ }
26
+ fullscreenHandlerModule.onFullscreenExitRequested(nativeId: nativeId)
27
+ }
28
+
29
+ /// Fetches the initialized `FullscreenHandlerModule` instance on RN's bridge object.
30
+ private func getFullscreenHandlerModule() -> FullscreenHandlerModule? {
31
+ bridge.module(for: FullscreenHandlerModule.self) as? FullscreenHandlerModule
32
+ }
33
+ }
@@ -0,0 +1,9 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ @interface RCT_EXTERN_REMAP_MODULE(FullscreenHandlerModule, FullscreenHandlerModule, NSObject)
4
+
5
+ RCT_EXTERN_METHOD(destroy:(NSString *)nativeId)
6
+
7
+ RCT_EXTERN__BLOCKING_SYNCHRONOUS_METHOD(onFullscreenChanged:(NSString *)nativeId isFullscreenEnabled:(BOOL)isFullscreenEnabled)
8
+ RCT_EXTERN_METHOD(registerHandler:(NSString *)nativeId)
9
+ @end