@jwplayer/jwplayer-react-native 1.3.2 → 1.3.4

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.
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
12
12
  s.platform = :ios, "15.0"
13
13
  s.source = { :git => "https://github.com/jwplayer/jwplayer-react-native.git", :tag => "v#{s.version}" }
14
14
  s.source_files = "ios/RNJWPlayer/*.{h,m,swift}"
15
- s.dependency 'JWPlayerKit', '4.25.0'
15
+ s.dependency 'JWPlayerKit', '4.25.1'
16
16
  s.dependency 'React-Core'
17
17
  s.static_framework = true
18
18
  s.info_plist = {
@@ -243,6 +243,9 @@ public class RNJWPlayerView extends RelativeLayout implements
243
243
  // Add completion handler field
244
244
  PlaylistItemDecision itemUpdatePromise = null;
245
245
 
246
+ // Flag to prevent race conditions during player destruction
247
+ private volatile boolean isDestroying = false;
248
+
246
249
  private void doBindService() {
247
250
  if (mMediaServiceController != null) {
248
251
  if (!isBackgroundAudioServiceRunning()) {
@@ -380,7 +383,22 @@ public class RNJWPlayerView extends RelativeLayout implements
380
383
  // }
381
384
 
382
385
  public void destroyPlayer() {
383
- if (mPlayer != null) {
386
+ if (mPlayer != null && !isDestroying) {
387
+ isDestroying = true;
388
+
389
+ // Disable touch events immediately to prevent race conditions
390
+ if (mPlayerView != null) {
391
+ Handler mainHandler = new Handler(Looper.getMainLooper());
392
+ mainHandler.post(() -> {
393
+ if (mPlayerView != null) {
394
+ mPlayerView.setClickable(false);
395
+ mPlayerView.setFocusable(false);
396
+ mPlayerView.setEnabled(false);
397
+ mPlayerView.setOnTouchListener(null);
398
+ }
399
+ });
400
+ }
401
+
384
402
  unRegisterReceiver();
385
403
 
386
404
  // If we are casting we need to break the cast session as there is no simple
@@ -480,6 +498,8 @@ public class RNJWPlayerView extends RelativeLayout implements
480
498
  audioManager = null;
481
499
 
482
500
  doUnbindService();
501
+
502
+ isDestroying = false; // Reset flag for potential reuse
483
503
  }
484
504
  }
485
505
 
@@ -1,5 +1,7 @@
1
1
  package com.jwplayer.rnjwplayer;
2
2
 
3
+ import android.util.Log;
4
+
3
5
  import com.facebook.react.bridge.ReactApplicationContext;
4
6
  import com.facebook.react.bridge.ReadableMap;
5
7
  import com.facebook.react.common.MapBuilder;
@@ -43,7 +45,14 @@ public class RNJWPlayerViewManager extends SimpleViewManager<RNJWPlayerView> {
43
45
  if (view == null || view.mPlayerView == null) {
44
46
  return;
45
47
  }
46
- view.mPlayerView.getPlayer().setControls(controls);
48
+ // Add null check for getPlayer() to prevent crashes
49
+ try {
50
+ if (view.mPlayerView.getPlayer() != null) {
51
+ view.mPlayerView.getPlayer().setControls(controls);
52
+ }
53
+ } catch (Exception e) {
54
+ Log.w(TAG, "Error setting controls: " + e.getMessage());
55
+ }
47
56
  }
48
57
 
49
58
  /**
@@ -58,8 +67,15 @@ public class RNJWPlayerViewManager extends SimpleViewManager<RNJWPlayerView> {
58
67
  if (view == null || view.mPlayerView == null) {
59
68
  return;
60
69
  }
61
- view.mPlayerView.getPlayer().stop();
62
- view.setConfig(config);
70
+ // Add null check for getPlayer() to prevent crashes
71
+ try {
72
+ if (view.mPlayerView.getPlayer() != null) {
73
+ view.mPlayerView.getPlayer().stop();
74
+ }
75
+ view.setConfig(config);
76
+ } catch (Exception e) {
77
+ Log.w(TAG, "Error recreating player: " + e.getMessage());
78
+ }
63
79
  }
64
80
 
65
81
  public Map getExportedCustomBubblingEventTypeConstants() {
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="20" role="img" aria-label="version: 1.3.2"><title>version: 1.3.2</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="51" height="20" fill="#555"/><rect x="51" width="39" height="20" fill="#007ec6"/><rect width="90" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="265" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="410">version</text><text x="265" y="140" transform="scale(.1)" fill="#fff" textLength="410">version</text><text aria-hidden="true" x="695" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="290">1.3.2</text><text x="695" y="140" transform="scale(.1)" fill="#fff" textLength="290">1.3.2</text></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="90" height="20" role="img" aria-label="version: 1.3.4"><title>version: 1.3.4</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="51" height="20" fill="#555"/><rect x="51" width="39" height="20" fill="#007ec6"/><rect width="90" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="265" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="410">version</text><text x="265" y="140" transform="scale(.1)" fill="#fff" textLength="410">version</text><text aria-hidden="true" x="695" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="290">1.3.4</text><text x="695" y="140" transform="scale(.1)" fill="#fff" textLength="290">1.3.4</text></g></svg>
@@ -1255,10 +1255,16 @@ class RNJWPlayerView: UIView, JWPlayerDelegate, JWPlayerStateDelegate,
1255
1255
  }
1256
1256
 
1257
1257
  func dismissPlayerViewController() {
1258
- if (playerViewController != nil) {
1259
- playerViewController.player.pause() // hack for stop not always stopping on unmount
1260
- playerViewController.player.stop()
1261
- playerViewController.enableLockScreenControls = false
1258
+ guard playerViewController != nil else { return }
1259
+
1260
+ // Ensure UI operations happen on main thread to prevent crashes
1261
+ // when deinit is called from a background thread during unmount
1262
+ let cleanup = { [self] in
1263
+ guard let pvc = self.playerViewController else { return }
1264
+
1265
+ pvc.player.pause() // hack for stop not always stopping on unmount
1266
+ pvc.player.stop()
1267
+ pvc.enableLockScreenControls = false
1262
1268
 
1263
1269
  // hack for stop not always stopping on unmount
1264
1270
  let configBuilder:JWPlayerConfigurationBuilder! = JWPlayerConfigurationBuilder()
@@ -1266,19 +1272,24 @@ class RNJWPlayerView: UIView, JWPlayerDelegate, JWPlayerStateDelegate,
1266
1272
 
1267
1273
  do {
1268
1274
  let configuration: JWPlayerConfiguration = try configBuilder.build()
1269
- playerViewController.player.configurePlayer(with: configuration)
1275
+ pvc.player.configurePlayer(with: configuration)
1270
1276
  } catch {
1271
1277
  print(error)
1272
1278
  }
1273
1279
 
1274
-
1275
- playerViewController.parentView = nil
1276
- playerViewController.setVisibility(.hidden, for:[.pictureInPictureButton])
1277
- playerViewController.view.removeFromSuperview()
1278
- playerViewController.removeFromParent()
1279
- playerViewController.willMove(toParent: nil)
1280
- playerViewController.removeDelegates()
1281
- playerViewController = nil
1280
+ pvc.parentView = nil
1281
+ pvc.setVisibility(.hidden, for:[.pictureInPictureButton])
1282
+ pvc.view.removeFromSuperview()
1283
+ pvc.removeFromParent()
1284
+ pvc.willMove(toParent: nil)
1285
+ pvc.removeDelegates()
1286
+ self.playerViewController = nil
1287
+ }
1288
+
1289
+ if Thread.isMainThread {
1290
+ cleanup()
1291
+ } else {
1292
+ DispatchQueue.main.sync(execute: cleanup)
1282
1293
  }
1283
1294
  }
1284
1295
 
@@ -1330,10 +1341,21 @@ class RNJWPlayerView: UIView, JWPlayerDelegate, JWPlayerStateDelegate,
1330
1341
  }
1331
1342
 
1332
1343
  func removePlayerView() {
1333
- if (playerView != nil) {
1334
- playerView.player.stop()
1335
- playerView.removeFromSuperview()
1336
- playerView = nil
1344
+ guard playerView != nil else { return }
1345
+
1346
+ // Ensure UI operations happen on main thread to prevent crashes
1347
+ // when deinit is called from a background thread during unmount
1348
+ let cleanup = { [self] in
1349
+ guard let pv = self.playerView else { return }
1350
+ pv.player.stop()
1351
+ pv.removeFromSuperview()
1352
+ self.playerView = nil
1353
+ }
1354
+
1355
+ if Thread.isMainThread {
1356
+ cleanup()
1357
+ } else {
1358
+ DispatchQueue.main.sync(execute: cleanup)
1337
1359
  }
1338
1360
  }
1339
1361
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jwplayer/jwplayer-react-native",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "React-native Android/iOS plugin for JWPlayer SDK (https://www.jwplayer.com/)",
5
5
  "main": "index.js",
6
6
  "types": "./index.d.ts",