bitmovin-player-react-native 0.7.2 → 0.9.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.
- package/README.md +2 -17
- package/RNBitmovinPlayer.podspec +3 -5
- package/android/build.gradle +2 -2
- package/android/gradlew +8 -4
- package/android/src/main/java/com/bitmovin/player/reactnative/AnalyticsModule.kt +12 -2
- package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt +10 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt +39 -20
- package/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt +28 -0
- package/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerModule.kt +5 -0
- package/ios/AnalyticsModule.m +3 -2
- package/ios/AnalyticsModule.swift +46 -7
- package/ios/FullscreenHandlerModule.m +1 -0
- package/ios/FullscreenHandlerModule.swift +5 -0
- package/ios/RCTConvert+BitmovinPlayer.swift +141 -18
- package/ios/RNPlayerViewManager.m +1 -0
- package/ios/RNPlayerViewManager.swift +22 -0
- package/lib/index.d.ts +92 -11
- package/lib/index.js +51 -4
- package/lib/index.mjs +51 -5
- package/package.json +4 -2
- package/src/analytics/collector.ts +25 -3
- package/src/analytics/config.ts +32 -4
- package/src/components/PlayerView/index.tsx +44 -4
- package/src/styleConfig.ts +39 -5
- package/src/ui/fullscreenhandlerbridge.ts +14 -0
package/README.md
CHANGED
|
@@ -11,11 +11,6 @@ Official React Native bindings for Bitmovin's mobile Player SDKs.
|
|
|
11
11
|
[](https://www.npmjs.com/package/bitmovin-player-react-native)
|
|
12
12
|
|
|
13
13
|
> As the library is under active development, this means certain features from our native SDKs are not yet exposed through these React Native bindings.
|
|
14
|
-
> See [Feature Support](#feature-support) for an overview of the supported features.
|
|
15
|
-
>
|
|
16
|
-
> Not seeing the features you’re looking for?
|
|
17
|
-
> We are accepting community pull requests to this open-source project so please feel free to contribute.
|
|
18
|
-
> or let us know in [our community](https://community.bitmovin.com/c/requests/14) what features we should work on next.
|
|
19
14
|
|
|
20
15
|
- [Bitmovin Player React Native](#bitmovin-player-react-native)
|
|
21
16
|
- [Platform Support](#platform-support)
|
|
@@ -33,24 +28,14 @@ This library requires at least React Native 0.64+ and React 17+ to work properly
|
|
|
33
28
|
- iOS 14.0+
|
|
34
29
|
- tvOS 14.0+
|
|
35
30
|
- Android API Level 21+
|
|
36
|
-
- Android TV API Level
|
|
31
|
+
- Android TV API Level 24+
|
|
37
32
|
- Fire TV FireOS 5.0+
|
|
38
33
|
|
|
39
34
|
Please note that browsers and other browser-like environments such as webOS and Tizen are not supported. For more details regarding Bitmovin Player SDK platform and device support, please refer to the [Supported Platforms & Devices](https://developer.bitmovin.com/playback/docs/supported-platforms-devices-player) page of our documentation.
|
|
40
35
|
|
|
41
36
|
## Feature Support
|
|
42
37
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
| Feature | State |
|
|
46
|
-
| -------------------------------- | ----------------------------------------- |
|
|
47
|
-
| Playback of DRM-protected assets | :white_check_mark: Available since v0.2.0 |
|
|
48
|
-
| Subtitles & Captions | :white_check_mark: Available since v0.2.0 |
|
|
49
|
-
| Advertising | :white_check_mark: Available since v0.4.0 |
|
|
50
|
-
| Analytics | :white_check_mark: Available since v0.5.0 |
|
|
51
|
-
| Playlist API | :x: Not available |
|
|
52
|
-
| Casting | :x: Not available |
|
|
53
|
-
| Offline Playback | :x: Not available |
|
|
38
|
+
Please refer to the [Features](https://developer.bitmovin.com/playback/docs/react-native-introduction#features) section of our documentation for an up-to-date list of supported Player features.
|
|
54
39
|
|
|
55
40
|
## Documentation
|
|
56
41
|
|
package/RNBitmovinPlayer.podspec
CHANGED
|
@@ -19,9 +19,7 @@ Pod::Spec.new do |s|
|
|
|
19
19
|
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
20
20
|
|
|
21
21
|
s.dependency "React-Core"
|
|
22
|
-
s.dependency "BitmovinPlayer", "3.
|
|
23
|
-
s.dependency "
|
|
24
|
-
s.dependency "
|
|
25
|
-
s.ios.dependency "GoogleAds-IMA-iOS-SDK", "3.17.0"
|
|
26
|
-
s.tvos.dependency "GoogleAds-IMA-tvOS-SDK", "4.6.1"
|
|
22
|
+
s.dependency "BitmovinPlayer", "3.42.0"
|
|
23
|
+
s.ios.dependency "GoogleAds-IMA-iOS-SDK", "3.18.4"
|
|
24
|
+
s.tvos.dependency "GoogleAds-IMA-tvOS-SDK", "4.8.2"
|
|
27
25
|
end
|
package/android/build.gradle
CHANGED
|
@@ -51,10 +51,10 @@ android {
|
|
|
51
51
|
|
|
52
52
|
dependencies {
|
|
53
53
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
|
|
54
|
-
implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.
|
|
54
|
+
implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.29.0'
|
|
55
55
|
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
|
|
56
56
|
implementation 'com.bitmovin.analytics:collector-bitmovin-player:2.12.1'
|
|
57
|
-
implementation 'com.bitmovin.player:player:3.
|
|
57
|
+
implementation 'com.bitmovin.player:player:3.40.0'
|
|
58
58
|
//noinspection GradleDynamicVersion
|
|
59
59
|
implementation 'com.facebook.react:react-native:+' // From node_modules
|
|
60
60
|
}
|
package/android/gradlew
CHANGED
|
@@ -85,9 +85,6 @@ done
|
|
|
85
85
|
APP_BASE_NAME=${0##*/}
|
|
86
86
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
|
87
87
|
|
|
88
|
-
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
89
|
-
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|
90
|
-
|
|
91
88
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
|
92
89
|
MAX_FD=maximum
|
|
93
90
|
|
|
@@ -133,10 +130,13 @@ location of your Java installation."
|
|
|
133
130
|
fi
|
|
134
131
|
else
|
|
135
132
|
JAVACMD=java
|
|
136
|
-
|
|
133
|
+
if ! command -v java >/dev/null 2>&1
|
|
134
|
+
then
|
|
135
|
+
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
137
136
|
|
|
138
137
|
Please set the JAVA_HOME variable in your environment to match the
|
|
139
138
|
location of your Java installation."
|
|
139
|
+
fi
|
|
140
140
|
fi
|
|
141
141
|
|
|
142
142
|
# Increase the maximum file descriptors if we can.
|
|
@@ -197,6 +197,10 @@ if "$cygwin" || "$msys" ; then
|
|
|
197
197
|
done
|
|
198
198
|
fi
|
|
199
199
|
|
|
200
|
+
|
|
201
|
+
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
202
|
+
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|
203
|
+
|
|
200
204
|
# Collect all arguments for the java command;
|
|
201
205
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
|
202
206
|
# shell script including quotes and variable substitutions, so put them in
|
|
@@ -104,7 +104,7 @@ class AnalyticsModule(private val context: ReactApplicationContext) : ReactConte
|
|
|
104
104
|
* @param json Custom data config json.
|
|
105
105
|
*/
|
|
106
106
|
@ReactMethod
|
|
107
|
-
fun setCustomData(nativeId: NativeId, json: ReadableMap?) {
|
|
107
|
+
fun setCustomData(nativeId: NativeId, playerId: NativeId?, json: ReadableMap?) {
|
|
108
108
|
uiManager()?.addUIBlock { _ ->
|
|
109
109
|
JsonConverter.toAnalyticsCustomData(json)?.let {
|
|
110
110
|
collectors[nativeId]?.customData = it
|
|
@@ -118,7 +118,7 @@ class AnalyticsModule(private val context: ReactApplicationContext) : ReactConte
|
|
|
118
118
|
* @param promise JS promise object.
|
|
119
119
|
*/
|
|
120
120
|
@ReactMethod
|
|
121
|
-
fun getCustomData(nativeId: NativeId, promise: Promise) {
|
|
121
|
+
fun getCustomData(nativeId: NativeId, playerId: NativeId?, promise: Promise) {
|
|
122
122
|
uiManager()?.addUIBlock { _ ->
|
|
123
123
|
collectors[nativeId]?.let {
|
|
124
124
|
promise.resolve(JsonConverter.fromAnalyticsCustomData(it.customData))
|
|
@@ -126,6 +126,16 @@ class AnalyticsModule(private val context: ReactApplicationContext) : ReactConte
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
@ReactMethod
|
|
130
|
+
fun addSourceMetadata(nativeId: NativeId, playerId: NativeId?, json: ReadableMap?) {
|
|
131
|
+
uiManager()?.addUIBlock { _ ->
|
|
132
|
+
val playerSource = playerModule()?.getPlayer(playerId)?.source ?: return@addUIBlock
|
|
133
|
+
JsonConverter.toAnalyticsSourceMetadata(json)?.let { sourceMetadata ->
|
|
134
|
+
collectors[nativeId]?.addSourceMetadata(playerSource, sourceMetadata)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
129
139
|
/**
|
|
130
140
|
* Gets the current user Id for a `BitmovinPlayerCollector` instance.
|
|
131
141
|
* @param nativeId Native Id of the the collector instance.
|
|
@@ -82,6 +82,16 @@ private val EVENT_CLASS_TO_REACT_NATIVE_NAME_MAPPING_UI = mapOf<KClass<out Event
|
|
|
82
82
|
@SuppressLint("ViewConstructor")
|
|
83
83
|
class RNPlayerView(val context: ReactApplicationContext) : LinearLayout(context),
|
|
84
84
|
LifecycleEventListener, View.OnLayoutChangeListener, RNPictureInPictureDelegate {
|
|
85
|
+
|
|
86
|
+
init {
|
|
87
|
+
// React Native has a bug that dynamically added views sometimes aren't laid out again properly.
|
|
88
|
+
// Since we dynamically add and remove SurfaceView under the hood this caused the player
|
|
89
|
+
// to suddenly not show the video anymore because SurfaceView was not laid out properly.
|
|
90
|
+
// Bitmovin player issue: https://github.com/bitmovin/bitmovin-player-react-native/issues/180
|
|
91
|
+
// React Native layout issue: https://github.com/facebook/react-native/issues/17968
|
|
92
|
+
getViewTreeObserver().addOnGlobalLayoutListener { requestLayout() }
|
|
93
|
+
}
|
|
94
|
+
|
|
85
95
|
/**
|
|
86
96
|
* Relays the provided set of events, emitted by the player, together with the associated name
|
|
87
97
|
* to the `eventOutput` callback.
|
|
@@ -24,10 +24,11 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
24
24
|
/**
|
|
25
25
|
* Native component functions.
|
|
26
26
|
*/
|
|
27
|
-
enum class Commands {
|
|
28
|
-
ATTACH_PLAYER,
|
|
29
|
-
ATTACH_FULLSCREEN_BRIDGE,
|
|
30
|
-
SET_CUSTOM_MESSAGE_HANDLER_BRIDGE_ID,
|
|
27
|
+
enum class Commands(val command: String) {
|
|
28
|
+
ATTACH_PLAYER("attachPlayer"),
|
|
29
|
+
ATTACH_FULLSCREEN_BRIDGE("attachFullscreenBridge"),
|
|
30
|
+
SET_CUSTOM_MESSAGE_HANDLER_BRIDGE_ID("setCustomMessageHandlerBridgeId"),
|
|
31
|
+
SET_FULLSCREEN("setFullscreen");
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
/**
|
|
@@ -132,11 +133,9 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
132
133
|
* to call 'functions' on them.
|
|
133
134
|
* @return map between names (used in js) and command ids (used in native code).
|
|
134
135
|
*/
|
|
135
|
-
override fun getCommandsMap():
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
"setCustomMessageHandlerBridgeId" to Commands.SET_CUSTOM_MESSAGE_HANDLER_BRIDGE_ID.ordinal,
|
|
139
|
-
)
|
|
136
|
+
override fun getCommandsMap(): Map<String, Int> = Commands.values().associate {
|
|
137
|
+
it.command to it.ordinal
|
|
138
|
+
}
|
|
140
139
|
|
|
141
140
|
/**
|
|
142
141
|
* Callback triggered in response to command dispatches from the js side.
|
|
@@ -145,19 +144,23 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
145
144
|
* @param args Arguments list sent from the js side.
|
|
146
145
|
*/
|
|
147
146
|
override fun receiveCommand(view: RNPlayerView, commandId: String?, args: ReadableArray?) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
147
|
+
val command = commandId?.toInt()?.toCommand() ?: throw IllegalArgumentException(
|
|
148
|
+
"The received command is not supported by the Bitmovin Player View"
|
|
149
|
+
)
|
|
150
|
+
when (command) {
|
|
151
|
+
Commands.ATTACH_PLAYER -> attachPlayer(view, args?.getString(1), args?.getMap(2))
|
|
152
|
+
Commands.ATTACH_FULLSCREEN_BRIDGE -> args?.getString(1)?.let { fullscreenBridgeId ->
|
|
153
|
+
attachFullscreenBridge(view, fullscreenBridgeId)
|
|
154
|
+
}
|
|
155
|
+
Commands.SET_CUSTOM_MESSAGE_HANDLER_BRIDGE_ID -> {
|
|
156
|
+
args?.getString(1)?.let { customMessageHandlerBridgeId ->
|
|
157
|
+
setCustomMessageHandlerBridgeId(view, customMessageHandlerBridgeId)
|
|
154
158
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
+
}
|
|
160
|
+
Commands.SET_FULLSCREEN -> {
|
|
161
|
+
args?.getBoolean(1)?.let { isFullscreen ->
|
|
162
|
+
setFullscreen(view, isFullscreen)
|
|
159
163
|
}
|
|
160
|
-
else -> {}
|
|
161
164
|
}
|
|
162
165
|
}
|
|
163
166
|
}
|
|
@@ -170,6 +173,20 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
170
173
|
}
|
|
171
174
|
}
|
|
172
175
|
|
|
176
|
+
private fun setFullscreen(view: RNPlayerView, isFullscreen: Boolean) {
|
|
177
|
+
if (view.playerView?.isFullscreen == isFullscreen) return
|
|
178
|
+
|
|
179
|
+
Handler(Looper.getMainLooper()).post {
|
|
180
|
+
with(view.playerView ?: return@post) {
|
|
181
|
+
if (isFullscreen) {
|
|
182
|
+
enterFullscreen()
|
|
183
|
+
} else {
|
|
184
|
+
exitFullscreen()
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
173
190
|
private fun setCustomMessageHandlerBridgeId(view: RNPlayerView, customMessageHandlerBridgeId: NativeId) {
|
|
174
191
|
this.customMessageHandlerBridgeId = customMessageHandlerBridgeId
|
|
175
192
|
attachCustomMessageHandlerBridge(view)
|
|
@@ -223,3 +240,5 @@ class RNPlayerViewManager(private val context: ReactApplicationContext) : Simple
|
|
|
223
240
|
*/
|
|
224
241
|
private fun getPlayerModule(): PlayerModule? = context.getModule()
|
|
225
242
|
}
|
|
243
|
+
|
|
244
|
+
private fun Int.toCommand(): RNPlayerViewManager.Commands? = RNPlayerViewManager.Commands.values().getOrNull(this)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package com.bitmovin.player.reactnative.converter
|
|
2
2
|
|
|
3
3
|
import com.bitmovin.analytics.BitmovinAnalyticsConfig
|
|
4
|
+
import com.bitmovin.analytics.config.SourceMetadata
|
|
4
5
|
import com.bitmovin.analytics.data.CustomData
|
|
5
6
|
import com.bitmovin.player.api.DeviceDescription.DeviceName
|
|
6
7
|
import com.bitmovin.player.api.DeviceDescription.ModelName
|
|
@@ -784,6 +785,9 @@ class JsonConverter {
|
|
|
784
785
|
customData.setProperty("customData${n}", customDataN)
|
|
785
786
|
}
|
|
786
787
|
}
|
|
788
|
+
it.getString("experimentName")?.let { experimentName ->
|
|
789
|
+
customData.experimentName = experimentName
|
|
790
|
+
}
|
|
787
791
|
customData
|
|
788
792
|
}
|
|
789
793
|
|
|
@@ -800,9 +804,33 @@ class JsonConverter {
|
|
|
800
804
|
json.putString("customData${n}", customDataN)
|
|
801
805
|
}
|
|
802
806
|
}
|
|
807
|
+
it.experimentName?.let { experimentName ->
|
|
808
|
+
json.putString("experimentName", experimentName)
|
|
809
|
+
}
|
|
803
810
|
json
|
|
804
811
|
}
|
|
805
812
|
|
|
813
|
+
@JvmStatic
|
|
814
|
+
fun toAnalyticsSourceMetadata(json: ReadableMap?): SourceMetadata? = json?.let {
|
|
815
|
+
val sourceMetadata = SourceMetadata(
|
|
816
|
+
title = it.getString("title"),
|
|
817
|
+
videoId = it.getString("videoId"),
|
|
818
|
+
cdnProvider = it.getString("cdnProvider"),
|
|
819
|
+
path = it.getString("path"),
|
|
820
|
+
isLive = it.getBoolean("isLive")
|
|
821
|
+
)
|
|
822
|
+
|
|
823
|
+
for (n in 1..30) {
|
|
824
|
+
it.getString("customData${n}")?.let { customDataN ->
|
|
825
|
+
sourceMetadata.setProperty("customData${n}", customDataN)
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
it.getString("experimentName")?.let { experimentName ->
|
|
829
|
+
sourceMetadata.experimentName = experimentName
|
|
830
|
+
}
|
|
831
|
+
sourceMetadata
|
|
832
|
+
}
|
|
833
|
+
|
|
806
834
|
/**
|
|
807
835
|
* Converts any `VideoQuality` value into its json representation.
|
|
808
836
|
* @param videoQuality `VideoQuality` value.
|
|
@@ -66,6 +66,11 @@ class FullscreenHandlerModule(private val context: ReactApplicationContext) : Re
|
|
|
66
66
|
this.fullscreenHandler[nativeId] = fullscreenHandler
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
@ReactMethod
|
|
70
|
+
fun setIsFullscreenActive(nativeId: NativeId, isFullscreenActive: Boolean) {
|
|
71
|
+
fullscreenHandler[nativeId]?.isFullscreen = isFullscreenActive
|
|
72
|
+
}
|
|
73
|
+
|
|
69
74
|
@ReactMethod
|
|
70
75
|
fun destroy(nativeId: NativeId) {
|
|
71
76
|
fullscreenHandler.remove(nativeId)
|
package/ios/AnalyticsModule.m
CHANGED
|
@@ -7,8 +7,9 @@ RCT_EXTERN_METHOD(destroy:(NSString *)nativeId)
|
|
|
7
7
|
RCT_EXTERN_METHOD(attach:(NSString *)nativeId playerId:(NSString *)playerId)
|
|
8
8
|
RCT_EXTERN_METHOD(detach:(NSString *)nativeId)
|
|
9
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)
|
|
10
|
+
RCT_EXTERN_METHOD(setCustomData:(NSString *)nativeId playerId:(nullable NSString *)playerId json:(nullable id)json)
|
|
11
|
+
RCT_EXTERN_METHOD(getCustomData:(NSString *)nativeId playerId:(nullable NSString *)playerId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
12
12
|
RCT_EXTERN_METHOD(getUserId:(NSString *)nativeId resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
|
|
13
|
+
RCT_EXTERN_METHOD(addSourceMetadata:(NSString *)nativeId playerId:(nullable NSString *)playerId json:(nullable id)json)
|
|
13
14
|
|
|
14
15
|
@end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import BitmovinPlayer
|
|
2
|
-
import
|
|
2
|
+
import BitmovinCollector
|
|
3
3
|
|
|
4
4
|
@objc(AnalyticsModule)
|
|
5
5
|
class AnalyticsModule: NSObject, RCTBridgeModule {
|
|
@@ -118,37 +118,50 @@ class AnalyticsModule: NSObject, RCTBridgeModule {
|
|
|
118
118
|
/**
|
|
119
119
|
Sets the custom data config for a `BitmovinPlayerCollector` instance.
|
|
120
120
|
- Parameter nativeId: Native Id of the collector instance.
|
|
121
|
+
- Parameter playerId: Native Id of the player instance.
|
|
121
122
|
- Parameter json: Custom data config json.
|
|
122
123
|
*/
|
|
123
|
-
@objc(setCustomData:json:)
|
|
124
|
-
func setCustomData(
|
|
124
|
+
@objc(setCustomData:playerId:json:)
|
|
125
|
+
func setCustomData(
|
|
126
|
+
_ nativeId: NativeId,
|
|
127
|
+
playerId: NativeId?,
|
|
128
|
+
json: Any?
|
|
129
|
+
) {
|
|
125
130
|
bridge.uiManager.addUIBlock { [weak self] _, _ in
|
|
126
131
|
guard
|
|
127
132
|
let collector = self?.collectors[nativeId],
|
|
128
|
-
let customData = RCTConvert.analyticsCustomData(json)
|
|
133
|
+
let customData = RCTConvert.analyticsCustomData(json),
|
|
134
|
+
let playerId = playerId,
|
|
135
|
+
let player = self?.bridge[PlayerModule.self]?.retrieve(playerId),
|
|
136
|
+
let source = player.source
|
|
129
137
|
else {
|
|
130
138
|
return
|
|
131
139
|
}
|
|
132
|
-
collector.
|
|
140
|
+
collector.apply(customData: customData, for: source)
|
|
133
141
|
}
|
|
134
142
|
}
|
|
135
143
|
|
|
136
144
|
/**
|
|
137
145
|
Gets the current custom data config for a `BitmovinPlayerCollector` instance.
|
|
138
146
|
- Parameter nativeId: Native Id of the the collector instance.
|
|
147
|
+
- Parameter playerId: Native Id of the player instance.
|
|
139
148
|
- Parameter resolver: JS promise resolver.
|
|
140
149
|
- Parameter rejecter: JS promise rejecter.
|
|
141
150
|
*/
|
|
142
|
-
@objc(getCustomData:resolver:rejecter:)
|
|
151
|
+
@objc(getCustomData:playerId:resolver:rejecter:)
|
|
143
152
|
func getCustomData(
|
|
144
153
|
_ nativeId: NativeId,
|
|
154
|
+
playerId: NativeId?,
|
|
145
155
|
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
146
156
|
rejecter reject: @escaping RCTPromiseRejectBlock
|
|
147
157
|
) {
|
|
148
158
|
bridge.uiManager.addUIBlock { [weak self] _, _ in
|
|
149
159
|
guard
|
|
150
160
|
let collector = self?.collectors[nativeId],
|
|
151
|
-
let
|
|
161
|
+
let playerId = playerId,
|
|
162
|
+
let player = self?.bridge[PlayerModule.self]?.retrieve(playerId),
|
|
163
|
+
let source = player.source,
|
|
164
|
+
let customData = RCTConvert.toJson(analyticsCustomData: collector.customData(for: source))
|
|
152
165
|
else {
|
|
153
166
|
reject("[AnalyticsModule]", "Could not find analytics collector with ID (\(nativeId))", nil)
|
|
154
167
|
return
|
|
@@ -177,4 +190,30 @@ class AnalyticsModule: NSObject, RCTBridgeModule {
|
|
|
177
190
|
resolve(collector.getUserId())
|
|
178
191
|
}
|
|
179
192
|
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
Applies the source metadata for the current source via the `BitmovinPlayerCollector` instance.
|
|
196
|
+
- Parameter nativeId: Native Id of the collector instance.
|
|
197
|
+
- Parameter playerId: Native Id of the player instance.
|
|
198
|
+
- Parameter json: Custom data config json.
|
|
199
|
+
*/
|
|
200
|
+
@objc(addSourceMetadata:playerId:json:)
|
|
201
|
+
func addSourceMetadata(
|
|
202
|
+
_ nativeId: NativeId,
|
|
203
|
+
playerId: NativeId?,
|
|
204
|
+
json: Any?
|
|
205
|
+
) {
|
|
206
|
+
bridge.uiManager.addUIBlock { [weak self] _, _ in
|
|
207
|
+
guard
|
|
208
|
+
let collector = self?.collectors[nativeId],
|
|
209
|
+
let sourceMetadata = RCTConvert.analyticsSourceMetadata(json),
|
|
210
|
+
let playerId = playerId,
|
|
211
|
+
let player = self?.bridge[PlayerModule.self]?.retrieve(playerId),
|
|
212
|
+
let source = player.source
|
|
213
|
+
else {
|
|
214
|
+
return
|
|
215
|
+
}
|
|
216
|
+
collector.apply(sourceMetadata: sourceMetadata, for: source)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
180
219
|
}
|
|
@@ -6,4 +6,5 @@ RCT_EXTERN_METHOD(destroy:(NSString *)nativeId)
|
|
|
6
6
|
|
|
7
7
|
RCT_EXTERN__BLOCKING_SYNCHRONOUS_METHOD(onFullscreenChanged:(NSString *)nativeId isFullscreenEnabled:(BOOL)isFullscreenEnabled)
|
|
8
8
|
RCT_EXTERN_METHOD(registerHandler:(NSString *)nativeId)
|
|
9
|
+
RCT_EXTERN_METHOD(setIsFullscreenActive:(NSString *)nativeId isFullscreen:(BOOL)isFullscreen)
|
|
9
10
|
@end
|
|
@@ -57,6 +57,11 @@ class FullscreenHandlerModule: NSObject, RCTBridgeModule {
|
|
|
57
57
|
fullscreenHandlers[nativeId] = FullscreenHandlerBridge(nativeId, bridge: bridge)
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
@objc(setIsFullscreenActive:isFullscreen:)
|
|
61
|
+
func setIsFullscreenActive(_ nativeId: NativeId, isFullscreen: Bool) {
|
|
62
|
+
fullscreenHandlers[nativeId]?.isFullscreen = isFullscreen
|
|
63
|
+
}
|
|
64
|
+
|
|
60
65
|
func onFullscreenRequested(nativeId: NativeId) {
|
|
61
66
|
fullscreenChangeDispatchGroup.enter()
|
|
62
67
|
bridge.enqueueJSCall("FullscreenBridge-\(nativeId)", method: "enterFullscreen", args: []) {}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import BitmovinPlayer
|
|
3
|
-
import
|
|
3
|
+
import BitmovinCollector
|
|
4
4
|
|
|
5
5
|
extension RCTConvert {
|
|
6
6
|
/**
|
|
@@ -72,6 +72,9 @@ extension RCTConvert {
|
|
|
72
72
|
if let isUiEnabled = json["isUiEnabled"] as? Bool {
|
|
73
73
|
styleConfig.isUiEnabled = isUiEnabled
|
|
74
74
|
}
|
|
75
|
+
if let userInterfaceType = userInterfaceType(json["userInterfaceType"]) {
|
|
76
|
+
styleConfig.userInterfaceType = userInterfaceType
|
|
77
|
+
}
|
|
75
78
|
#if !os(tvOS)
|
|
76
79
|
if let playerUiCss = json["playerUiCss"] as? String {
|
|
77
80
|
styleConfig.playerUiCss = RCTConvert.nsurl(playerUiCss)
|
|
@@ -614,11 +617,37 @@ extension RCTConvert {
|
|
|
614
617
|
if let randomizeUserId = json["randomizeUserId"] as? Bool {
|
|
615
618
|
config.randomizeUserId = randomizeUserId
|
|
616
619
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
620
|
+
config.customData1 = json["customData1"] as? String
|
|
621
|
+
config.customData2 = json["customData2"] as? String
|
|
622
|
+
config.customData3 = json["customData3"] as? String
|
|
623
|
+
config.customData4 = json["customData4"] as? String
|
|
624
|
+
config.customData5 = json["customData5"] as? String
|
|
625
|
+
config.customData6 = json["customData6"] as? String
|
|
626
|
+
config.customData7 = json["customData7"] as? String
|
|
627
|
+
config.customData8 = json["customData8"] as? String
|
|
628
|
+
config.customData9 = json["customData9"] as? String
|
|
629
|
+
config.customData10 = json["customData10"] as? String
|
|
630
|
+
config.customData11 = json["customData11"] as? String
|
|
631
|
+
config.customData12 = json["customData12"] as? String
|
|
632
|
+
config.customData13 = json["customData13"] as? String
|
|
633
|
+
config.customData14 = json["customData14"] as? String
|
|
634
|
+
config.customData15 = json["customData15"] as? String
|
|
635
|
+
config.customData16 = json["customData16"] as? String
|
|
636
|
+
config.customData17 = json["customData17"] as? String
|
|
637
|
+
config.customData18 = json["customData18"] as? String
|
|
638
|
+
config.customData19 = json["customData19"] as? String
|
|
639
|
+
config.customData20 = json["customData20"] as? String
|
|
640
|
+
config.customData21 = json["customData21"] as? String
|
|
641
|
+
config.customData22 = json["customData22"] as? String
|
|
642
|
+
config.customData23 = json["customData23"] as? String
|
|
643
|
+
config.customData24 = json["customData24"] as? String
|
|
644
|
+
config.customData25 = json["customData25"] as? String
|
|
645
|
+
config.customData26 = json["customData26"] as? String
|
|
646
|
+
config.customData27 = json["customData27"] as? String
|
|
647
|
+
config.customData28 = json["customData28"] as? String
|
|
648
|
+
config.customData29 = json["customData29"] as? String
|
|
649
|
+
config.customData30 = json["customData30"] as? String
|
|
650
|
+
config.experimentName = json["experimentName"] as? String
|
|
622
651
|
return config
|
|
623
652
|
}
|
|
624
653
|
|
|
@@ -631,13 +660,39 @@ extension RCTConvert {
|
|
|
631
660
|
guard let json = json as? [String: Any?] else {
|
|
632
661
|
return nil
|
|
633
662
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
663
|
+
return CustomData(
|
|
664
|
+
customData1: json["customData1"] as? String,
|
|
665
|
+
customData2: json["customData2"] as? String,
|
|
666
|
+
customData3: json["customData3"] as? String,
|
|
667
|
+
customData4: json["customData4"] as? String,
|
|
668
|
+
customData5: json["customData5"] as? String,
|
|
669
|
+
customData6: json["customData6"] as? String,
|
|
670
|
+
customData7: json["customData7"] as? String,
|
|
671
|
+
customData8: json["customData8"] as? String,
|
|
672
|
+
customData9: json["customData9"] as? String,
|
|
673
|
+
customData10: json["customData10"] as? String,
|
|
674
|
+
customData11: json["customData11"] as? String,
|
|
675
|
+
customData12: json["customData12"] as? String,
|
|
676
|
+
customData13: json["customData13"] as? String,
|
|
677
|
+
customData14: json["customData14"] as? String,
|
|
678
|
+
customData15: json["customData15"] as? String,
|
|
679
|
+
customData16: json["customData16"] as? String,
|
|
680
|
+
customData17: json["customData17"] as? String,
|
|
681
|
+
customData18: json["customData18"] as? String,
|
|
682
|
+
customData19: json["customData19"] as? String,
|
|
683
|
+
customData20: json["customData20"] as? String,
|
|
684
|
+
customData21: json["customData21"] as? String,
|
|
685
|
+
customData22: json["customData22"] as? String,
|
|
686
|
+
customData23: json["customData23"] as? String,
|
|
687
|
+
customData24: json["customData24"] as? String,
|
|
688
|
+
customData25: json["customData25"] as? String,
|
|
689
|
+
customData26: json["customData26"] as? String,
|
|
690
|
+
customData27: json["customData27"] as? String,
|
|
691
|
+
customData28: json["customData28"] as? String,
|
|
692
|
+
customData29: json["customData29"] as? String,
|
|
693
|
+
customData30: json["customData30"] as? String,
|
|
694
|
+
experimentName: json["experimentName"] as? String
|
|
695
|
+
)
|
|
641
696
|
}
|
|
642
697
|
|
|
643
698
|
/**
|
|
@@ -650,13 +705,62 @@ extension RCTConvert {
|
|
|
650
705
|
return nil
|
|
651
706
|
}
|
|
652
707
|
var json: [String: Any?] = [:]
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
708
|
+
json["customData1"] = analyticsCustomData.customData1
|
|
709
|
+
json["customData2"] = analyticsCustomData.customData2
|
|
710
|
+
json["customData3"] = analyticsCustomData.customData3
|
|
711
|
+
json["customData4"] = analyticsCustomData.customData4
|
|
712
|
+
json["customData5"] = analyticsCustomData.customData5
|
|
713
|
+
json["customData6"] = analyticsCustomData.customData6
|
|
714
|
+
json["customData7"] = analyticsCustomData.customData7
|
|
715
|
+
json["customData8"] = analyticsCustomData.customData8
|
|
716
|
+
json["customData9"] = analyticsCustomData.customData9
|
|
717
|
+
json["customData10"] = analyticsCustomData.customData10
|
|
718
|
+
json["customData11"] = analyticsCustomData.customData11
|
|
719
|
+
json["customData12"] = analyticsCustomData.customData12
|
|
720
|
+
json["customData13"] = analyticsCustomData.customData13
|
|
721
|
+
json["customData14"] = analyticsCustomData.customData14
|
|
722
|
+
json["customData15"] = analyticsCustomData.customData15
|
|
723
|
+
json["customData16"] = analyticsCustomData.customData16
|
|
724
|
+
json["customData17"] = analyticsCustomData.customData17
|
|
725
|
+
json["customData18"] = analyticsCustomData.customData18
|
|
726
|
+
json["customData19"] = analyticsCustomData.customData19
|
|
727
|
+
json["customData20"] = analyticsCustomData.customData20
|
|
728
|
+
json["customData21"] = analyticsCustomData.customData21
|
|
729
|
+
json["customData22"] = analyticsCustomData.customData22
|
|
730
|
+
json["customData23"] = analyticsCustomData.customData23
|
|
731
|
+
json["customData24"] = analyticsCustomData.customData24
|
|
732
|
+
json["customData25"] = analyticsCustomData.customData25
|
|
733
|
+
json["customData26"] = analyticsCustomData.customData26
|
|
734
|
+
json["customData27"] = analyticsCustomData.customData27
|
|
735
|
+
json["customData28"] = analyticsCustomData.customData28
|
|
736
|
+
json["customData29"] = analyticsCustomData.customData29
|
|
737
|
+
json["customData30"] = analyticsCustomData.customData30
|
|
738
|
+
json["experimentName"] = analyticsCustomData.experimentName
|
|
658
739
|
return json
|
|
659
740
|
}
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
Utility method to get an analytics `SourceMetadata` value from a JS object.
|
|
744
|
+
- Parameter json: JS object.
|
|
745
|
+
- Returns: The associated `SourceMetadata` value or nil.
|
|
746
|
+
*/
|
|
747
|
+
static func analyticsSourceMetadata(_ json: Any?) -> SourceMetadata? {
|
|
748
|
+
guard let json = json as? [String: Any?] else {
|
|
749
|
+
return nil
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
let customData = analyticsCustomData(json)
|
|
753
|
+
|
|
754
|
+
return SourceMetadata(
|
|
755
|
+
videoId: json["videoId"] as? String,
|
|
756
|
+
title: json["title"] as? String,
|
|
757
|
+
path: json["path"] as? String,
|
|
758
|
+
isLive: json["isLive"] as? Bool,
|
|
759
|
+
cdnProvider: json["cdnProvider"] as? String,
|
|
760
|
+
customData: customData ?? CustomData()
|
|
761
|
+
)
|
|
762
|
+
}
|
|
763
|
+
|
|
660
764
|
/**
|
|
661
765
|
Utility method to compute a JS value from a `VideoQuality` object.
|
|
662
766
|
- Parameter videoQuality `VideoQuality` object to be converted.
|
|
@@ -675,4 +779,23 @@ extension RCTConvert {
|
|
|
675
779
|
"bitrate": videoQuality.bitrate,
|
|
676
780
|
]
|
|
677
781
|
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
Utility method to get a `UserInterfaceType` from a JS object.
|
|
785
|
+
- Parameter json: JS object
|
|
786
|
+
- Returns: The associated `UserInterfaceType` value or `nil`
|
|
787
|
+
*/
|
|
788
|
+
static func userInterfaceType(_ json: Any?) -> UserInterfaceType? {
|
|
789
|
+
guard let json = json as? String else {
|
|
790
|
+
return .none
|
|
791
|
+
}
|
|
792
|
+
switch json {
|
|
793
|
+
#if os(iOS)
|
|
794
|
+
case "Bitmovin": return .bitmovin
|
|
795
|
+
#endif
|
|
796
|
+
case "System": return .system
|
|
797
|
+
case "Subtitle": return .subtitle
|
|
798
|
+
default: return nil
|
|
799
|
+
}
|
|
800
|
+
}
|
|
678
801
|
}
|
|
@@ -56,5 +56,6 @@ RCT_EXPORT_VIEW_PROPERTY(onFullscreenExit, RCTBubblingEventBlock)
|
|
|
56
56
|
RCT_EXTERN_METHOD(attachPlayer:(nonnull NSNumber *)viewId playerId:(NSString *)playerId playerConfig:(nullable NSDictionary *)playerConfig)
|
|
57
57
|
RCT_EXTERN_METHOD(attachFullscreenBridge:(nonnull NSNumber *)viewId fullscreenBridgeId:(NSString *)fullscreenBridgeId)
|
|
58
58
|
RCT_EXTERN_METHOD(setCustomMessageHandlerBridgeId:(nonnull NSNumber *)viewId customMessageHandlerBridgeId:(NSString *)fullscreenBridgeId)
|
|
59
|
+
RCT_EXTERN_METHOD(setFullscreen:(nonnull NSNumber *)viewId isFullscreen:(BOOL)isFullscreen)
|
|
59
60
|
|
|
60
61
|
@end
|