bitmovin-player-react-native 0.11.0 → 0.13.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 (92) hide show
  1. package/README.md +27 -14
  2. package/RNBitmovinPlayer.podspec +1 -1
  3. package/android/.editorconfig +8 -0
  4. package/android/build.gradle +8 -2
  5. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  6. package/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  7. package/android/src/main/java/com/bitmovin/player/reactnative/BitmovinCastManagerModule.kt +76 -0
  8. package/android/src/main/java/com/bitmovin/player/reactnative/DrmModule.kt +4 -4
  9. package/android/src/main/java/com/bitmovin/player/reactnative/EventRelay.kt +1 -1
  10. package/android/src/main/java/com/bitmovin/player/reactnative/OfflineModule.kt +22 -11
  11. package/android/src/main/java/com/bitmovin/player/reactnative/PlayerAnalyticsModule.kt +58 -0
  12. package/android/src/main/java/com/bitmovin/player/reactnative/PlayerModule.kt +80 -1
  13. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt +36 -5
  14. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt +71 -29
  15. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewPackage.kt +6 -3
  16. package/android/src/main/java/com/bitmovin/player/reactnative/SourceModule.kt +55 -17
  17. package/android/src/main/java/com/bitmovin/player/reactnative/UuidModule.kt +1 -1
  18. package/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt +387 -165
  19. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReadableMapExtension.kt +19 -1
  20. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/WritableMap.kt +8 -0
  21. package/android/src/main/java/com/bitmovin/player/reactnative/offline/OfflineContentManagerBridge.kt +39 -22
  22. package/android/src/main/java/com/bitmovin/player/reactnative/offline/OfflineDownloadRequest.kt +1 -1
  23. package/android/src/main/java/com/bitmovin/player/reactnative/ui/CustomMessageHandlerBridge.kt +9 -7
  24. package/android/src/main/java/com/bitmovin/player/reactnative/ui/CustomMessageHandlerModule.kt +6 -6
  25. package/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerBridge.kt +1 -1
  26. package/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerModule.kt +4 -4
  27. package/android/src/main/java/com/bitmovin/player/reactnative/ui/RNPictureInPictureHandler.kt +14 -5
  28. package/ios/AudioSessionModule.swift +9 -9
  29. package/ios/BitmovinCastManagerModule.m +14 -0
  30. package/ios/BitmovinCastManagerModule.swift +82 -0
  31. package/ios/CustomMessageHandlerBridge.swift +11 -3
  32. package/ios/CustomMessageHandlerModule.swift +20 -12
  33. package/ios/DrmModule.swift +58 -37
  34. package/ios/Event+JSON.swift +28 -7
  35. package/ios/FullscreenHandlerBridge.swift +1 -1
  36. package/ios/FullscreenHandlerModule.swift +10 -10
  37. package/ios/OfflineContentManagerBridge.swift +53 -24
  38. package/ios/OfflineModule.swift +46 -35
  39. package/ios/PlayerAnalyticsModule.m +8 -0
  40. package/ios/PlayerAnalyticsModule.swift +66 -0
  41. package/ios/PlayerModule.m +11 -0
  42. package/ios/PlayerModule.swift +122 -18
  43. package/ios/RCTConvert+BitmovinPlayer.swift +355 -129
  44. package/ios/RNPlayerView+PlayerListener.swift +80 -43
  45. package/ios/RNPlayerView+UserInterfaceListener.swift +8 -8
  46. package/ios/RNPlayerView.swift +13 -2
  47. package/ios/RNPlayerViewManager.m +13 -0
  48. package/ios/RNPlayerViewManager.swift +114 -33
  49. package/ios/Registry.swift +2 -2
  50. package/ios/SourceModule.m +9 -2
  51. package/ios/SourceModule.swift +81 -20
  52. package/ios/UuidModule.swift +6 -5
  53. package/lib/index.d.mts +1192 -576
  54. package/lib/index.d.ts +1192 -576
  55. package/lib/index.js +233 -170
  56. package/lib/index.mjs +219 -155
  57. package/package.json +9 -6
  58. package/src/advertising.ts +18 -0
  59. package/src/analytics/config.ts +48 -64
  60. package/src/analytics/index.ts +1 -1
  61. package/src/analytics/player.ts +36 -0
  62. package/src/bitmovinCastManager.ts +87 -0
  63. package/src/bufferConfig.ts +42 -0
  64. package/src/components/PlayerView/events.ts +237 -0
  65. package/src/components/PlayerView/index.tsx +31 -45
  66. package/src/components/PlayerView/native.ts +1 -1
  67. package/src/components/PlayerView/pictureInPictureConfig.ts +22 -0
  68. package/src/components/PlayerView/playerViewConfig.ts +38 -0
  69. package/src/components/PlayerView/properties.ts +66 -0
  70. package/src/components/index.ts +3 -0
  71. package/src/drm/index.ts +7 -2
  72. package/src/drm/widevineConfig.ts +11 -3
  73. package/src/events.ts +112 -20
  74. package/src/hooks/usePlayer.ts +2 -1
  75. package/src/index.ts +8 -0
  76. package/src/offline/offlineContentManager.ts +33 -4
  77. package/src/offline/offlineContentManagerListener.ts +45 -29
  78. package/src/playbackConfig.ts +77 -0
  79. package/src/player.ts +56 -141
  80. package/src/playerConfig.ts +66 -0
  81. package/src/remoteControlConfig.ts +37 -0
  82. package/src/source.ts +42 -5
  83. package/src/subtitleTrack.ts +8 -11
  84. package/src/tweaksConfig.ts +8 -8
  85. package/src/ui/custommessagehandler.ts +33 -12
  86. package/src/ui/custommessagehandlerbridge.ts +2 -0
  87. package/src/ui/custommessagesender.ts +1 -0
  88. package/android/src/main/java/com/bitmovin/player/reactnative/AnalyticsModule.kt +0 -196
  89. package/ios/AnalyticsModule.m +0 -15
  90. package/ios/AnalyticsModule.swift +0 -219
  91. package/src/analytics/collector.ts +0 -119
  92. package/src/utils.ts +0 -15
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Bitmovin Player React Native
2
2
 
