@jwplayer/jwplayer-react-native 1.3.1 → 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.
- package/RNJWPlayer.podspec +1 -1
- package/android/src/ima/java/com/jwplayer/rnjwplayer/ImaHelper.java +0 -1
- package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java +21 -1
- package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerViewManager.java +19 -3
- package/badges/version.svg +1 -1
- package/ios/RNJWPlayer/RNJWPlayerView.swift +39 -17
- package/package.json +1 -1
package/RNJWPlayer.podspec
CHANGED
|
@@ -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.
|
|
15
|
+
s.dependency 'JWPlayerKit', '4.25.1'
|
|
16
16
|
s.dependency 'React-Core'
|
|
17
17
|
s.static_framework = true
|
|
18
18
|
s.info_plist = {
|
|
@@ -21,7 +21,6 @@ import java.util.Objects;
|
|
|
21
21
|
public class ImaHelper {
|
|
22
22
|
|
|
23
23
|
public static AdvertisingConfig configureImaOrDai(ReadableMap ads, List<AdBreak> adSchedule) {
|
|
24
|
-
cd /Users/jmilham/source/sdk/react/jwplayer-react-native/Example && yarn remove @jwplayer/jwplayer-react-native && yarn add file:../ 2>&1 | tail -20 // Check both "client" (JWPlayer JSON format) and "adClient" (RN wrapper format)
|
|
25
24
|
String adClientType = null;
|
|
26
25
|
if (ads.hasKey("adClient")) {
|
|
27
26
|
adClientType = ads.getString("adClient");
|
|
@@ -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
|
-
|
|
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
|
-
|
|
62
|
-
|
|
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() {
|
package/badges/version.svg
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
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
|
-
|
|
1275
|
+
pvc.player.configurePlayer(with: configuration)
|
|
1270
1276
|
} catch {
|
|
1271
1277
|
print(error)
|
|
1272
1278
|
}
|
|
1273
1279
|
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
playerViewController
|
|
1281
|
-
|
|
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
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
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
|
|