@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.
@@ -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
- mMediaServiceController.bindService();
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
- mPlayer.deregisterActivityForPip();
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("error", adWarningEvent.getMessage());
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
- PlaylistItem newPlayListItem = getPlaylistItem((playlistItem));
79
- playlist.add(newPlayListItem);
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 (ReadableMap playlistItem) {
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
  }
@@ -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.2"><title>version: 1.0.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.0.2</text><text x="695" y="140" transform="scale(.1)" fill="#fff" textLength="290">1.0.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.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: string;
502
- warning: string;
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 NativeError = (event: BaseEvent<PlayerErrorEventProps>) => void;
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]);