3
- Official React Native bindings for Bitmovin's mobile Player SDKs.
3
+ This is an open-source project created to enable customers to integrate the Bitmovin mobile Player SDKs into React Native projects. It has been created to provide customers with a starting point, which can be built upon through active collaboration and contributions. We look forward to seeing this library expand and grow.
4
4
 
5
5
  [![Build](https://github.com/bitmovin/bitmovin-player-react-native/actions/workflows/ci.yml/badge.svg)](https://github.com/bitmovin/bitmovin-player-react-native/actions/workflows/ci.yml)
6
6
  ![Platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20tvOS%20%7C%20Android%20%7C%20Android%20TV-lightgrey.svg)
@@ -10,17 +10,6 @@ Official React Native bindings for Bitmovin's mobile Player SDKs.
10
10
  [![npm dm](https://img.shields.io/npm/dm/bitmovin-player-react-native.svg)](https://www.npmjs.com/package/bitmovin-player-react-native)
11
11
  [![npm dt](https://img.shields.io/npm/dt/bitmovin-player-react-native.svg)](https://www.npmjs.com/package/bitmovin-player-react-native)
12
12
 
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
-
15
- - [Bitmovin Player React Native](#bitmovin-player-react-native)
16
- - [Platform Support](#platform-support)
17
- - [Feature Support](#feature-support)
18
- - [Documentation](#documentation)
19
- - [Getting Started](#getting-started-guide)
20
- - [Feature Guides](#feature-guides)
21
- - [Sample Application](#sample-application)
22
- - [Contributing](#contributing)
23
-
24
13
  ## Platform Support
25
14
 
26
15
  This library requires at least React Native 0.64+ and React 17+ to work properly. The currently supported platforms are:
@@ -39,6 +28,10 @@ Please refer to the [Features](https://developer.bitmovin.com/playback/docs/reac
39
28
 
40
29
  ## Documentation
41
30
 
31
+ ### API Reference
32
+
33
+ Our API reference can be found [here](https://cdn.bitmovin.com/player/reactnative/0/docs/index.html).
34
+
42
35
  ### Getting Started Guide
43
36
 
44
37
  Our [Getting Started Guide](https://developer.bitmovin.com/playback/docs/getting-started-react-native) walks you through setting up and configuring the Bitmovin Player in React Native projects.
@@ -51,6 +44,26 @@ Check out our [React Native Guides](https://developer.bitmovin.com/playback/docs
51
44
 
52
45
  In the [/example/](https://github.com/bitmovin/bitmovin-player-react-native/tree/development/example) folder you can find a sample application showcasing many of the features of the Player React Native SDK.
53
46
 
54
- ## Contributing
47
+ ## Maintenance and Updates
48
+
49
+ As an open source project, this library is not part of a regular maintenance or update schedule and is updated on an adhoc basis when contributions are made.
50
+
51
+ ## Contributing to this project
52
+
53
+ We are pleased to accept changes, updates and fixes from the community wishing to use and expand this project. Bitmovin will review any Pull Requests made. We do our best to provide timely feedback, but please note that no SLAs apply. New releases are tagged and published on our discretion. Please see [CONTRIBUTING.md](CONTRIBUTING.md) for more details on how to contribute.
54
+
55
+ ## Raising a Feature Suggestion
56
+
57
+ If you see something missing that might be useful but are unable to contribute the feature yourself, please feel free to [submit a feature request](https://community.bitmovin.com/t/how-to-submit-a-feature-request-to-us/1463) through the Bitmovin Community. Feature suggestions will be considered by Bitmovin’s Product team for future roadmap plans.
58
+
59
+ ## Reporting a bug
60
+
61
+ If you come across a bug related to the Player React Native SDK, please raise this through the support ticketing system accessible in your [Bitmovin Dashboard](https://dashboard.bitmovin.com/support/tickets).
62
+
63
+ ## Support and SLA Disclaimer
64
+
65
+ As an open-source project and not a core product offering, any request, issue or query related to this project is excluded from any SLA and Support terms that a customer might have with either Bitmovin or another third-party service provider or Company contributing to this project. Any and all updates are purely at the contributor's discretion.
66
+
67
+ ## Need more help?
55
68
 
56
- See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
69
+ Should you need further help, please raise your request to your Bitmovin account team. We can assist in a number of ways, from providing you professional services help to putting you in touch with preferred system integrators who can work with you to achieve your goals.
@@ -19,7 +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.44.1"
22
+ s.dependency "BitmovinPlayer", "3.46.0"
23
23
  s.ios.dependency "GoogleAds-IMA-iOS-SDK", "3.18.4"
24
24
  s.tvos.dependency "GoogleAds-IMA-tvOS-SDK", "4.8.2"
25
25
  end
@@ -0,0 +1,8 @@
1
+ [*.{kt,kts}]
2
+ ktlint_code_style=android_studio
3
+ max_line_length=120
4
+ # Allow wildcard imports for react native bridge, since IntelliJ does this automatically when performing
5
+ # imports optimization.
6
+ ij_kotlin_packages_to_use_import_on_demand = com.facebook.react.bridge.*
7
+ ij_kotlin_allow_trailing_comma=true
8
+ ij_kotlin_allow_trailing_comma_on_call_site=true
@@ -2,19 +2,25 @@ buildscript {
2
2
  ext {
3
3
  kotlinVersion = '1.7.21'
4
4
  androidToolsVersion = '7.4.2'
5
+ ktlintVersion = '11.6.0'
5
6
  }
6
7
  repositories {
7
8
  google()
8
9
  mavenCentral()
10
+ maven {
11
+ url "https://plugins.gradle.org/m2/"
12
+ }
9
13
  }
10
14
  dependencies {
11
15
  classpath "com.android.tools.build:gradle:$androidToolsVersion"
12
16
  classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
17
+ classpath "org.jlleitschuh.gradle:ktlint-gradle:$ktlintVersion"
13
18
  }
14
19
  }
15
20
 
16
21
  apply plugin: 'com.android.library'
17
22
  apply plugin: 'kotlin-android'
23
+ apply plugin: 'org.jlleitschuh.gradle.ktlint'
18
24
 
19
25
  repositories {
20
26
  mavenLocal()
@@ -51,9 +57,9 @@ android {
51
57
 
52
58
  dependencies {
53
59
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
54
- implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.29.0'
60
+ implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.31.0'
55
61
  implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
56
- implementation 'com.bitmovin.player:player:3.43.0'
62
+ implementation 'com.bitmovin.player:player:3.48.0+jason'
57
63
  //noinspection GradleDynamicVersion
58
64
  implementation 'com.facebook.react:react-native:+' // From node_modules
59
65
  }
@@ -0,0 +1,7 @@
1
+ distributionBase=GRADLE_USER_HOME
2
+ distributionPath=wrapper/dists
3
+ distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
4
+ networkTimeout=10000
5
+ validateDistributionUrl=true
6
+ zipStoreBase=GRADLE_USER_HOME
7
+ zipStorePath=wrapper/dists
@@ -0,0 +1,76 @@
1
+ package com.bitmovin.player.reactnative
2
+
3
+ import com.bitmovin.player.casting.BitmovinCastManager
4
+ import com.bitmovin.player.reactnative.converter.JsonConverter
5
+ import com.facebook.react.bridge.Promise
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
8
+ import com.facebook.react.bridge.ReactMethod
9
+ import com.facebook.react.bridge.ReadableMap
10
+ import com.facebook.react.module.annotations.ReactModule
11
+ import com.facebook.react.uimanager.UIManagerModule
12
+
13
+ private const val MODULE_NAME = "BitmovinCastManagerModule"
14
+
15
+ @ReactModule(name = MODULE_NAME)
16
+ class BitmovinCastManagerModule(
17
+ private val context: ReactApplicationContext,
18
+ ) : ReactContextBaseJavaModule(context) {
19
+ override fun getName() = MODULE_NAME
20
+
21
+ /**
22
+ * Returns whether the [BitmovinCastManager] is initialized.
23
+ */
24
+ @ReactMethod
25
+ fun isInitialized(promise: Promise) = uiManager?.addUIBlock {
26
+ promise.resolve(BitmovinCastManager.isInitialized())
27
+ }
28
+
29
+ /**
30
+ * Initializes the [BitmovinCastManager] with the given options.
31
+ */
32
+ @ReactMethod
33
+ fun initializeCastManager(options: ReadableMap?, promise: Promise) {
34
+ val castOptions = JsonConverter.toCastOptions(options)
35
+ uiManager?.addUIBlock {
36
+ BitmovinCastManager.initialize(
37
+ castOptions?.applicationId,
38
+ castOptions?.messageNamespace,
39
+ )
40
+ promise.resolve(null)
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Sends a message to the receiver.
46
+ */
47
+ @ReactMethod
48
+ fun sendMessage(message: String, messageNamespace: String?, promise: Promise) {
49
+ uiManager?.addUIBlock {
50
+ BitmovinCastManager.getInstance().sendMessage(message, messageNamespace)
51
+ promise.resolve(null)
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Updates the context of the [BitmovinCastManager] to the current activity.
57
+ */
58
+ @ReactMethod
59
+ fun updateContext(promise: Promise) {
60
+ uiManager?.addUIBlock {
61
+ BitmovinCastManager.getInstance().updateContext(currentActivity)
62
+ promise.resolve(null)
63
+ }
64
+ }
65
+
66
+ private val uiManager: UIManagerModule?
67
+ get() = context.getNativeModule(UIManagerModule::class.java)
68
+ }
69
+
70
+ /**
71
+ * Represents configuration options for the [BitmovinCastManager].
72
+ */
73
+ data class BitmovinCastManagerOptions(
74
+ val applicationId: String? = null,
75
+ val messageNamespace: String? = null,
76
+ )
@@ -130,7 +130,7 @@ class DrmModule(private val context: ReactApplicationContext) : ReactContextBase
130
130
  nativeId,
131
131
  "onPrepareMessage",
132
132
  preparedMessages,
133
- preparedMessagesCondition
133
+ preparedMessagesCondition,
134
134
  )
135
135
  widevineConfig.prepareMessageCallback = PrepareMessageCallback {
136
136
  prepareMessage(it)
@@ -151,7 +151,7 @@ class DrmModule(private val context: ReactApplicationContext) : ReactContextBase
151
151
  nativeId,
152
152
  "onPrepareLicense",
153
153
  preparedLicenses,
154
- preparedLicensesCondition
154
+ preparedLicensesCondition,
155
155
  )
156
156
  widevineConfig.prepareLicenseCallback = PrepareLicenseCallback {
157
157
  prepareLicense(it)
@@ -170,11 +170,11 @@ class DrmModule(private val context: ReactApplicationContext) : ReactContextBase
170
170
  nativeId: NativeId,
171
171
  method: String,
172
172
  registry: Registry<String>,
173
- registryCondition: Condition
173
+ registryCondition: Condition,
174
174
  ): PrepareCallback = {
175
175
  val args = Arguments.createArray()
176
176
  args.pushString(Base64.encodeToString(it, Base64.NO_WRAP))
177
- context.catalystInstance.callFunction("DRM-${nativeId}", method, args as NativeArray)
177
+ context.catalystInstance.callFunction("DRM-$nativeId", method, args as NativeArray)
178
178
  lock.withLock {
179
179
  registryCondition.await()
180
180
  val result = registry[nativeId]
@@ -21,7 +21,7 @@ class EventRelay<E : EventEmitter<T>, T : Event>(
21
21
  /**
22
22
  * Is called for every relayed event together with its associated name.
23
23
  */
24
- private val eventOutput: (String, Event) -> Unit
24
+ private val eventOutput: (String, Event) -> Unit,
25
25
  ) {
26
26
  private val eventListeners = forwardingEventClassesAndNameMapping.map {
27
27
  Subscription(it.key) { event -> eventOutput(it.value, event) }
@@ -3,8 +3,8 @@ package com.bitmovin.player.reactnative
3
3
  import com.bitmovin.player.api.offline.options.OfflineOptionEntryState
4
4
  import com.bitmovin.player.reactnative.converter.JsonConverter
5
5
  import com.bitmovin.player.reactnative.extensions.toList
6
- import com.bitmovin.player.reactnative.offline.OfflineDownloadRequest
7
6
  import com.bitmovin.player.reactnative.offline.OfflineContentManagerBridge
7
+ import com.bitmovin.player.reactnative.offline.OfflineDownloadRequest
8
8
  import com.facebook.react.bridge.*
9
9
  import com.facebook.react.module.annotations.ReactModule
10
10
  import com.facebook.react.uimanager.UIManagerModule
@@ -69,7 +69,13 @@ class OfflineModule(private val context: ReactApplicationContext) : ReactContext
69
69
  return@addUIBlock
70
70
  }
71
71
 
72
- offlineContentManagerBridges[nativeId] = OfflineContentManagerBridge(nativeId, context, identifier, sourceConfig, context.cacheDir.path)
72
+ offlineContentManagerBridges[nativeId] = OfflineContentManagerBridge(
73
+ nativeId,
74
+ context,
75
+ identifier,
76
+ sourceConfig,
77
+ context.cacheDir.path,
78
+ )
73
79
  }
74
80
  promise.resolve(null)
75
81
  }
@@ -117,7 +123,8 @@ class OfflineModule(private val context: ReactApplicationContext) : ReactContext
117
123
  return@safeOfflineContentManager
118
124
  }
119
125
  OfflineOptionEntryState.Downloading,
120
- OfflineOptionEntryState.Failed -> {
126
+ OfflineOptionEntryState.Failed,
127
+ -> {
121
128
  promise.reject(IllegalStateException("Download already in progress"))
122
129
  return@safeOfflineContentManager
123
130
  }
@@ -127,7 +134,7 @@ class OfflineModule(private val context: ReactApplicationContext) : ReactContext
127
134
  }
128
135
  else -> {}
129
136
  }
130
- val minimumBitRate = if(request.hasKey("minimumBitrate")) request.getInt("minimumBitrate") else null
137
+ val minimumBitRate = if (request.hasKey("minimumBitrate")) request.getInt("minimumBitrate") else null
131
138
  if (minimumBitRate != null && minimumBitRate < 0) {
132
139
  promise.reject(IllegalArgumentException("Invalid download request"))
133
140
  return@safeOfflineContentManager
@@ -136,7 +143,9 @@ class OfflineModule(private val context: ReactApplicationContext) : ReactContext
136
143
  val audioOptionIds = request.getArray("audioOptionIds")?.toList<String>()?.filterNotNull()
137
144
  val textOptionIds = request.getArray("textOptionIds")?.toList<String>()?.filterNotNull()
138
145
 
139
- getOfflineContentManagerBridge(nativeId)?.process(OfflineDownloadRequest(minimumBitRate, audioOptionIds, textOptionIds))
146
+ getOfflineContentManagerBridge(nativeId)?.process(
147
+ OfflineDownloadRequest(minimumBitRate, audioOptionIds, textOptionIds),
148
+ )
140
149
  promise.resolve(null)
141
150
  } catch (e: Exception) {
142
151
  promise.reject(e)
@@ -260,20 +269,22 @@ class OfflineModule(private val context: ReactApplicationContext) : ReactContext
260
269
  }
261
270
  }
262
271
 
263
- private fun safeOfflineContentManager(nativeId: NativeId, promise: Promise, runBlock: OfflineContentManagerBridge.() -> Unit) {
272
+ private fun safeOfflineContentManager(
273
+ nativeId: NativeId,
274
+ promise: Promise,
275
+ runBlock: OfflineContentManagerBridge.() -> Unit,
276
+ ) {
264
277
  getOfflineContentManagerBridge(nativeId)?.let(runBlock)
265
- ?: promise.reject(IllegalArgumentException("Could not find the offline module instance"))
278
+ ?: promise.reject(IllegalArgumentException("Could not find the offline module instance"))
266
279
  }
267
280
 
268
281
  /**
269
282
  * Helper function that returns the initialized `DrmModule` instance.
270
283
  */
271
- private fun drmModule(): DrmModule? =
272
- context.getNativeModule(DrmModule::class.java)
284
+ private fun drmModule(): DrmModule? = context.getNativeModule(DrmModule::class.java)
273
285
 
274
286
  /**
275
287
  * Helper function that returns the initialized `UIManager` instance.
276
288
  */
277
- private fun uiManager(): UIManagerModule? =
278
- context.getNativeModule(UIManagerModule::class.java)
289
+ private fun uiManager(): UIManagerModule? = context.getNativeModule(UIManagerModule::class.java)
279
290
  }
@@ -0,0 +1,58 @@
1
+ package com.bitmovin.player.reactnative
2
+
3
+ import com.bitmovin.player.api.analytics.AnalyticsApi.Companion.analytics
4
+ import com.bitmovin.player.reactnative.converter.JsonConverter
5
+ import com.facebook.react.bridge.*
6
+ import com.facebook.react.module.annotations.ReactModule
7
+ import com.facebook.react.uimanager.UIManagerModule
8
+
9
+ private const val MODULE_NAME = "PlayerAnalyticsModule"
10
+
11
+ @ReactModule(name = MODULE_NAME)
12
+ class PlayerAnalyticsModule(private val context: ReactApplicationContext) : ReactContextBaseJavaModule(context) {
13
+ /**
14
+ * JS exported module name.
15
+ */
16
+ override fun getName() = MODULE_NAME
17
+
18
+ /**
19
+ * Sends a sample with the provided custom data.
20
+ * Does not change the configured custom data of the collector or source.
21
+ * @param nativeId Native Id of the collector instance.
22
+ * @param json Custom data config json.
23
+ */
24
+ @ReactMethod
25
+ fun sendCustomDataEvent(nativeId: NativeId, json: ReadableMap?) {
26
+ uiManager()?.addUIBlock { _ ->
27
+ JsonConverter.toAnalyticsCustomData(json)?.let {
28
+ playerModule()?.getPlayer(nativeId)?.analytics?.sendCustomDataEvent(it)
29
+ }
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Gets the current user Id for a player instance with analytics.
35
+ * @param nativeId Native Id of the the player instance.
36
+ * @param promise JS promise object.
37
+ */
38
+ @ReactMethod
39
+ fun getUserId(playerId: NativeId, promise: Promise) {
40
+ uiManager()?.addUIBlock { _ ->
41
+ playerModule()?.getPlayer(playerId)?.analytics?.let {
42
+ promise.resolve(it.userId)
43
+ }
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Helper function that gets the instantiated `UIManagerModule` from modules registry.
49
+ */
50
+ private fun uiManager(): UIManagerModule? =
51
+ context.getNativeModule(UIManagerModule::class.java)
52
+
53
+ /**
54
+ * Helper function that gets the instantiated `PlayerModule` from modules registry.
55
+ */
56
+ private fun playerModule(): PlayerModule? =
57
+ context.getNativeModule(PlayerModule::class.java)
58
+ }
@@ -1,6 +1,10 @@
1
1
  package com.bitmovin.player.reactnative
2
2
 
3
+ import android.util.Log
4
+ import com.bitmovin.analytics.api.DefaultMetadata
3
5
  import com.bitmovin.player.api.Player
6
+ import com.bitmovin.player.api.analytics.create
7
+ import com.bitmovin.player.api.event.PlayerEvent
4
8
  import com.bitmovin.player.reactnative.converter.JsonConverter
5
9
  import com.facebook.react.bridge.*
6
10
  import com.facebook.react.module.annotations.ReactModule
@@ -47,6 +51,37 @@ class PlayerModule(private val context: ReactApplicationContext) : ReactContextB
47
51
  }
48
52
  }
49
53
 
54
+ /**
55
+ * Creates a new `Player` instance inside the internal players using the provided `playerConfig` and `analyticsConfig`.
56
+ * @param playerConfigJson `PlayerConfig` object received from JS.
57
+ * @param analyticsConfigJson `AnalyticsConfig` object received from JS.
58
+ */
59
+ @ReactMethod
60
+ fun initWithAnalyticsConfig(nativeId: NativeId, playerConfigJson: ReadableMap?, analyticsConfigJson: ReadableMap?) {
61
+ uiManager()?.addUIBlock {
62
+ if (players.containsKey(nativeId)) {
63
+ Log.d("[PlayerModule]", "Duplicate player creation for id $nativeId")
64
+ return@addUIBlock
65
+ }
66
+ val playerConfig = JsonConverter.toPlayerConfig(playerConfigJson)
67
+ val analyticsConfig = JsonConverter.toAnalyticsConfig(analyticsConfigJson)
68
+ val defaultMetadata = JsonConverter.toAnalyticsDefaultMetadata(
69
+ analyticsConfigJson?.getMap("defaultMetadata"),
70
+ )
71
+
72
+ players[nativeId] = if (analyticsConfig == null) {
73
+ Player.create(context, playerConfig)
74
+ } else {
75
+ Player.create(
76
+ context = context,
77
+ playerConfig = playerConfig,
78
+ analyticsConfig = analyticsConfig,
79
+ defaultMetadata = defaultMetadata ?: DefaultMetadata(),
80
+ )
81
+ }
82
+ }
83
+ }
84
+
50
85
  /**
51
86
  * Load the source of the given `nativeId` with `config` options from JS.
52
87
  * @param nativeId Target player.
@@ -448,7 +483,9 @@ class PlayerModule(private val context: ReactApplicationContext) : ReactContextB
448
483
  @ReactMethod
449
484
  fun setMaxSelectableBitrate(nativeId: NativeId, maxSelectableBitrate: Int) {
450
485
  uiManager()?.addUIBlock {
451
- players[nativeId]?.setMaxSelectableVideoBitrate(maxSelectableBitrate.takeUnless { it == -1 } ?: Integer.MAX_VALUE)
486
+ players[nativeId]?.setMaxSelectableVideoBitrate(
487
+ maxSelectableBitrate.takeUnless { it == -1 } ?: Integer.MAX_VALUE,
488
+ )
452
489
  }
453
490
  }
454
491
 
@@ -464,6 +501,48 @@ class PlayerModule(private val context: ReactApplicationContext) : ReactContextB
464
501
  }
465
502
  }
466
503
 
504
+ /**
505
+ * Initiates casting the current video to a cast-compatible remote device. The user has to choose to which device it
506
+ * should be sent.
507
+ */
508
+ @ReactMethod
509
+ fun castVideo(nativeId: NativeId) {
510
+ uiManager()?.addUIBlock {
511
+ players[nativeId]?.castVideo()
512
+ }
513
+ }
514
+
515
+ /**
516
+ * Stops casting the current video. Has no effect if [isCasting] is false.
517
+ */
518
+ @ReactMethod
519
+ fun castStop(nativeId: NativeId) {
520
+ uiManager()?.addUIBlock {
521
+ players[nativeId]?.castStop()
522
+ }
523
+ }
524
+
525
+ /**
526
+ * Whether casting to a cast-compatible remote device is available. [PlayerEvent.CastAvailable] signals when
527
+ * casting becomes available.
528
+ */
529
+ @ReactMethod
530
+ fun isCastAvailable(nativeId: NativeId, promise: Promise) {
531
+ uiManager()?.addUIBlock {
532
+ promise.resolve(players[nativeId]?.isCastAvailable)
533
+ }
534
+ }
535
+
536
+ /**
537
+ * Whether video is currently being casted to a remote device and not played locally.
538
+ */
539
+ @ReactMethod
540
+ fun isCasting(nativeId: NativeId, promise: Promise) {
541
+ uiManager()?.addUIBlock {
542
+ promise.resolve(players[nativeId]?.isCasting)
543
+ }
544
+ }
545
+
467
546
  /**
468
547
  * Helper function that returns the initialized `UIManager` instance.
469
548
  */
@@ -11,6 +11,7 @@ import com.bitmovin.player.api.Player
11
11
  import com.bitmovin.player.api.event.Event
12
12
  import com.bitmovin.player.api.event.PlayerEvent
13
13
  import com.bitmovin.player.api.event.SourceEvent
14
+ import com.bitmovin.player.api.ui.PlayerViewConfig
14
15
  import com.bitmovin.player.reactnative.converter.JsonConverter
15
16
  import com.bitmovin.player.reactnative.ui.RNPictureInPictureDelegate
16
17
  import com.bitmovin.player.reactnative.ui.RNPictureInPictureHandler
@@ -62,6 +63,18 @@ private val EVENT_CLASS_TO_REACT_NATIVE_NAME_MAPPING = mapOf(
62
63
  PlayerEvent.AdSkipped::class to "adSkipped",
63
64
  PlayerEvent.AdStarted::class to "adStarted",
64
65
  PlayerEvent.VideoPlaybackQualityChanged::class to "videoPlaybackQualityChanged",
66
+ PlayerEvent.CastStart::class to "castStart",
67
+ @Suppress("DEPRECATION")
68
+ PlayerEvent.CastPlaybackFinished::class to "castPlaybackFinished",
69
+ @Suppress("DEPRECATION")
70
+ PlayerEvent.CastPaused::class to "castPaused",
71
+ @Suppress("DEPRECATION")
72
+ PlayerEvent.CastPlaying::class to "castPlaying",
73
+ PlayerEvent.CastStarted::class to "castStarted",
74
+ PlayerEvent.CastAvailable::class to "castAvailable",
75
+ PlayerEvent.CastStopped::class to "castStopped",
76
+ PlayerEvent.CastWaitingForDevice::class to "castWaitingForDevice",
77
+ PlayerEvent.CastTimeUpdated::class to "castTimeUpdated",
65
78
  )
66
79
 
67
80
  private val EVENT_CLASS_TO_REACT_NATIVE_NAME_MAPPING_UI = mapOf<KClass<out Event>, String>(
@@ -80,8 +93,11 @@ private val EVENT_CLASS_TO_REACT_NATIVE_NAME_MAPPING_UI = mapOf<KClass<out Event
80
93
  * exposes player events as bubbling events.
81
94
  */
82
95
  @SuppressLint("ViewConstructor")
83
- class RNPlayerView(val context: ReactApplicationContext) : LinearLayout(context),
84
- LifecycleEventListener, View.OnLayoutChangeListener, RNPictureInPictureDelegate {
96
+ class RNPlayerView(val context: ReactApplicationContext) :
97
+ LinearLayout(context),
98
+ LifecycleEventListener,
99
+ View.OnLayoutChangeListener,
100
+ RNPictureInPictureDelegate {
85
101
 
86
102
  init {
87
103
  // React Native has a bug that dynamically added views sometimes aren't laid out again properly.
@@ -97,6 +113,7 @@ class RNPlayerView(val context: ReactApplicationContext) : LinearLayout(context)
97
113
  * to the `eventOutput` callback.
98
114
  */
99
115
  private val playerEventRelay = EventRelay<Player, Event>(EVENT_CLASS_TO_REACT_NATIVE_NAME_MAPPING, ::emitEvent)
116
+
100
117
  /**
101
118
  * Relays the provided set of events, emitted by the player view, together with the associated name
102
119
  * to the `eventOutput` callback.
@@ -128,6 +145,11 @@ class RNPlayerView(val context: ReactApplicationContext) : LinearLayout(context)
128
145
  */
129
146
  var pictureInPictureHandler: RNPictureInPictureHandler? = null
130
147
 
148
+ /**
149
+ * Configures the visual presentation and behaviour of the [playerView].
150
+ */
151
+ var config: RNPlayerViewConfigWrapper? = null
152
+
131
153
  /**
132
154
  * Whether this view should pause video playback when activity's onPause is called.
133
155
  * By default, `shouldPausePlaybackOnActivityPause` is set to false when entering PiP mode.
@@ -261,7 +283,7 @@ class RNPlayerView(val context: ReactApplicationContext) : LinearLayout(context)
261
283
  post {
262
284
  measure(
263
285
  MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
264
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
286
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
265
287
  )
266
288
  layout(left, top, right, bottom)
267
289
  }
@@ -269,8 +291,8 @@ class RNPlayerView(val context: ReactApplicationContext) : LinearLayout(context)
269
291
 
270
292
  /**
271
293
  * Emits a bubbling event with payload to js.
272
- * @param event Native event name.
273
- * @param json Optional js object to be sent as payload.
294
+ * @param name Native event name.
295
+ * @param event Optional js object to be sent as payload.
274
296
  */
275
297
  private inline fun <reified E : Event> emitEvent(name: String, event: E) {
276
298
  val payload = if (event is PlayerEvent) {
@@ -284,3 +306,12 @@ class RNPlayerView(val context: ReactApplicationContext) : LinearLayout(context)
284
306
  .receiveEvent(id, name, payload)
285
307
  }
286
308
  }
309
+
310
+ /**
311
+ * Representation of the React Native API `PlayerViewConfig` object.
312
+ * This is necessary as not all of its values can be directly mapped to the native `PlayerViewConfig`.
313
+ */
314
+ data class RNPlayerViewConfigWrapper(
315
+ val playerViewConfig: PlayerViewConfig?,
316
+ val pictureInPictureConfig: RNPictureInPictureHandler.PictureInPictureConfig?,
317
+ )