bitmovin-player-react-native 0.7.2 → 0.8.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 CHANGED
@@ -11,11 +11,6 @@ Official React Native bindings for Bitmovin's mobile Player SDKs.
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
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 21+
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
- Features of the native mobile Player SDKs are progressively being implemented in this React Native library. The table below summarizes the current state of the main Player SDK features.
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
 
@@ -19,9 +19,9 @@ 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.41.0"
22
+ s.dependency "BitmovinPlayer", "3.41.2"
23
23
  s.dependency "BitmovinAnalyticsCollector/Core", "2.9.4"
24
24
  s.dependency "BitmovinAnalyticsCollector/BitmovinPlayer", "2.9.4"
25
- s.ios.dependency "GoogleAds-IMA-iOS-SDK", "3.17.0"
26
- s.tvos.dependency "GoogleAds-IMA-tvOS-SDK", "4.6.1"
25
+ s.ios.dependency "GoogleAds-IMA-iOS-SDK", "3.18.4"
26
+ s.tvos.dependency "GoogleAds-IMA-tvOS-SDK", "4.8.2"
27
27
  end
@@ -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.26.0'
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.39.0'
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
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
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
@@ -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(): MutableMap<String, Int> = mutableMapOf(
136
- "attachPlayer" to Commands.ATTACH_PLAYER.ordinal,
137
- "attachFullscreenBridge" to Commands.ATTACH_FULLSCREEN_BRIDGE.ordinal,
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
- super.receiveCommand(view, commandId, args)
149
- commandId?.toInt()?.let {
150
- when (it) {
151
- Commands.ATTACH_PLAYER.ordinal -> attachPlayer(view, args?.getString(1), args?.getMap(2))
152
- Commands.ATTACH_FULLSCREEN_BRIDGE.ordinal -> args?.getString(1)?.let { fullscreenBridgeId ->
153
- attachFullscreenBridge(view, fullscreenBridgeId)
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
- Commands.SET_CUSTOM_MESSAGE_HANDLER_BRIDGE_ID.ordinal -> {
156
- args?.getString(1)?.let { customMessageHandlerBridgeId ->
157
- setCustomMessageHandlerBridgeId(view, customMessageHandlerBridgeId)
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)
@@ -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)
@@ -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: []) {}
@@ -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
@@ -71,6 +71,28 @@ class RNPlayerViewManager: RCTViewManager {
71
71
  self.customMessageHandlerBridgeId = customMessageHandlerBridgeId
72
72
  }
73
73
 
74
+ @objc func setFullscreen(_ viewId: NSNumber, isFullscreen: Bool) {
75
+ bridge.uiManager.addUIBlock { [weak self] _, views in
76
+ guard
77
+ let self,
78
+ let view = views?[viewId] as? RNPlayerView
79
+ else {
80
+ return
81
+ }
82
+ guard let playerView = view.playerView else {
83
+ return
84
+ }
85
+ guard playerView.isFullscreen != isFullscreen else {
86
+ return
87
+ }
88
+ if isFullscreen {
89
+ playerView.enterFullscreen()
90
+ } else {
91
+ playerView.exitFullscreen()
92
+ }
93
+ }
94
+ }
95
+
74
96
  /// Fetches the initialized `PlayerModule` instance on RN's bridge object.
75
97
  private func getPlayerModule() -> PlayerModule? {
76
98
  bridge.module(for: PlayerModule.self) as? PlayerModule
package/lib/index.d.ts CHANGED
@@ -2174,14 +2174,29 @@ interface PlayerViewProps extends BasePlayerViewProps, PlayerViewEvents {
2174
2174
  * and render audio/video inside the `PlayerView`.
2175
2175
  */
2176
2176
  player: Player;
2177
+ /**
2178
+ * The `FullscreenHandler` that is used by the `PlayerView` to control the fullscreen mode.
2179
+ */
2177
2180
  fullscreenHandler?: FullscreenHandler;
2181
+ /**
2182
+ * The `CustomMessageHandler` that can be used to directly communicate with the embedded WebUi.
2183
+ */
2178
2184
  customMessageHandler?: CustomMessageHandler;
2185
+ /**
2186
+ * Can be set to `true` to request fullscreen mode, or `false` to request exit of fullscreen mode.
2187
+ * Should not be used to get the current fullscreen state. Use `onFullscreenEnter` and `onFullscreenExit`
2188
+ * or the `FullscreenHandler.isFullscreenActive` property to get the current state.
2189
+ * Using this property to change the fullscreen state, it is ensured that the embedded Player UI is also aware
2190
+ * of potential fullscreen state changes.
2191
+ * To use this property, a `FullscreenHandler` must be set.
2192
+ */
2193
+ isFullscreenRequested?: Boolean;
2179
2194
  }
2180
2195
  /**
2181
2196
  * Component that provides the Bitmovin Player UI and default UI handling to an attached `Player` instance.
2182
2197
  * This component needs a `Player` instance to work properly so make sure one is passed to it as a prop.
2183
2198
  */
2184
- declare function PlayerView({ style, player, fullscreenHandler, customMessageHandler, ...props }: PlayerViewProps): JSX.Element;
2199
+ declare function PlayerView({ style, player, fullscreenHandler, customMessageHandler, isFullscreenRequested, ...props }: PlayerViewProps): JSX.Element;
2185
2200
 
2186
2201
  /**
2187
2202
  * React hook that creates and returns a reference to a `Player` instance
package/lib/index.js CHANGED
@@ -186,6 +186,16 @@ var FullscreenHandlerBridge = class {
186
186
  );
187
187
  FullscreenHandlerModule.registerHandler(this.nativeId);
188
188
  }
189
+ setFullscreenHandler(fullscreenHandler) {
190
+ if (this.fullscreenHandler === fullscreenHandler) {
191
+ return;
192
+ }
193
+ this.fullscreenHandler = fullscreenHandler;
194
+ FullscreenHandlerModule.setIsFullscreenActive(
195
+ this.nativeId,
196
+ fullscreenHandler?.isFullscreenActive ?? false
197
+ );
198
+ }
189
199
  destroy() {
190
200
  if (!this.isDestroyed) {
191
201
  FullscreenHandlerModule.destroy(this.nativeId);
@@ -273,8 +283,19 @@ function PlayerView({
273
283
  player,
274
284
  fullscreenHandler,
275
285
  customMessageHandler,
286
+ isFullscreenRequested = false,
276
287
  ...props
277
288
  }) {
289
+ const fakeStateUpdater = (0, import_react2.useState)(1)[1];
290
+ const workaroundViewManagerCommandNotSent = (0, import_react2.useCallback)(() => {
291
+ setTimeout(
292
+ () => fakeStateUpdater((i) => {
293
+ console.log("Workaround #163");
294
+ return i + 1;
295
+ }),
296
+ 100
297
+ );
298
+ }, [fakeStateUpdater]);
278
299
  const nativeView = (0, import_react2.useRef)(null);
279
300
  const proxy = useProxy(nativeView);
280
301
  const nativeViewStyle = import_react_native8.StyleSheet.flatten([styles.baseStyle, style]);
@@ -283,7 +304,7 @@ function PlayerView({
283
304
  fullscreenBridge.current = new FullscreenHandlerBridge();
284
305
  }
285
306
  if (fullscreenBridge.current) {
286
- fullscreenBridge.current.fullscreenHandler = fullscreenHandler;
307
+ fullscreenBridge.current.setFullscreenHandler(fullscreenHandler);
287
308
  }
288
309
  const customMessageHandlerBridge = (0, import_react2.useRef)(void 0);
289
310
  if (customMessageHandler && !customMessageHandlerBridge.current) {
@@ -313,6 +334,7 @@ function PlayerView({
313
334
  fullscreenBridge.current.nativeId
314
335
  );
315
336
  }
337
+ workaroundViewManagerCommandNotSent();
316
338
  }
317
339
  return () => {
318
340
  fullscreenBridge.current?.destroy();
@@ -320,7 +342,13 @@ function PlayerView({
320
342
  customMessageHandlerBridge.current?.destroy();
321
343
  customMessageHandlerBridge.current = void 0;
322
344
  };
323
- }, [player]);
345
+ }, [player, workaroundViewManagerCommandNotSent]);
346
+ (0, import_react2.useEffect)(() => {
347
+ const node = (0, import_react_native8.findNodeHandle)(nativeView.current);
348
+ if (node) {
349
+ dispatch("setFullscreen", node, isFullscreenRequested);
350
+ }
351
+ }, [isFullscreenRequested, nativeView]);
324
352
  return /* @__PURE__ */ import_react2.default.createElement(NativePlayerView, {
325
353
  ref: nativeView,
326
354
  style: nativeViewStyle,
package/lib/index.mjs CHANGED
@@ -98,7 +98,7 @@ var AudioSession = {
98
98
  };
99
99
 
100
100
  // src/components/PlayerView/index.tsx
101
- import React, { useRef, useEffect } from "react";
101
+ import React, { useRef, useEffect, useState, useCallback as useCallback2 } from "react";
102
102
  import {
103
103
  Platform,
104
104
  UIManager,
@@ -147,6 +147,16 @@ var FullscreenHandlerBridge = class {
147
147
  );
148
148
  FullscreenHandlerModule.registerHandler(this.nativeId);
149
149
  }
150
+ setFullscreenHandler(fullscreenHandler) {
151
+ if (this.fullscreenHandler === fullscreenHandler) {
152
+ return;
153
+ }
154
+ this.fullscreenHandler = fullscreenHandler;
155
+ FullscreenHandlerModule.setIsFullscreenActive(
156
+ this.nativeId,
157
+ fullscreenHandler?.isFullscreenActive ?? false
158
+ );
159
+ }
150
160
  destroy() {
151
161
  if (!this.isDestroyed) {
152
162
  FullscreenHandlerModule.destroy(this.nativeId);
@@ -234,8 +244,19 @@ function PlayerView({
234
244
  player,
235
245
  fullscreenHandler,
236
246
  customMessageHandler,
247
+ isFullscreenRequested = false,
237
248
  ...props
238
249
  }) {
250
+ const fakeStateUpdater = useState(1)[1];
251
+ const workaroundViewManagerCommandNotSent = useCallback2(() => {
252
+ setTimeout(
253
+ () => fakeStateUpdater((i) => {
254
+ console.log("Workaround #163");
255
+ return i + 1;
256
+ }),
257
+ 100
258
+ );
259
+ }, [fakeStateUpdater]);
239
260
  const nativeView = useRef(null);
240
261
  const proxy = useProxy(nativeView);
241
262
  const nativeViewStyle = StyleSheet.flatten([styles.baseStyle, style]);
@@ -244,7 +265,7 @@ function PlayerView({
244
265
  fullscreenBridge.current = new FullscreenHandlerBridge();
245
266
  }
246
267
  if (fullscreenBridge.current) {
247
- fullscreenBridge.current.fullscreenHandler = fullscreenHandler;
268
+ fullscreenBridge.current.setFullscreenHandler(fullscreenHandler);
248
269
  }
249
270
  const customMessageHandlerBridge = useRef(void 0);
250
271
  if (customMessageHandler && !customMessageHandlerBridge.current) {
@@ -274,6 +295,7 @@ function PlayerView({
274
295
  fullscreenBridge.current.nativeId
275
296
  );
276
297
  }
298
+ workaroundViewManagerCommandNotSent();
277
299
  }
278
300
  return () => {
279
301
  fullscreenBridge.current?.destroy();
@@ -281,7 +303,13 @@ function PlayerView({
281
303
  customMessageHandlerBridge.current?.destroy();
282
304
  customMessageHandlerBridge.current = void 0;
283
305
  };
284
- }, [player]);
306
+ }, [player, workaroundViewManagerCommandNotSent]);
307
+ useEffect(() => {
308
+ const node = findNodeHandle2(nativeView.current);
309
+ if (node) {
310
+ dispatch("setFullscreen", node, isFullscreenRequested);
311
+ }
312
+ }, [isFullscreenRequested, nativeView]);
285
313
  return /* @__PURE__ */ React.createElement(NativePlayerView, {
286
314
  ref: nativeView,
287
315
  style: nativeViewStyle,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bitmovin-player-react-native",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "Official React Native bindings for Bitmovin's mobile Player SDKs.",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/index.mjs",
@@ -28,7 +28,9 @@
28
28
  "format": "prettier --write .",
29
29
  "build": "tsup ./src/index.ts --dts --target es2020 --format cjs,esm -d lib",
30
30
  "example": "yarn --cwd example",
31
- "pods": "cd example/ios && bundle install && bundle exec pod install --silent",
31
+ "pods": "yarn pods-install || yarn pods-install --repo-update || yarn pods-update",
32
+ "pods-install": "cd example/ios && bundle install && bundle exec pod install --silent",
33
+ "pods-update": "cd example/ios && bundle install && bundle exec pod update --silent",
32
34
  "bootstrap": "yarn && yarn example && yarn pods",
33
35
  "prepare": "husky install"
34
36
  },
@@ -1,4 +1,4 @@
1
- import React, { useRef, useEffect } from 'react';
1
+ import React, { useRef, useEffect, useState, useCallback } from 'react';
2
2
  import {
3
3
  Platform,
4
4
  UIManager,
@@ -35,9 +35,25 @@ export interface PlayerViewProps extends BasePlayerViewProps, PlayerViewEvents {
35
35
  */
36
36
  player: Player;
37
37
 
38
+ /**
39
+ * The `FullscreenHandler` that is used by the `PlayerView` to control the fullscreen mode.
40
+ */
38
41
  fullscreenHandler?: FullscreenHandler;
39
42
 
43
+ /**
44
+ * The `CustomMessageHandler` that can be used to directly communicate with the embedded WebUi.
45
+ */
40
46
  customMessageHandler?: CustomMessageHandler;
47
+
48
+ /**
49
+ * Can be set to `true` to request fullscreen mode, or `false` to request exit of fullscreen mode.
50
+ * Should not be used to get the current fullscreen state. Use `onFullscreenEnter` and `onFullscreenExit`
51
+ * or the `FullscreenHandler.isFullscreenActive` property to get the current state.
52
+ * Using this property to change the fullscreen state, it is ensured that the embedded Player UI is also aware
53
+ * of potential fullscreen state changes.
54
+ * To use this property, a `FullscreenHandler` must be set.
55
+ */
56
+ isFullscreenRequested?: Boolean;
41
57
  }
42
58
 
43
59
  /**
@@ -73,9 +89,20 @@ export function PlayerView({
73
89
  player,
74
90
  fullscreenHandler,
75
91
  customMessageHandler,
92
+ isFullscreenRequested = false,
76
93
  ...props
77
94
  }: PlayerViewProps) {
78
- // Native view reference.
95
+ const fakeStateUpdater = useState(1)[1];
96
+ const workaroundViewManagerCommandNotSent = useCallback(() => {
97
+ setTimeout(
98
+ () =>
99
+ fakeStateUpdater((i) => {
100
+ console.log('Workaround #163'); // Player will not load 1/10th if this log is removed :((((
101
+ return i + 1;
102
+ }),
103
+ 100
104
+ );
105
+ }, [fakeStateUpdater]);
79
106
  const nativeView = useRef(null);
80
107
  // Native events proxy helper.
81
108
  const proxy = useProxy(nativeView);
@@ -89,7 +116,7 @@ export function PlayerView({
89
116
  fullscreenBridge.current = new FullscreenHandlerBridge();
90
117
  }
91
118
  if (fullscreenBridge.current) {
92
- fullscreenBridge.current.fullscreenHandler = fullscreenHandler;
119
+ fullscreenBridge.current.setFullscreenHandler(fullscreenHandler);
93
120
  }
94
121
 
95
122
  const customMessageHandlerBridge: React.MutableRefObject<
@@ -127,14 +154,27 @@ export function PlayerView({
127
154
  fullscreenBridge.current.nativeId
128
155
  );
129
156
  }
157
+ // Workaround React Native Command not sent until UI refresh
158
+ // See: https://github.com/bitmovin/bitmovin-player-react-native/issues/163
159
+ // Seems to be a dup of https://github.com/microsoft/react-native-windows/issues/7543
160
+ // Remove the workaround when React Native is updated
161
+ workaroundViewManagerCommandNotSent();
130
162
  }
163
+
131
164
  return () => {
132
165
  fullscreenBridge.current?.destroy();
133
166
  fullscreenBridge.current = undefined;
134
167
  customMessageHandlerBridge.current?.destroy();
135
168
  customMessageHandlerBridge.current = undefined;
136
169
  };
137
- }, [player]);
170
+ }, [player, workaroundViewManagerCommandNotSent]);
171
+
172
+ useEffect(() => {
173
+ const node = findNodeHandle(nativeView.current);
174
+ if (node) {
175
+ dispatch('setFullscreen', node, isFullscreenRequested);
176
+ }
177
+ }, [isFullscreenRequested, nativeView]);
138
178
  return (
139
179
  <NativePlayerView
140
180
  ref={nativeView}
@@ -23,6 +23,20 @@ export class FullscreenHandlerBridge {
23
23
  FullscreenHandlerModule.registerHandler(this.nativeId);
24
24
  }
25
25
 
26
+ setFullscreenHandler(fullscreenHandler: FullscreenHandler | undefined) {
27
+ if (this.fullscreenHandler === fullscreenHandler) {
28
+ return;
29
+ }
30
+
31
+ this.fullscreenHandler = fullscreenHandler;
32
+
33
+ // synchronize current state from fullscreenHandler to native
34
+ FullscreenHandlerModule.setIsFullscreenActive(
35
+ this.nativeId,
36
+ fullscreenHandler?.isFullscreenActive ?? false
37
+ );
38
+ }
39
+
26
40
  /**
27
41
  * Destroys the native FullscreenHandler
28
42
  */