bitmovin-player-react-native 0.3.1 → 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 (56) hide show
  1. package/README.md +486 -24
  2. package/RNBitmovinPlayer.podspec +5 -1
  3. package/android/build.gradle +8 -5
  4. package/android/src/main/java/com/bitmovin/player/reactnative/AnalyticsModule.kt +154 -0
  5. package/android/src/main/java/com/bitmovin/player/reactnative/DrmModule.kt +4 -5
  6. package/android/src/main/java/com/bitmovin/player/reactnative/PlayerModule.kt +41 -5
  7. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt +318 -2
  8. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt +70 -9
  9. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewPackage.kt +3 -0
  10. package/android/src/main/java/com/bitmovin/player/reactnative/SourceModule.kt +4 -5
  11. package/android/src/main/java/com/bitmovin/player/reactnative/UuidModule.kt +3 -1
  12. package/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt +389 -0
  13. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/Any.kt +27 -0
  14. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReactContextExtension.kt +8 -0
  15. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReadableArray.kt +19 -2
  16. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/String.kt +8 -0
  17. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/WritableMap.kt +19 -0
  18. package/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerBridge.kt +37 -0
  19. package/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerModule.kt +73 -0
  20. package/android/src/main/java/com/bitmovin/player/reactnative/ui/RNPictureInPictureHandler.kt +191 -0
  21. package/ios/AnalyticsModule.m +14 -0
  22. package/ios/AnalyticsModule.swift +180 -0
  23. package/ios/AudioSessionModule.m +10 -0
  24. package/ios/AudioSessionModule.swift +65 -0
  25. package/ios/Event+JSON.swift +134 -0
  26. package/ios/FullscreenHandlerBridge.swift +33 -0
  27. package/ios/FullscreenHandlerModule.m +9 -0
  28. package/ios/FullscreenHandlerModule.swift +71 -0
  29. package/ios/PlayerModule.m +6 -0
  30. package/ios/PlayerModule.swift +44 -0
  31. package/ios/RCTConvert+BitmovinPlayer.swift +382 -0
  32. package/ios/RNPlayerView+PlayerListener.swift +56 -0
  33. package/ios/RNPlayerView+UserInterfaceListener.swift +35 -0
  34. package/ios/RNPlayerView.swift +22 -0
  35. package/ios/RNPlayerViewManager.m +24 -1
  36. package/ios/RNPlayerViewManager.swift +23 -1
  37. package/lib/index.d.ts +1013 -150
  38. package/lib/index.js +251 -48
  39. package/lib/index.mjs +231 -34
  40. package/package.json +1 -1
  41. package/src/advertising.ts +155 -0
  42. package/src/analytics/collector.ts +97 -0
  43. package/src/analytics/config.ts +218 -0
  44. package/src/analytics/index.ts +2 -0
  45. package/src/audioSession.ts +47 -0
  46. package/src/components/PlayerView/events.ts +49 -3
  47. package/src/components/PlayerView/index.tsx +68 -11
  48. package/src/components/PlayerView/native.ts +4 -1
  49. package/src/events.ts +255 -0
  50. package/src/index.ts +4 -0
  51. package/src/media.ts +33 -0
  52. package/src/player.ts +57 -1
  53. package/src/source.ts +4 -0
  54. package/src/styleConfig.ts +87 -0
  55. package/src/ui/fullscreenhandler.ts +19 -0
  56. package/src/ui/fullscreenhandlerbridge.ts +59 -0
@@ -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
+ }
@@ -1,7 +1,6 @@
1
1
  package com.bitmovin.player.reactnative.extensions
2
2
 
3
- import com.facebook.react.bridge.ReadableArray
4
- import com.facebook.react.bridge.ReadableMap
3
+ import com.facebook.react.bridge.*
5
4
 
