@jwplayer/jwplayer-react-native 1.0.2 → 1.0.3
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 +1 -0
- package/RNJWPlayer.podspec +3 -3
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerModule.java +510 -458
- package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java +141 -24
- package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerViewManager.java +8 -0
- package/android/src/main/java/com/jwplayer/rnjwplayer/Util.java +26 -7
- package/badges/version.svg +1 -1
- package/index.d.ts +36 -7
- package/index.js +18 -0
- package/ios/RNJWPlayer/RNJWPlayerView.swift +46 -33
- package/ios/RNJWPlayer/RNJWPlayerViewController.swift +14 -8
- package/ios/RNJWPlayer/RNJWPlayerViewManager.m +5 -1
- package/ios/RNJWPlayer/RNJWPlayerViewManager.swift +46 -12
- package/jwplayer-jwplayer-react-native-1.0.3.tgz +0 -0
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ package com.jwplayer.rnjwplayer;
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
import android.app.Activity;
|
|
5
|
+
import android.app.ActivityManager;
|
|
5
6
|
import android.content.BroadcastReceiver;
|
|
6
7
|
import android.content.Context;
|
|
7
8
|
import android.content.Intent;
|
|
@@ -18,16 +19,20 @@ import android.os.Build;
|
|
|
18
19
|
import android.os.Handler;
|
|
19
20
|
import android.os.Looper;
|
|
20
21
|
import android.util.Log;
|
|
21
|
-
import android.view.Choreographer;
|
|
22
22
|
import android.view.View;
|
|
23
23
|
import android.view.ViewGroup;
|
|
24
24
|
import android.view.Window;
|
|
25
25
|
import android.view.WindowManager;
|
|
26
|
-
import android.widget.FrameLayout;
|
|
27
26
|
import android.widget.LinearLayout;
|
|
28
27
|
import android.widget.RelativeLayout;
|
|
29
28
|
|
|
29
|
+
import androidx.annotation.NonNull;
|
|
30
30
|
import androidx.appcompat.app.AppCompatActivity;
|
|
31
|
+
import androidx.lifecycle.Lifecycle;
|
|
32
|
+
import androidx.lifecycle.LifecycleEventObserver;
|
|
33
|
+
import androidx.lifecycle.LifecycleObserver;
|
|
34
|
+
import androidx.lifecycle.LifecycleOwner;
|
|
35
|
+
import androidx.lifecycle.LifecycleRegistry;
|
|
31
36
|
|
|
32
37
|
import com.facebook.react.ReactActivity;
|
|
33
38
|
import com.facebook.react.bridge.Arguments;
|
|
@@ -35,15 +40,17 @@ import com.facebook.react.bridge.LifecycleEventListener;
|
|
|
35
40
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
36
41
|
import com.facebook.react.bridge.ReadableArray;
|
|
37
42
|
import com.facebook.react.bridge.ReadableMap;
|
|
43
|
+
import com.facebook.react.bridge.WritableArray;
|
|
38
44
|
import com.facebook.react.bridge.WritableMap;
|
|
39
45
|
import com.facebook.react.common.MapBuilder;
|
|
40
46
|
import com.facebook.react.uimanager.ThemedReactContext;
|
|
41
47
|
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
|
42
48
|
import com.google.common.collect.ImmutableMap;
|
|
43
49
|
import com.google.gson.Gson;
|
|
44
|
-
import com.jwplayer.pub.api.JsonHelper;
|
|
45
50
|
import com.jwplayer.pub.api.JWPlayer;
|
|
51
|
+
import com.jwplayer.pub.api.JsonHelper;
|
|
46
52
|
import com.jwplayer.pub.api.UiGroup;
|
|
53
|
+
import com.jwplayer.pub.api.background.MediaService;
|
|
47
54
|
import com.jwplayer.pub.api.background.MediaServiceController;
|
|
48
55
|
import com.jwplayer.pub.api.configuration.PlayerConfig;
|
|
49
56
|
import com.jwplayer.pub.api.configuration.UiConfig;
|
|
@@ -110,9 +117,12 @@ import com.jwplayer.pub.api.fullscreen.delegates.DeviceOrientationDelegate;
|
|
|
110
117
|
import com.jwplayer.pub.api.fullscreen.delegates.DialogLayoutDelegate;
|
|
111
118
|
import com.jwplayer.pub.api.fullscreen.delegates.SystemUiDelegate;
|
|
112
119
|
import com.jwplayer.pub.api.license.LicenseUtil;
|
|
120
|
+
import com.jwplayer.pub.api.media.captions.Caption;
|
|
113
121
|
import com.jwplayer.pub.api.media.playlists.PlaylistItem;
|
|
114
122
|
import com.jwplayer.ui.views.CueMarkerSeekbar;
|
|
115
123
|
|
|
124
|
+
import org.json.JSONObject;
|
|
125
|
+
|
|
116
126
|
import java.util.ArrayList;
|
|
117
127
|
import java.util.Arrays;
|
|
118
128
|
import java.util.HashMap;
|
|
@@ -120,8 +130,6 @@ import java.util.List;
|
|
|
120
130
|
import java.util.Map;
|
|
121
131
|
import java.util.Objects;
|
|
122
132
|
|
|
123
|
-
import org.json.JSONObject;
|
|
124
|
-
|
|
125
133
|
public class RNJWPlayerView extends RelativeLayout implements
|
|
126
134
|
VideoPlayerEvents.OnFullscreenListener,
|
|
127
135
|
VideoPlayerEvents.OnReadyListener,
|
|
@@ -179,7 +187,7 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
179
187
|
|
|
180
188
|
AudioManager.OnAudioFocusChangeListener,
|
|
181
189
|
|
|
182
|
-
LifecycleEventListener {
|
|
190
|
+
LifecycleEventListener, LifecycleOwner {
|
|
183
191
|
public RNJWPlayer mPlayerView = null;
|
|
184
192
|
public JWPlayer mPlayer = null;
|
|
185
193
|
|
|
@@ -230,7 +238,13 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
230
238
|
|
|
231
239
|
private void doBindService() {
|
|
232
240
|
if (mMediaServiceController != null) {
|
|
233
|
-
|
|
241
|
+
if (!isBackgroundAudioServiceRunning()) {
|
|
242
|
+
// This may not be your expected behavior, but is necessary to avoid crashing
|
|
243
|
+
// Do not use multiple player instances with background audio enabled
|
|
244
|
+
|
|
245
|
+
// don't rebind me if the service is already active with a player.
|
|
246
|
+
mMediaServiceController.bindService();
|
|
247
|
+
}
|
|
234
248
|
}
|
|
235
249
|
}
|
|
236
250
|
|
|
@@ -265,10 +279,23 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
265
279
|
return superContext;
|
|
266
280
|
}
|
|
267
281
|
|
|
282
|
+
private boolean isBackgroundAudioServiceRunning() {
|
|
283
|
+
ActivityManager manager = (ActivityManager) mAppContext.getSystemService(Context.ACTIVITY_SERVICE);
|
|
284
|
+
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
|
|
285
|
+
if (MediaService.class.getName().equals(service.service.getClassName())) {
|
|
286
|
+
Log.w(TAG, "MediaService is already running with another player loaded. To avoid crashing, this player, "
|
|
287
|
+
+ mPlayerView.getTag() + " will not be loaded into the background service.");
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
|
|
268
294
|
public RNJWPlayerView(ThemedReactContext reactContext, ReactApplicationContext appContext) {
|
|
269
295
|
super(getNonBuggyContext(reactContext, appContext));
|
|
270
296
|
mAppContext = appContext;
|
|
271
297
|
|
|
298
|
+
registry.setCurrentState(Lifecycle.State.CREATED);
|
|
272
299
|
mThemedReactContext = reactContext;
|
|
273
300
|
|
|
274
301
|
mActivity = (ReactActivity) getActivity();
|
|
@@ -276,11 +303,25 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
276
303
|
mWindow = mActivity.getWindow();
|
|
277
304
|
}
|
|
278
305
|
|
|
306
|
+
if (mActivity != null) {
|
|
307
|
+
mActivity.getLifecycle().addObserver(lifecycleObserver);
|
|
308
|
+
}
|
|
309
|
+
|
|
279
310
|
mRootView = mActivity.findViewById(android.R.id.content);
|
|
280
311
|
|
|
281
312
|
getReactContext().addLifecycleEventListener(this);
|
|
282
313
|
}
|
|
283
314
|
|
|
315
|
+
private LifecycleObserver lifecycleObserver = new LifecycleEventObserver() {
|
|
316
|
+
@Override
|
|
317
|
+
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
|
|
318
|
+
if (event.getTargetState() == Lifecycle.State.DESTROYED) {
|
|
319
|
+
return; // no op: handled elsewhere
|
|
320
|
+
}
|
|
321
|
+
registry.setCurrentState(event.getTargetState());
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
|
|
284
325
|
public ReactApplicationContext getAppContext() {
|
|
285
326
|
return mAppContext;
|
|
286
327
|
}
|
|
@@ -303,12 +344,41 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
303
344
|
return mThemedReactContext.getReactApplicationContext().getCurrentActivity();
|
|
304
345
|
}
|
|
305
346
|
|
|
347
|
+
// The registry for lifecycle events. Required by player object. Main use case if for garbage collection / teardown
|
|
348
|
+
private final LifecycleRegistry registry = new LifecycleRegistry(this);
|
|
349
|
+
|
|
350
|
+
@NonNull
|
|
351
|
+
@Override
|
|
352
|
+
public Lifecycle getLifecycle() {
|
|
353
|
+
return registry;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// // closest to `ondestroy` for a view without listening to the activity event
|
|
357
|
+
// // The activity event can be deceptive in React-Native
|
|
358
|
+
// @Override
|
|
359
|
+
// protected void onDetachedFromWindow() {
|
|
360
|
+
// super.onDetachedFromWindow();
|
|
361
|
+
// registry.setCurrentState(Lifecycle.State.DESTROYED);
|
|
362
|
+
// }
|
|
363
|
+
|
|
306
364
|
public void destroyPlayer() {
|
|
307
365
|
if (mPlayer != null) {
|
|
308
366
|
unRegisterReceiver();
|
|
309
|
-
|
|
367
|
+
|
|
368
|
+
// If we are casting we need to break the cast session as there is no simple
|
|
369
|
+
// way to reconnect to an existing session if the player that created it is dead
|
|
370
|
+
|
|
371
|
+
// If this doesn't match your use case, using a single player object and load content
|
|
372
|
+
// into it rather than creating a new player for every piece of content.
|
|
310
373
|
mPlayer.stop();
|
|
311
374
|
|
|
375
|
+
// send signal to JW SDK player is destroyed
|
|
376
|
+
registry.setCurrentState(Lifecycle.State.DESTROYED);
|
|
377
|
+
|
|
378
|
+
// Stop listening to activities lifecycle
|
|
379
|
+
mActivity.getLifecycle().removeObserver(lifecycleObserver);
|
|
380
|
+
mPlayer.deregisterActivityForPip();
|
|
381
|
+
|
|
312
382
|
mPlayer.removeListeners(this,
|
|
313
383
|
// VideoPlayerEvents
|
|
314
384
|
EventType.READY,
|
|
@@ -456,6 +526,7 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
456
526
|
|
|
457
527
|
/**
|
|
458
528
|
* Helper to build the a generic `ExtensibleFullscreenHandler` with small tweaks to play nice with Modals
|
|
529
|
+
*
|
|
459
530
|
* @return {@link ExtensibleFullscreenHandler}
|
|
460
531
|
*/
|
|
461
532
|
private ExtensibleFullscreenHandler createModalFullscreenHandler() {
|
|
@@ -622,6 +693,9 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
622
693
|
mPlayerViewContainer.post(new Runnable() {
|
|
623
694
|
@Override
|
|
624
695
|
public void run() {
|
|
696
|
+
// View may not have been removed properly (especially if returning from PiP)
|
|
697
|
+
mPlayerViewContainer.removeView(mPlayerView);
|
|
698
|
+
|
|
625
699
|
mPlayerViewContainer.addView(mPlayerView, new ViewGroup.LayoutParams(
|
|
626
700
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
627
701
|
ViewGroup.LayoutParams.MATCH_PARENT));
|
|
@@ -714,6 +788,11 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
714
788
|
mPlayer.setForceControlsVisibility(true);
|
|
715
789
|
mPlayer.setForceControlsVisibility(false);
|
|
716
790
|
|
|
791
|
+
// If player was in fullscreen when going into PiP, we need to force it back out
|
|
792
|
+
if (mPlayer.getFullscreen()) {
|
|
793
|
+
mPlayer.setFullscreen(false, true);
|
|
794
|
+
}
|
|
795
|
+
|
|
717
796
|
// Strip player view
|
|
718
797
|
rootView.removeView(mPlayerView);
|
|
719
798
|
|
|
@@ -966,6 +1045,11 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
966
1045
|
LinearLayout.LayoutParams.MATCH_PARENT));
|
|
967
1046
|
addView(mPlayerView);
|
|
968
1047
|
|
|
1048
|
+
// Ensure we have a valid state before applying to the player
|
|
1049
|
+
registry.setCurrentState(Lifecycle.State.STARTED);
|
|
1050
|
+
|
|
1051
|
+
mPlayer = mPlayerView.getPlayer(this);
|
|
1052
|
+
|
|
969
1053
|
if (prop.hasKey("controls")) { // Hack for controls hiding not working right away
|
|
970
1054
|
mPlayerView.getPlayer().setControls(prop.getBoolean("controls"));
|
|
971
1055
|
}
|
|
@@ -992,8 +1076,6 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
992
1076
|
mPlayerView.exitFullScreenOnPortrait = exitFullScreenOnPortrait;
|
|
993
1077
|
}
|
|
994
1078
|
|
|
995
|
-
mPlayer = mPlayerView.getPlayer();
|
|
996
|
-
|
|
997
1079
|
if (isJwConfig) {
|
|
998
1080
|
mPlayer.setup(jwConfig);
|
|
999
1081
|
} else {
|
|
@@ -1015,7 +1097,7 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
1015
1097
|
// Legacy
|
|
1016
1098
|
// This isn't the ideal way to do this on Android. All drawables/colors/themes shoudld
|
|
1017
1099
|
// be targed using styling. See `https://docs.jwplayer.com/players/docs/android-styling-guide`
|
|
1018
|
-
// for more information on how best to override the JWP styles using XML. If you are unsure of a
|
|
1100
|
+
// for more information on how best to override the JWP styles using XML. If you are unsure of a
|
|
1019
1101
|
// color/drawable/theme, open an `Ask` issue.
|
|
1020
1102
|
if (mColors != null) {
|
|
1021
1103
|
if (mColors.hasKey("backgroundColor")) {
|
|
@@ -1072,8 +1154,6 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
1072
1154
|
|
|
1073
1155
|
if (backgroundAudioEnabled) {
|
|
1074
1156
|
audioManager = (AudioManager) simpleContext.getSystemService(Context.AUDIO_SERVICE);
|
|
1075
|
-
// Throws a fatal error if using a playlistURL instead of manually created playlist
|
|
1076
|
-
// Related to SDK-11346
|
|
1077
1157
|
mMediaServiceController = new MediaServiceController.Builder((AppCompatActivity) mActivity, mPlayer)
|
|
1078
1158
|
.build();
|
|
1079
1159
|
}
|
|
@@ -1331,7 +1411,7 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
1331
1411
|
event.putString("message", "onPlayerAdWarning");
|
|
1332
1412
|
event.putInt("code", adWarningEvent.getCode());
|
|
1333
1413
|
event.putInt("adErrorCode", adWarningEvent.getAdErrorCode());
|
|
1334
|
-
event.putString("
|
|
1414
|
+
event.putString("warning", adWarningEvent.getMessage());
|
|
1335
1415
|
getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topPlayerAdWarning", event);
|
|
1336
1416
|
}
|
|
1337
1417
|
|
|
@@ -1437,6 +1517,38 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
1437
1517
|
|
|
1438
1518
|
}
|
|
1439
1519
|
|
|
1520
|
+
// Captions Events
|
|
1521
|
+
|
|
1522
|
+
@Override
|
|
1523
|
+
public void onCaptionsChanged(CaptionsChangedEvent captionsChangedEvent) {
|
|
1524
|
+
WritableMap event = Arguments.createMap();
|
|
1525
|
+
event.putString("message", "onCaptionsChanged");
|
|
1526
|
+
event.putInt("index", captionsChangedEvent.getCurrentTrack());
|
|
1527
|
+
getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topCaptionsChanged", event);
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
@Override
|
|
1531
|
+
public void onCaptionsList(CaptionsListEvent captionsListEvent) {
|
|
1532
|
+
WritableMap event = Arguments.createMap();
|
|
1533
|
+
List<Caption> captionTrackList = captionsListEvent.getCaptions();
|
|
1534
|
+
WritableArray captionTracks = Arguments.createArray();
|
|
1535
|
+
if (captionTrackList != null) {
|
|
1536
|
+
for(int i = 0; i < captionTrackList.size(); i++) {
|
|
1537
|
+
WritableMap captionTrack = Arguments.createMap();
|
|
1538
|
+
Caption track = captionTrackList.get(i);
|
|
1539
|
+
captionTrack.putString("file", track.getFile());
|
|
1540
|
+
captionTrack.putString("label", track.getLabel());
|
|
1541
|
+
captionTrack.putBoolean("default", track.isDefault());
|
|
1542
|
+
captionTracks.pushMap(captionTrack);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
event.putString("message", "onCaptionsList");
|
|
1546
|
+
event.putInt("index", captionsListEvent.getCurrentCaptionIndex());
|
|
1547
|
+
event.putArray("tracks", captionTracks);
|
|
1548
|
+
getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topCaptionsList", event);
|
|
1549
|
+
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1440
1552
|
// Player Events
|
|
1441
1553
|
|
|
1442
1554
|
@Override
|
|
@@ -1485,6 +1597,7 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
1485
1597
|
if (ex != null) {
|
|
1486
1598
|
event.putString("error", ex.toString());
|
|
1487
1599
|
event.putString("description", errorEvent.getMessage());
|
|
1600
|
+
event.putInt("errorCode", errorEvent.getErrorCode());
|
|
1488
1601
|
}
|
|
1489
1602
|
getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topPlayerError", event);
|
|
1490
1603
|
|
|
@@ -1635,6 +1748,8 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
1635
1748
|
public void onSetupError(SetupErrorEvent setupErrorEvent) {
|
|
1636
1749
|
WritableMap event = Arguments.createMap();
|
|
1637
1750
|
event.putString("message", "onSetupError");
|
|
1751
|
+
event.putString("errorMessage", setupErrorEvent.getMessage());
|
|
1752
|
+
event.putInt("errorCode", setupErrorEvent.getCode());
|
|
1638
1753
|
getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topSetupPlayerError", event);
|
|
1639
1754
|
|
|
1640
1755
|
updateWakeLock(false);
|
|
@@ -1649,16 +1764,6 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
1649
1764
|
getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topTime", event);
|
|
1650
1765
|
}
|
|
1651
1766
|
|
|
1652
|
-
@Override
|
|
1653
|
-
public void onCaptionsChanged(CaptionsChangedEvent captionsChangedEvent) {
|
|
1654
|
-
|
|
1655
|
-
}
|
|
1656
|
-
|
|
1657
|
-
@Override
|
|
1658
|
-
public void onCaptionsList(CaptionsListEvent captionsListEvent) {
|
|
1659
|
-
|
|
1660
|
-
}
|
|
1661
|
-
|
|
1662
1767
|
@Override
|
|
1663
1768
|
public void onMeta(MetaEvent metaEvent) {
|
|
1664
1769
|
|
|
@@ -1678,6 +1783,17 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
1678
1783
|
|
|
1679
1784
|
// Casting events
|
|
1680
1785
|
|
|
1786
|
+
private boolean mIsCastActive = false;
|
|
1787
|
+
|
|
1788
|
+
/**
|
|
1789
|
+
* Get if this player-view is currently casting
|
|
1790
|
+
*
|
|
1791
|
+
* @return true if casting
|
|
1792
|
+
*/
|
|
1793
|
+
public boolean getIsCastActive() {
|
|
1794
|
+
return mIsCastActive;
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1681
1797
|
@Override
|
|
1682
1798
|
public void onCast(CastEvent castEvent) {
|
|
1683
1799
|
WritableMap event = Arguments.createMap();
|
|
@@ -1686,6 +1802,7 @@ public class RNJWPlayerView extends RelativeLayout implements
|
|
|
1686
1802
|
event.putBoolean("active", castEvent.isActive());
|
|
1687
1803
|
event.putBoolean("available", castEvent.isAvailable());
|
|
1688
1804
|
getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "topCasting", event);
|
|
1805
|
+
mIsCastActive = castEvent.isActive();
|
|
1689
1806
|
// stop/start the background audio service if it's running and we're casting
|
|
1690
1807
|
if (castEvent.isActive()) {
|
|
1691
1808
|
doUnbindService();
|
|
@@ -155,6 +155,14 @@ public class RNJWPlayerViewManager extends SimpleViewManager<RNJWPlayerView> {
|
|
|
155
155
|
MapBuilder.of(
|
|
156
156
|
"phasedRegistrationNames",
|
|
157
157
|
MapBuilder.of("bubbled", "onAudioTracks")))
|
|
158
|
+
.put("topCaptionsChanged",
|
|
159
|
+
MapBuilder.of(
|
|
160
|
+
"phasedRegistrationNames",
|
|
161
|
+
MapBuilder.of("bubbled", "onCaptionsChanged")))
|
|
162
|
+
.put("topCaptionsList",
|
|
163
|
+
MapBuilder.of(
|
|
164
|
+
"phasedRegistrationNames",
|
|
165
|
+
MapBuilder.of("bubbled", "onCaptionsList")))
|
|
158
166
|
.put("topCasting",
|
|
159
167
|
MapBuilder.of(
|
|
160
168
|
"phasedRegistrationNames",
|
|
@@ -2,17 +2,21 @@ package com.jwplayer.rnjwplayer;
|
|
|
2
2
|
|
|
3
3
|
import static androidx.media3.common.util.Util.toByteArray;
|
|
4
4
|
|
|
5
|
+
import android.util.Log;
|
|
5
6
|
import android.util.Patterns;
|
|
6
7
|
import android.webkit.URLUtil;
|
|
7
8
|
|
|
8
9
|
import com.facebook.react.bridge.ReadableArray;
|
|
9
10
|
import com.facebook.react.bridge.ReadableMap;
|
|
11
|
+
import com.jwplayer.pub.api.JsonHelper;
|
|
10
12
|
import com.jwplayer.pub.api.media.ads.AdBreak;
|
|
11
13
|
import com.jwplayer.pub.api.media.captions.Caption;
|
|
12
14
|
import com.jwplayer.pub.api.media.captions.CaptionType;
|
|
13
15
|
import com.jwplayer.pub.api.media.playlists.MediaSource;
|
|
14
16
|
import com.jwplayer.pub.api.media.playlists.PlaylistItem;
|
|
15
17
|
|
|
18
|
+
import org.json.JSONObject;
|
|
19
|
+
|
|
16
20
|
import java.io.IOException;
|
|
17
21
|
import java.io.InputStream;
|
|
18
22
|
import java.io.OutputStream;
|
|
@@ -20,8 +24,8 @@ import java.net.HttpURLConnection;
|
|
|
20
24
|
import java.net.URL;
|
|
21
25
|
import java.util.ArrayList;
|
|
22
26
|
import java.util.List;
|
|
23
|
-
import java.util.Map;
|
|
24
27
|
import java.util.Locale;
|
|
28
|
+
import java.util.Map;
|
|
25
29
|
|
|
26
30
|
public class Util {
|
|
27
31
|
|
|
@@ -62,7 +66,7 @@ public class Util {
|
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
public static boolean isValidURL(String url){
|
|
69
|
+
public static boolean isValidURL(String url) {
|
|
66
70
|
return URLUtil.isValidUrl(url) && Patterns.WEB_URL.matcher(url).matches();
|
|
67
71
|
}
|
|
68
72
|
|
|
@@ -75,14 +79,28 @@ public class Util {
|
|
|
75
79
|
while (playlistItems.size() > j) {
|
|
76
80
|
ReadableMap playlistItem = playlistItems.getMap(j);
|
|
77
81
|
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
JSONObject obj;
|
|
83
|
+
PlaylistItem item = null;
|
|
84
|
+
// Try since legacy config may or may not conform to this standard
|
|
85
|
+
try {
|
|
86
|
+
obj = MapUtil.toJSONObject(playlistItem);
|
|
87
|
+
item = JsonHelper.parsePlaylistItemJson(obj);
|
|
88
|
+
} catch (Exception ex) {
|
|
89
|
+
Log.e("createPlaylist", ex.toString());
|
|
90
|
+
}
|
|
91
|
+
if (item != null) {
|
|
92
|
+
playlist.add(item);
|
|
93
|
+
} else {
|
|
94
|
+
// Try to use the legacy format
|
|
95
|
+
PlaylistItem newPlayListItem = getPlaylistItem((playlistItem));
|
|
96
|
+
playlist.add(newPlayListItem);
|
|
97
|
+
}
|
|
80
98
|
j++;
|
|
81
99
|
}
|
|
82
100
|
return playlist;
|
|
83
101
|
}
|
|
84
102
|
|
|
85
|
-
public static PlaylistItem getPlaylistItem
|
|
103
|
+
public static PlaylistItem getPlaylistItem(ReadableMap playlistItem) {
|
|
86
104
|
PlaylistItem.Builder itemBuilder = new PlaylistItem.Builder();
|
|
87
105
|
|
|
88
106
|
if (playlistItem.hasKey("file")) {
|
|
@@ -194,11 +212,12 @@ public class Util {
|
|
|
194
212
|
|
|
195
213
|
/**
|
|
196
214
|
* Internal helper for parsing a caption type from a known string
|
|
215
|
+
*
|
|
197
216
|
* @param type one of "CAPTIONS", "CHAPTERS", "THUMBNAILS"
|
|
198
217
|
* @return the correct Enum CaptionType
|
|
199
218
|
*/
|
|
200
|
-
public static CaptionType getCaptionType(String type){
|
|
201
|
-
for (CaptionType captionType: CaptionType.values()) {
|
|
219
|
+
public static CaptionType getCaptionType(String type) {
|
|
220
|
+
for (CaptionType captionType : CaptionType.values()) {
|
|
202
221
|
if (captionType.name().equals(type)) {
|
|
203
222
|
return CaptionType.valueOf(type);
|
|
204
223
|
}
|
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.0.
|
|
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.0.3"><title>version: 1.0.3</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.0.3</text><text x="695" y="140" transform="scale(.1)" fill="#fff" textLength="290">1.0.3</text></g></svg>
|
package/index.d.ts
CHANGED
|
@@ -26,7 +26,13 @@ declare module "@jwplayer/jwplayer-react-native" {
|
|
|
26
26
|
advertising?: JwAdvertisingConfig;
|
|
27
27
|
playbackRates?: number[];
|
|
28
28
|
playbackRateControls?: boolean;
|
|
29
|
+
// Non-Json Parsing props
|
|
29
30
|
license: string;
|
|
31
|
+
playerInModal?: boolean;
|
|
32
|
+
fullScreenOnLandscape?: boolean;
|
|
33
|
+
landscapeOnFullScreen?: boolean;
|
|
34
|
+
portraitOnExitFullScreen?: boolean;
|
|
35
|
+
exitFullScreenOnPortrait?: boolean;
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
type JwThumbnailPreview = 101 | 102 | 103;
|
|
@@ -477,6 +483,15 @@ declare module "@jwplayer/jwplayer-react-native" {
|
|
|
477
483
|
rate: number;
|
|
478
484
|
at: number;
|
|
479
485
|
}
|
|
486
|
+
interface PlayerSetupErrorProps {
|
|
487
|
+
errorMessage?: string;
|
|
488
|
+
errorCode?: number;
|
|
489
|
+
}
|
|
490
|
+
interface PlayerErrorProps {
|
|
491
|
+
error?: string;
|
|
492
|
+
errorCode?: number;
|
|
493
|
+
description?: string; // Android Only
|
|
494
|
+
}
|
|
480
495
|
interface TimeEventProps {
|
|
481
496
|
position: number;
|
|
482
497
|
duration: number;
|
|
@@ -498,15 +513,26 @@ declare module "@jwplayer/jwplayer-react-native" {
|
|
|
498
513
|
error: string;
|
|
499
514
|
}
|
|
500
515
|
interface PlayerWarningEventProps {
|
|
501
|
-
code
|
|
502
|
-
warning
|
|
516
|
+
code?: number;
|
|
517
|
+
warning?: string;
|
|
518
|
+
adErrorCode?: number; // Android only
|
|
503
519
|
}
|
|
504
520
|
interface AdEventProps {
|
|
505
521
|
client?: string;
|
|
506
522
|
reason?: string;
|
|
507
523
|
type: number;
|
|
508
524
|
}
|
|
509
|
-
type
|
|
525
|
+
// Overloaded type to be used in multiple error events
|
|
526
|
+
interface CaptionsChangedEventProps {
|
|
527
|
+
index?: number;
|
|
528
|
+
}
|
|
529
|
+
interface CaptionsListEventProps {
|
|
530
|
+
index: number;
|
|
531
|
+
file?: string;
|
|
532
|
+
label: string;
|
|
533
|
+
default: string;
|
|
534
|
+
}
|
|
535
|
+
type NativeError = (event: BaseEvent<PlayerErrorEventProps> | BaseEvent<PlayerSetupErrorProps> | BaseEvent<PlayerErrorProps>) => void;
|
|
510
536
|
type NativeWarning = (event: BaseEvent<PlayerWarningEventProps>) => void;
|
|
511
537
|
interface PropsType {
|
|
512
538
|
config: Config | JwConfig;
|
|
@@ -526,9 +552,9 @@ declare module "@jwplayer/jwplayer-react-native" {
|
|
|
526
552
|
onRateChanged?: (event?: BaseEvent<RateChangedEventProps>) => void;
|
|
527
553
|
onSetupPlayerError?: NativeError;
|
|
528
554
|
onPlayerError?: NativeError;
|
|
529
|
-
onPlayerWarning?: NativeWarning;
|
|
530
|
-
onPlayerAdError?: NativeError;
|
|
531
|
-
onPlayerAdWarning?: NativeWarning;
|
|
555
|
+
onPlayerWarning?: NativeWarning;
|
|
556
|
+
onPlayerAdError?: NativeError;
|
|
557
|
+
onPlayerAdWarning?: NativeWarning;
|
|
532
558
|
onAdEvent?: (event: BaseEvent<AdEventProps>) => void;
|
|
533
559
|
onAdTime?: (event: BaseEvent<TimeEventProps>) => void;
|
|
534
560
|
onBuffer?: () => void;
|
|
@@ -540,6 +566,8 @@ declare module "@jwplayer/jwplayer-react-native" {
|
|
|
540
566
|
onControlBarVisible?: (event: BaseEvent<ControlBarVisibleEventProps>) => void;
|
|
541
567
|
onPlaylistComplete?: () => void;
|
|
542
568
|
onPlaylistItem?: (event: BaseEvent<PlaylistItemEventProps>) => void;
|
|
569
|
+
onCaptionsChanged?: (event: BaseEvent<CaptionsChangedEventProps>) => void;
|
|
570
|
+
onCaptionsList?: (event: BaseEvent<CaptionsListEventProps>) => void;
|
|
543
571
|
onAudioTracks?: () => void;
|
|
544
572
|
shouldComponentUpdate?: (nextProps: any, nextState: any) => boolean;
|
|
545
573
|
}
|
|
@@ -558,7 +586,7 @@ declare module "@jwplayer/jwplayer-react-native" {
|
|
|
558
586
|
setControls(show: boolean): void;
|
|
559
587
|
setLockScreenControls(show: boolean): void;
|
|
560
588
|
seekTo(time: number): void;
|
|
561
|
-
loadPlaylist(playlistItems: PlaylistItem[]): void;
|
|
589
|
+
loadPlaylist(playlistItems: PlaylistItem[] | JwPlaylistItem[] | string): void;
|
|
562
590
|
setFullscreen(fullScreen: boolean): void;
|
|
563
591
|
position(): Promise<number>;
|
|
564
592
|
setUpCastController(): void;
|
|
@@ -571,6 +599,7 @@ declare module "@jwplayer/jwplayer-react-native" {
|
|
|
571
599
|
getCurrentAudioTrack(): Promise<number | null>;
|
|
572
600
|
setCurrentAudioTrack(index: number): void;
|
|
573
601
|
setCurrentCaptions(index: number): void;
|
|
602
|
+
getCurrentCaptions(): Promise<number | null>;
|
|
574
603
|
setVisibility(visibility: boolean, controls: JWControlType[]): void;
|
|
575
604
|
}
|
|
576
605
|
}
|
package/index.js
CHANGED
|
@@ -370,6 +370,9 @@ export default class JWPlayer extends Component {
|
|
|
370
370
|
getCurrentAudioTrack: PropTypes.func,
|
|
371
371
|
setCurrentAudioTrack: PropTypes.func,
|
|
372
372
|
setCurrentCaptions: PropTypes.func,
|
|
373
|
+
getCurrentCaptions: PropTypes.func,
|
|
374
|
+
onCaptionsChanged: PropTypes.func,
|
|
375
|
+
onCaptionsList: PropTypes.func,
|
|
373
376
|
onAudioTracks: PropTypes.func,
|
|
374
377
|
};
|
|
375
378
|
|
|
@@ -688,6 +691,21 @@ export default class JWPlayer extends Component {
|
|
|
688
691
|
);
|
|
689
692
|
}
|
|
690
693
|
}
|
|
694
|
+
|
|
695
|
+
async getCurrentCaptions() {
|
|
696
|
+
if (RNJWPlayerManager) {
|
|
697
|
+
try {
|
|
698
|
+
var currentCaptionTrack =
|
|
699
|
+
await RNJWPlayerManager.getCurrentCaptions(
|
|
700
|
+
this.getRNJWPlayerBridgeHandle()
|
|
701
|
+
);
|
|
702
|
+
return currentCaptionTrack;
|
|
703
|
+
} catch (e) {
|
|
704
|
+
console.error(e);
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
691
709
|
|
|
692
710
|
getRNJWPlayerBridgeHandle() {
|
|
693
711
|
return findNodeHandle(this[this.ref_key]);
|