6
5
  inline fun <reified T> ReadableArray.toList(): List<T?> = (0 until size()).map { i ->
7
6
  getDynamic(i).let {
@@ -12,7 +11,25 @@ inline fun <reified T> ReadableArray.toList(): List<T?> = (0 until size()).map {
12
11
  Int::class -> it.asInt() as T
13
12
  ReadableArray::class -> it.asArray() as T
14
13
  ReadableMap::class -> it.asMap() as T
14
+ WritableArray::class -> it.asArray() as T
15
+ WritableMap::class -> it.asMap() as T
15
16
  else -> null
16
17
  }
17
18
  }
18
19
  }
20
+
21
+ inline fun <reified T> List<T>.toReadableArray(): ReadableArray = Arguments.createArray().apply {
22
+ forEach {
23
+ when (T::class) {
24
+ Boolean::class -> pushBoolean(it as Boolean)
25
+ String::class -> pushString(it as String)
26
+ Double::class -> pushDouble(it as Double)
27
+ Int::class -> pushInt(it as Int)
28
+ ReadableArray::class -> pushArray(it as ReadableArray)
29
+ ReadableMap::class -> pushMap(it as ReadableMap)
30
+ WritableArray::class -> pushArray(it as ReadableArray)
31
+ WritableMap::class -> pushMap(it as ReadableMap)
32
+ else -> pushNull()
33
+ }
34
+ }
35
+ }
@@ -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,19 @@
1
+ package com.bitmovin.player.reactnative.extensions
2
+
3
+ import com.facebook.react.bridge.WritableMap
4
+
5
+ fun WritableMap.putInt(key: String, i: Int?) {
6
+ if (i == null) {
7
+ putNull(key)
8
+ } else {
9
+ putInt(key, i)
10
+ }
11
+ }
12
+
13
+ fun WritableMap.putDouble(key: String, d: Double?) {
14
+ if (d == null) {
15
+ putNull(key)
16
+ } else {
17
+ putDouble(key, d)
18
+ }
19
+ }
@@ -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,191 @@
1
+ package com.bitmovin.player.reactnative.ui
2
+
3
+ import android.app.PictureInPictureParams
4
+ import android.content.pm.PackageManager
5
+ import android.content.res.Configuration
6
+ import android.graphics.Rect
7
+ import android.os.Build
8
+ import android.util.Rational
9
+ import androidx.annotation.RequiresApi
10
+ import androidx.appcompat.app.AppCompatActivity
11
+ import com.bitmovin.player.api.ui.PictureInPictureHandler
12
+ import com.facebook.react.bridge.ReactApplicationContext
13
+
14
+ /**
15
+ * Delegate object for `RNPictureInPictureHandler`. It delegates all view logic that needs
16
+ * to be performed during each PiP state to this object.
17
+ */
18
+ interface RNPictureInPictureDelegate {
19
+ /**
20
+ * Called whenever the handler's `isInPictureInPictureMode` changes to `true`.
21
+ */
22
+ fun onExitPictureInPicture()
23
+ /**
24
+ * Called whenever the handler's `isInPictureInPictureMode` changes to `false`.
25
+ */
26
+ fun onEnterPictureInPicture()
27
+ /**
28
+ * Called whenever the activity's PiP mode state changes with the new resources configuration.
29
+ */
30
+ fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration?)
31
+ /**
32
+ * Called whenever the handler needs to compute a new `sourceRectHint` for PiP params.
33
+ * The passed rect reference is expected to be fulfilled with the PlayerView's global visible
34
+ * rect.
35
+ */
36
+ fun setSourceRectHint(sourceRectHint: Rect)
37
+ }
38
+
39
+ /**
40
+ * Custom PictureInPictureHandler` concrete implementation designed for React Native. It relies on
41
+ * React Native's application context to manage the application's PiP state. Can be subclassed in
42
+ * order to provide custom PiP capabilities.
43
+ */
44
+ open class RNPictureInPictureHandler(val context: ReactApplicationContext): PictureInPictureHandler {
45
+ /**
46
+ * PiP delegate object that contains the view logic to be performed on each PiP state change.
47
+ */
48
+ private var delegate: RNPictureInPictureDelegate? = null
49
+
50
+ /**
51
+ * Whether the user has enabled PiP support via `isPictureInPictureEnabled` playback configuration in JS.
52
+ */
53
+ var isPictureInPictureEnabled = false
54
+
55
+ /**
56
+ * Whether this view is currently in PiP mode.
57
+ */
58
+ var isInPictureInPictureMode = false
59
+
60
+ /**
61
+ * Whether the current Android version supports PiP mode.
62
+ */
63
+ private val isPictureInPictureSupported: Boolean
64
+ get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
65
+ && context.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
66
+
67
+ /**
68
+ * Whether the picture in picture feature is available and enabled.
69
+ */
70
+ override val isPictureInPictureAvailable: Boolean
71
+ get() = isPictureInPictureEnabled && isPictureInPictureSupported
72
+
73
+ /**
74
+ * Whether this view is currently in PiP mode. Required for PictureInPictureHandler interface.
75
+ */
76
+ override val isPictureInPicture: Boolean
77
+ get() = isInPictureInPictureMode
78
+
79
+ /**
80
+ * Current React activity computed property.
81
+ */
82
+ private val currentActivity: AppCompatActivity?
83
+ get() {
84
+ if (context.hasCurrentActivity()) {
85
+ return context.currentActivity as AppCompatActivity
86
+ }
87
+ return null
88
+ }
89
+
90
+ /**
91
+ * Sets the new delegate object and update the activity's PiP parameters accordingly.
92
+ */
93
+ open fun setDelegate(delegate: RNPictureInPictureDelegate?) {
94
+ this.delegate = delegate
95
+ // Update the activity's PiP params once the delegate has been set.
96
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isPictureInPictureAvailable) {
97
+ applyPictureInPictureParams()
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Called whenever bitmovin's `PlayerView` needs to enter PiP mode.
103
+ */
104
+ override fun enterPictureInPicture() {
105
+ if (isPictureInPictureAvailable) {
106
+ currentActivity?.let {
107
+ it.supportActionBar?.hide()
108
+ it.enterPictureInPictureMode()
109
+ }
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Called whenever bitmovin's `PlayerView` needs to exit PiP mode.
115
+ */
116
+ override fun exitPictureInPicture() {
117
+ if (isPictureInPictureAvailable) {
118
+ currentActivity?.supportActionBar?.show()
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Called whenever the activity content resources have changed.
124
+ */
125
+ open fun onConfigurationChanged(newConfig: Configuration?) {
126
+ // PiP mode is supported since Android 7.0
127
+ if (isPictureInPictureAvailable) {
128
+ handlePictureInPictureModeChanges(newConfig)
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Checks whether the current activity `isInPictureInPictureMode` has changed since the last lifecycle
134
+ * configuration change.
135
+ */
136
+ @RequiresApi(Build.VERSION_CODES.N)
137
+ private fun handlePictureInPictureModeChanges(newConfig: Configuration?) = currentActivity?.let {
138
+ if (isInPictureInPictureMode != it.isInPictureInPictureMode) {
139
+ delegate?.onPictureInPictureModeChanged(it.isInPictureInPictureMode, newConfig)
140
+ if (it.isInPictureInPictureMode) {
141
+ delegate?.onEnterPictureInPicture()
142
+ } else {
143
+ delegate?.onExitPictureInPicture()
144
+ }
145
+ isInPictureInPictureMode = it.isInPictureInPictureMode
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Applies Android recommended PiP params on the current activity for smoother transitions.
151
+ *
152
+ * You can read more about the recommended settings for PiP here:
153
+ * - https://developer.android.com/develop/ui/views/picture-in-picture#smoother-transition
154
+ * - https://developer.android.com/develop/ui/views/picture-in-picture#smoother-exit
155
+ */
156
+ @RequiresApi(Build.VERSION_CODES.O)
157
+ private fun applyPictureInPictureParams() = currentActivity?.let {
158
+ // See also: https://developer.android.com/develop/ui/views/picture-in-picture#smoother-transition
159
+ val sourceRectHint = Rect()
160
+ delegate?.setSourceRectHint(sourceRectHint)
161
+ val ratio = Rational(16, 9)
162
+ val params = PictureInPictureParams.Builder()
163
+ .setAspectRatio(ratio)
164
+ .setSourceRectHint(sourceRectHint)
165
+ when {
166
+ // See also: https://developer.android.com/develop/ui/views/picture-in-picture#smoother-exit
167
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ->
168
+ params.setAutoEnterEnabled(true).setSeamlessResizeEnabled(true)
169
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ->
170
+ params.setExpandedAspectRatio(ratio)
171
+ }
172
+ it.setPictureInPictureParams(params.build())
173
+ }
174
+
175
+ /**
176
+ * Update source rect hint on activity's PiP params.
177
+ */
178
+ open fun updateSourceRectHint() {
179
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !isPictureInPictureAvailable) {
180
+ return
181
+ }
182
+ currentActivity?.let {
183
+ val sourceRectHint = Rect()
184
+ delegate?.setSourceRectHint(sourceRectHint)
185
+ it.setPictureInPictureParams(
186
+ PictureInPictureParams.Builder()
187
+ .setSourceRectHint(sourceRectHint)
188
+ .build())
189
+ }
190
+ }
191
+ }
@@ -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
+ }
@@ -0,0 +1,10 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ @interface RCT_EXTERN_REMAP_MODULE(AudioSessionModule, AudioSessionModule, NSObject)
4
+
5
+ RCT_EXTERN_METHOD(
6
+ setCategory:(NSString *)category
7
+ resolver:(RCTPromiseResolveBlock)resolve
8
+ rejecter:(RCTPromiseRejectBlock)reject)
9
+
10
+ @end
@@ -0,0 +1,65 @@
1
+ import AVFAudio
2
+
3
+ @objc(AudioSessionModule)
4
+ class AudioSessionModule: NSObject, RCTBridgeModule {
5
+ // Run this module methods on main thread.
6
+ var methodQueue: DispatchQueue! {
7
+ DispatchQueue.main
8
+ }
9
+
10
+ /// JS module name.
11
+ static func moduleName() -> String! {
12
+ "AudioSessionModule"
13
+ }
14
+
15
+ // Requires module initialization.
16
+ static func requiresMainQueueSetup() -> Bool {
17
+ true
18
+ }
19
+
20
+ /**
21
+ Sets the audio session’s category.
22
+ - Parameter category: Category string.
23
+ - Parameter resolver: JS promise resolver block.
24
+ - Parameter rejecter: JS promise rejecter block.
25
+ */
26
+ @objc func setCategory(
27
+ _ category: String,
28
+ resolver resolve: @escaping RCTPromiseResolveBlock,
29
+ rejecter reject: @escaping RCTPromiseRejectBlock
30
+ ) {
31
+ if let category = parseCategory(category) {
32
+ do {
33
+ try AVAudioSession.sharedInstance().setCategory(category)
34
+ resolve(nil)
35
+ } catch {
36
+ reject("\((error as NSError).code)", error.localizedDescription, error as NSError)
37
+ }
38
+ } else {
39
+ let error = RCTErrorWithMessage("Unknown audio session category: \(category)") as NSError
40
+ reject("\(error.code)", error.localizedDescription, error)
41
+ }
42
+ }
43
+
44
+ /**
45
+ Parse any category string to an `AVAudioSession.Category` type.
46
+ */
47
+ private func parseCategory(_ category: String) -> AVAudioSession.Category? {
48
+ switch (category) {
49
+ case "ambient":
50
+ return .ambient
51
+ case "multiRoute":
52
+ return .multiRoute
53
+ case "playAndRecord":
54
+ return .playAndRecord
55
+ case "playback":
56
+ return .playback
57
+ case "record":
58
+ return .record
59
+ case "soloAmbient":
60
+ return .soloAmbient
61
+ default:
62
+ return nil
63
+ }
64
+ }
65
+ }