@jwplayer/jwplayer-react-native 1.0.1 → 1.0.2

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.
Files changed (55) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +10 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +10 -0
  3. package/.github/ISSUE_TEMPLATE/implement.md +9 -0
  4. package/.github/ISSUE_TEMPLATE/question.md +10 -0
  5. package/README.md +1 -0
  6. package/RNJWPlayer.podspec +2 -2
  7. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  8. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  9. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  10. package/android/.gradle/buildOutputCleanup/cache.properties +2 -2
  11. package/android/build.gradle +1 -1
  12. package/android/src/main/AndroidManifest.xml +2 -0
  13. package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java +246 -11
  14. package/android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerViewManager.java +4 -0
  15. package/badges/version.svg +1 -1
  16. package/index.d.ts +5 -0
  17. package/index.js +4 -2
  18. package/ios/RNJWPlayer/RNJWPlayerView.swift +1 -1
  19. package/ios/RNJWPlayer/RNJWPlayerViewController.swift +1 -1
  20. package/ios/RNJWPlayer/RNJWPlayerViewManager.m +4 -0
  21. package/ios/RNJWPlayer/RNJWPlayerViewManager.swift +3 -3
  22. package/package.json +3 -3
  23. package/.idea/modules.xml +0 -8
  24. package/.idea/vcs.xml +0 -6
  25. package/android/.gradle/8.1.1/checksums/checksums.lock +0 -0
  26. package/android/.gradle/8.1.1/checksums/md5-checksums.bin +0 -0
  27. package/android/.gradle/8.1.1/checksums/sha1-checksums.bin +0 -0
  28. package/android/.gradle/8.1.1/dependencies-accessors/dependencies-accessors.lock +0 -0
  29. package/android/.gradle/8.1.1/fileHashes/fileHashes.lock +0 -0
  30. package/android/.gradle/8.4/checksums/checksums.lock +0 -0
  31. package/android/.gradle/8.4/checksums/md5-checksums.bin +0 -0
  32. package/android/.gradle/8.4/checksums/sha1-checksums.bin +0 -0
  33. package/android/.gradle/8.4/dependencies-accessors/dependencies-accessors.lock +0 -0
  34. package/android/.gradle/8.4/dependencies-accessors/gc.properties +0 -0
  35. package/android/.gradle/8.4/executionHistory/executionHistory.bin +0 -0
  36. package/android/.gradle/8.4/executionHistory/executionHistory.lock +0 -0
  37. package/android/.gradle/8.4/fileChanges/last-build.bin +0 -0
  38. package/android/.gradle/8.4/fileHashes/fileHashes.bin +0 -0
  39. package/android/.gradle/8.4/fileHashes/fileHashes.lock +0 -0
  40. package/android/.gradle/8.4/gc.properties +0 -0
  41. package/android/.gradle/config.properties +0 -2
  42. package/android/.gradle/file-system.probe +0 -0
  43. package/android/.idea/compiler.xml +0 -6
  44. package/android/.idea/gradle.xml +0 -18
  45. package/android/.idea/migrations.xml +0 -10
  46. package/android/.idea/misc.xml +0 -10
  47. package/android/.idea/vcs.xml +0 -6
  48. package/android/local.properties +0 -8
  49. package/ios/RNJWPlayer.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  50. package/ios/RNJWPlayer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  51. package/ios/RNJWPlayer.xcodeproj/project.xcworkspace/xcuserdata/jmilham.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  52. package/ios/RNJWPlayer.xcodeproj/xcuserdata/jmilham.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  53. /package/android/.gradle/{8.1.1 → 8.9}/dependencies-accessors/gc.properties +0 -0
  54. /package/android/.gradle/{8.1.1 → 8.9}/fileChanges/last-build.bin +0 -0
  55. /package/android/.gradle/{8.1.1 → 8.9}/gc.properties +0 -0
@@ -36,3 +36,13 @@ If you are having a build issue, we would like to know about your machine.
36
36
 
37
37
  **Additional context**
38
38
  Add any other context about the problem here.
39
+
40
+ **JWP Ticketing**
41
+ To expedite resolution and maintain confidentiality, submit a JWP [request](https://support.jwplayer.com/hc/en-us/requests/new) in addition to this new `Issue`. A JWP request offers the following benefits:
42
+ * Associates the `Issue` with your company
43
+ * Permits securely share sensitive information related to the bug, request, or question
44
+ * Enhances JWP's ability to track progress and provide timely updates
45
+ * Enables you to track and manage multiple issues through a single platform
46
+
47
+ | ℹ️ While `Issues` are valuable for open-source collaboration, a JWP request ensures that you will receive clear timelines and efficient support. |
48
+ |:---|
@@ -18,3 +18,13 @@ A clear and concise description of any alternative solutions or features you've
18
18
 
19
19
  **Additional context**
20
20
  Add any other context or screenshots about the feature request here.
21
+
22
+ **JWP Ticketing**
23
+ To expedite resolution and maintain confidentiality, submit a JWP [request](https://support.jwplayer.com/hc/en-us/requests/new) in addition to this new `Issue`. A JWP request offers the following benefits:
24
+ * Associates the `Issue` with your company
25
+ * Permits securely share sensitive information related to the bug, request, or question
26
+ * Enhances JWP's ability to track progress and provide timely updates
27
+ * Enables you to track and manage multiple issues through a single platform
28
+
29
+ | ℹ️ While `Issues` are valuable for open-source collaboration, a JWP request ensures that you will receive clear timelines and efficient support. |
30
+ |:---|
@@ -15,3 +15,12 @@ assignees: '@jwplayer/team_sdks'
15
15
 
16
16
  **Additional context**
17
17
 
18
+ **JWP Ticketing**
19
+ To expedite resolution and maintain confidentiality, submit a JWP [request](https://support.jwplayer.com/hc/en-us/requests/new) in addition to this new `Issue`. A JWP request offers the following benefits:
20
+ * Associates the `Issue` with your company
21
+ * Permits securely share sensitive information related to the bug, request, or question
22
+ * Enhances JWP's ability to track progress and provide timely updates
23
+ * Enables you to track and manage multiple issues through a single platform
24
+
25
+ | ℹ️ While `Issues` are valuable for open-source collaboration, a JWP request ensures that you will receive clear timelines and efficient support. |
26
+ |:---|
@@ -9,3 +9,13 @@ assignees: '@jwplayer/team_sdks'
9
9
 
10
10
  **What isn't clear?**
11
11
  What information are we not providing that you think we should?
12
+
13
+ **JWP Ticketing**
14
+ To expedite resolution and maintain confidentiality, submit a JWP [request](https://support.jwplayer.com/hc/en-us/requests/new) in addition to this new `Issue`. A JWP request offers the following benefits:
15
+ * Associates the `Issue` with your company
16
+ * Permits securely share sensitive information related to the bug, request, or question
17
+ * Enhances JWP's ability to track progress and provide timely updates
18
+ * Enables you to track and manage multiple issues through a single platform
19
+
20
+ | ℹ️ While `Issues` are valuable for open-source collaboration, a JWP request ensures that you will receive clear timelines and efficient support. |
21
+ |:---|
package/README.md CHANGED
@@ -178,6 +178,7 @@ Follow these steps to configure the media playback experience in your app:
178
178
  onPlayerError={event => this.onPlayerError(event)}
179
179
  onBuffer={() => this.onBuffer()}
180
180
  onTime={event => this.onTime(event)}
181
+ onLoaded={event => this.onLoaded(event)}
181
182
  onFullScreen={() => this.onFullScreen()}
182
183
  onFullScreenExit={() => this.onFullScreenExit()}
183
184
  />
@@ -28,14 +28,14 @@ Pod::Spec.new do |s|
28
28
 
29
29
  if defined?($RNJWPlayerUseGoogleCast)
30
30
  Pod::UI.puts "RNJWPlayer: enable Google Cast"
31
- s.dependency 'google-cast-sdk', '4.8.1'
31
+ s.dependency 'google-cast-sdk', '~> 4.8'
32
32
  s.pod_target_xcconfig = {
33
33
  'OTHER_SWIFT_FLAGS' => '$(inherited) -D USE_GOOGLE_CAST'
34
34
  }
35
35
  end
36
36
  if defined?($RNJWPlayerUseGoogleIMA)
37
37
  Pod::UI.puts "RNJWPlayer: enable IMA SDK"
38
- s.dependency 'GoogleAds-IMA-iOS-SDK', '3.22.0'
38
+ s.dependency 'GoogleAds-IMA-iOS-SDK', '~> 3.22'
39
39
  s.pod_target_xcconfig = {
40
40
  'OTHER_SWIFT_FLAGS' => '$(inherited) -D USE_GOOGLE_IMA'
41
41
  }
@@ -1,2 +1,2 @@
1
- #Wed May 22 15:58:05 CEST 2024
2
- gradle.version=8.1.1
1
+ #Tue Aug 06 14:41:55 PDT 2024
2
+ gradle.version=8.9
@@ -66,7 +66,7 @@ allprojects {
66
66
  }
67
67
  }
68
68
 
69
- def jwPlayerVersion = "4.18.0"
69
+ def jwPlayerVersion = "4.18.3"
70
70
  def exoplayerVersion = "2.18.7" // Deprecated. Use Media3 when targeting JW SDK > 4.16.0
71
71
  def media3ExoVersion = "1.1.1"
72
72
 
@@ -9,11 +9,13 @@
9
9
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
10
10
  <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
11
11
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
12
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
12
13
  <!-- READ_MEDIA_IMAGES, READ_MEDIA_VIDEO or READ_MEDIA_AUDIO.-->
13
14
 
14
15
  <application>
15
16
  <service
16
17
  android:name="com.jwplayer.pub.api.background.MediaService"
18
+ android:foregroundServiceType="mediaPlayback"
17
19
  android:exported="false">
18
20
  <intent-filter>
19
21
  <action android:name="android.intent.action.MEDIA_BUTTON" />
@@ -2,7 +2,10 @@ package com.jwplayer.rnjwplayer;
2
2
 
3
3
 
4
4
  import android.app.Activity;
5
+ import android.content.BroadcastReceiver;
5
6
  import android.content.Context;
7
+ import android.content.Intent;
8
+ import android.content.IntentFilter;
6
9
  import android.content.pm.ActivityInfo;
7
10
  import android.graphics.Color;
8
11
  import android.graphics.PorterDuff;
@@ -12,11 +15,15 @@ import android.media.AudioAttributes;
12
15
  import android.media.AudioFocusRequest;
13
16
  import android.media.AudioManager;
14
17
  import android.os.Build;
18
+ import android.os.Handler;
19
+ import android.os.Looper;
15
20
  import android.util.Log;
21
+ import android.view.Choreographer;
16
22
  import android.view.View;
17
23
  import android.view.ViewGroup;
18
24
  import android.view.Window;
19
25
  import android.view.WindowManager;
26
+ import android.widget.FrameLayout;
20
27
  import android.widget.LinearLayout;
21
28
  import android.widget.RelativeLayout;
22
29
 
@@ -96,7 +103,12 @@ import com.jwplayer.pub.api.events.listeners.AdvertisingEvents;
96
103
  import com.jwplayer.pub.api.events.listeners.CastingEvents;
97
104
  import com.jwplayer.pub.api.events.listeners.PipPluginEvents;
98
105
  import com.jwplayer.pub.api.events.listeners.VideoPlayerEvents;
106
+ import com.jwplayer.pub.api.fullscreen.ExtensibleFullscreenHandler;
107
+ import com.jwplayer.pub.api.fullscreen.FullscreenDialog;
99
108
  import com.jwplayer.pub.api.fullscreen.FullscreenHandler;
109
+ import com.jwplayer.pub.api.fullscreen.delegates.DeviceOrientationDelegate;
110
+ import com.jwplayer.pub.api.fullscreen.delegates.DialogLayoutDelegate;
111
+ import com.jwplayer.pub.api.fullscreen.delegates.SystemUiDelegate;
100
112
  import com.jwplayer.pub.api.license.LicenseUtil;
101
113
  import com.jwplayer.pub.api.media.playlists.PlaylistItem;
102
114
  import com.jwplayer.ui.views.CueMarkerSeekbar;
@@ -106,6 +118,7 @@ import java.util.Arrays;
106
118
  import java.util.HashMap;
107
119
  import java.util.List;
108
120
  import java.util.Map;
121
+ import java.util.Objects;
109
122
 
110
123
  import org.json.JSONObject;
111
124
 
@@ -183,6 +196,7 @@ public class RNJWPlayerView extends RelativeLayout implements
183
196
  Boolean fullScreenOnLandscape = false;
184
197
  Boolean portraitOnExitFullScreen = false;
185
198
  Boolean exitFullScreenOnPortrait = false;
199
+ Boolean playerInModal = false;
186
200
 
187
201
  Number currentPlayingIndex;
188
202
 
@@ -212,6 +226,7 @@ public class RNJWPlayerView extends RelativeLayout implements
212
226
  private ThemedReactContext mThemedReactContext;
213
227
 
214
228
  private MediaServiceController mMediaServiceController;
229
+ private PipHandlerReceiver mReceiver = null;
215
230
 
216
231
  private void doBindService() {
217
232
  if (mMediaServiceController != null) {
@@ -233,7 +248,7 @@ public class RNJWPlayerView extends RelativeLayout implements
233
248
  }
234
249
 
235
250
  private static Context getNonBuggyContext(ThemedReactContext reactContext,
236
- ReactApplicationContext appContext) {
251
+ ReactApplicationContext appContext) {
237
252
  Context superContext = reactContext;
238
253
  if (!contextHasBug(appContext.getCurrentActivity())) {
239
254
  superContext = appContext.getCurrentActivity();
@@ -290,6 +305,8 @@ public class RNJWPlayerView extends RelativeLayout implements
290
305
 
291
306
  public void destroyPlayer() {
292
307
  if (mPlayer != null) {
308
+ unRegisterReceiver();
309
+ mPlayer.deregisterActivityForPip();
293
310
  mPlayer.stop();
294
311
 
295
312
  mPlayer.removeListeners(this,
@@ -348,7 +365,7 @@ public class RNJWPlayerView extends RelativeLayout implements
348
365
  EventType.PIP_OPEN
349
366
  );
350
367
 
351
- mPlayer = null;
368
+ mPlayer = null;
352
369
  mPlayerView = null;
353
370
 
354
371
  getReactContext().removeLifecycleEventListener(this);
@@ -427,12 +444,116 @@ public class RNJWPlayerView extends RelativeLayout implements
427
444
  EventType.PIP_OPEN
428
445
  );
429
446
 
430
- mPlayer.setFullscreenHandler(new fullscreenHandler());
447
+ if (playerInModal) {
448
+ mPlayer.setFullscreenHandler(createModalFullscreenHandler());
449
+ } else {
450
+ mPlayer.setFullscreenHandler(new fullscreenHandler());
451
+ }
431
452
 
432
453
  mPlayer.allowBackgroundAudio(backgroundAudioEnabled);
433
454
  }
434
455
  }
435
456
 
457
+ /**
458
+ * Helper to build the a generic `ExtensibleFullscreenHandler` with small tweaks to play nice with Modals
459
+ * @return {@link ExtensibleFullscreenHandler}
460
+ */
461
+ private ExtensibleFullscreenHandler createModalFullscreenHandler() {
462
+ DeviceOrientationDelegate delegate = getDeviceOrientationDelegate();
463
+ FullscreenDialog dialog = new FullscreenDialog(
464
+ mActivity,
465
+ mActivity,
466
+ android.R.style.Theme_Black_NoTitleBar_Fullscreen
467
+ );
468
+
469
+ return new ExtensibleFullscreenHandler(
470
+ new DialogLayoutDelegate(
471
+ mPlayerView,
472
+ dialog
473
+ ),
474
+ delegate,
475
+ new SystemUiDelegate(
476
+ mActivity,
477
+ mActivity.getLifecycle(),
478
+ new Handler(),
479
+ dialog.getWindow().getDecorView()
480
+ )
481
+ ) {
482
+ @Override
483
+ public void onFullscreenRequested() {
484
+ // if landscape is priorty we have to turn off full-screen portrait before allowing
485
+ // the default call for full-screen
486
+ mPlayer.allowFullscreenPortrait(!landscapeOnFullScreen);
487
+ super.onFullscreenRequested();
488
+ // safely set it back on UI thread after work can be finished
489
+ final Handler handler = new Handler(Looper.getMainLooper());
490
+ handler.postDelayed(() -> {
491
+ if (mPlayer != null) {
492
+ mPlayer.allowFullscreenPortrait(true);
493
+ }
494
+ }, 100);
495
+ WritableMap eventEnterFullscreen = Arguments.createMap();
496
+ eventEnterFullscreen.putString("message", "onFullscreenRequested");
497
+ getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(
498
+ getId(),
499
+ "topFullScreenRequested",
500
+ eventEnterFullscreen);
501
+ }
502
+
503
+ @Override
504
+ public void onFullscreenExitRequested() {
505
+ super.onFullscreenExitRequested();
506
+
507
+ WritableMap eventExitFullscreen = Arguments.createMap();
508
+ eventExitFullscreen.putString("message", "onFullscreenExitRequested");
509
+ getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(
510
+ getId(),
511
+ "topFullScreenExitRequested",
512
+ eventExitFullscreen);
513
+ }
514
+ };
515
+ }
516
+
517
+ /**
518
+ * Add logic here for your custom orientation implementation
519
+ *
520
+ * @return Default {@link DeviceOrientationDelegate}
521
+ */
522
+ private DeviceOrientationDelegate getDeviceOrientationDelegate() {
523
+ DeviceOrientationDelegate delegate = new DeviceOrientationDelegate(
524
+ mActivity,
525
+ mActivity.getLifecycle(),
526
+ new Handler()
527
+ ) {
528
+ @Override
529
+ public void setFullscreen(boolean fullscreen) {
530
+ super.setFullscreen(fullscreen);
531
+ }
532
+
533
+ @Override
534
+ public void onAllowRotationChanged(boolean allowRotation) {
535
+ super.onAllowRotationChanged(allowRotation);
536
+ }
537
+
538
+ @Override
539
+ protected void doRotation(boolean fullscreen, boolean allowFullscreenPortrait) {
540
+ super.doRotation(fullscreen, allowFullscreenPortrait);
541
+ }
542
+
543
+ @Override
544
+ protected void doRotationListener() {
545
+ super.doRotationListener();
546
+ }
547
+
548
+ @Override
549
+ public void onAllowFullscreenPortrait(boolean allowFullscreenPortrait) {
550
+ super.onAllowFullscreenPortrait(allowFullscreenPortrait);
551
+ }
552
+ };
553
+ delegate.onAllowRotationChanged(true);
554
+ return delegate;
555
+ }
556
+
436
557
  private class fullscreenHandler implements FullscreenHandler {
437
558
  ViewGroup mPlayerViewContainer = (ViewGroup) mPlayerView.getParent();
438
559
  private View mDecorView;
@@ -504,8 +625,15 @@ public class RNJWPlayerView extends RelativeLayout implements
504
625
  mPlayerViewContainer.addView(mPlayerView, new ViewGroup.LayoutParams(
505
626
  ViewGroup.LayoutParams.MATCH_PARENT,
506
627
  ViewGroup.LayoutParams.MATCH_PARENT));
507
- mPlayerView.layout(mPlayerViewContainer.getLeft(), mPlayerViewContainer.getTop(),
508
- mPlayerViewContainer.getRight(), mPlayerViewContainer.getBottom());
628
+ // returning from full-screen portrait requires a different measure
629
+ if (mActivity.getResources().getConfiguration().orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
630
+ ) {
631
+ mPlayerView.layout(mPlayerView.getLeft(), mPlayerViewContainer.getTop(),
632
+ mPlayerViewContainer.getMeasuredWidth(), mPlayerViewContainer.getBottom());
633
+ } else {
634
+ mPlayerView.layout(mPlayerViewContainer.getLeft(), mPlayerViewContainer.getTop(),
635
+ mPlayerViewContainer.getRight(), mPlayerViewContainer.getBottom());
636
+ }
509
637
  }
510
638
  });
511
639
 
@@ -519,12 +647,12 @@ public class RNJWPlayerView extends RelativeLayout implements
519
647
 
520
648
  @Override
521
649
  public void onAllowRotationChanged(boolean b) {
522
- Log.e(TAG, "onAllowRotationChanged: " + b );
650
+ Log.e(TAG, "onAllowRotationChanged: " + b);
523
651
  }
524
652
 
525
653
  @Override
526
654
  public void onAllowFullscreenPortraitChanged(boolean allowFullscreenPortrait) {
527
- Log.e(TAG, "onAllowFullscreenPortraitChanged: " + allowFullscreenPortrait );
655
+ Log.e(TAG, "onAllowFullscreenPortraitChanged: " + allowFullscreenPortrait);
528
656
  }
529
657
 
530
658
  @Override
@@ -540,10 +668,96 @@ public class RNJWPlayerView extends RelativeLayout implements
540
668
  }
541
669
  }
542
670
 
671
+ private ArrayList<Integer> rootViewChildrenOriginalVisibility = new ArrayList<Integer>();
672
+
673
+ private class PipHandlerReceiver extends BroadcastReceiver {
674
+
675
+ @Override
676
+ public void onReceive(Context context, Intent intent) {
677
+ if (intent == null) {
678
+ return;
679
+ }
680
+ if (Objects.equals(intent.getAction(), "onPictureInPictureModeChanged")) {
681
+ if (intent.hasExtra("newConfig") && intent.hasExtra("isInPictureInPictureMode")) {
682
+ // Tell the JWP SDK we are toggling so it can handle toolbar / internal setup
683
+ mPlayer.onPictureInPictureModeChanged(intent.getBooleanExtra("isInPictureInPictureMode", false), intent.getParcelableExtra("newConfig"));
684
+
685
+ View decorView = mActivity.getWindow().getDecorView();
686
+ ViewGroup rootView = decorView.findViewById(android.R.id.content);
687
+
688
+ ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
689
+ ViewGroup.LayoutParams.MATCH_PARENT,
690
+ ViewGroup.LayoutParams.MATCH_PARENT);
691
+
692
+ if (intent.getBooleanExtra("isInPictureInPictureMode", false)) {
693
+ // Going into Picture in Picture
694
+ ViewGroup parent = (ViewGroup) mPlayerView.getParent();
695
+
696
+ // Remove the player view temporarily
697
+ if (parent != null) {
698
+ parent.removeView(mPlayerView);
699
+ }
700
+
701
+ // Hide all views but player view and keep a handle on them for later
702
+ for (int i = 0; i < rootView.getChildCount(); i++) {
703
+ if (rootView.getChildAt(i) != mPlayerView) {
704
+ rootViewChildrenOriginalVisibility.add(rootView.getChildAt(i).getVisibility());
705
+ rootView.getChildAt(i).setVisibility(View.GONE);
706
+ }
707
+ }
708
+ // Add player view back (This is safe since the JWP SDK has already calculated the PiP size/aspect off the View)
709
+ rootView.addView(mPlayerView, layoutParams);
710
+ } else {
711
+ // Exiting Picture in Picture
712
+
713
+ // Toggle controls to ensure we don't lose them -- weird UX bug fix where controls got lost
714
+ mPlayer.setForceControlsVisibility(true);
715
+ mPlayer.setForceControlsVisibility(false);
716
+
717
+ // Strip player view
718
+ rootView.removeView(mPlayerView);
719
+
720
+ // Add visibility back to any other controls
721
+ for (int i = 0; i < rootView.getChildCount(); i++) {
722
+ rootView.getChildAt(i).setVisibility(rootViewChildrenOriginalVisibility.get(i));
723
+ }
724
+ // Clear our list of views
725
+ rootViewChildrenOriginalVisibility.clear();
726
+ // Add player view back in main spot
727
+ addView(mPlayerView, 0, layoutParams);
728
+ }
729
+ }
730
+ }
731
+ }
732
+ }
733
+
734
+ private void registerReceiver() {
735
+ mReceiver = new PipHandlerReceiver();
736
+ IntentFilter intentFilter = new IntentFilter("onPictureInPictureModeChanged");
737
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
738
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
739
+ // Must be Exported to get intents
740
+ mActivity.registerReceiver(mReceiver, intentFilter, Context.RECEIVER_EXPORTED);
741
+ } else {
742
+ mActivity.registerReceiver(mReceiver, intentFilter); // Safe API level < 34
743
+ }
744
+ } else {
745
+ mActivity.registerReceiver(mReceiver, intentFilter); // Safe API level < 34
746
+ }
747
+ }
748
+
749
+ private void unRegisterReceiver() {
750
+ if (mReceiver != null) {
751
+ mActivity.unregisterReceiver(mReceiver);
752
+ mReceiver = null;
753
+ }
754
+
755
+ }
756
+
543
757
  public void setConfig(ReadableMap prop) {
544
758
  if (mConfig == null || !mConfig.equals(prop)) {
545
759
  if (mConfig != null && isOnlyDiff(prop, "playlist") && mPlayer != null) { // still safe check, even with JW
546
- // JSON change
760
+ // JSON change
547
761
  PlayerConfig oldConfig = mPlayer.getConfig();
548
762
  PlayerConfig config = new PlayerConfig.Builder()
549
763
  .autostart(oldConfig.getAutostart())
@@ -610,7 +824,7 @@ public class RNJWPlayerView extends RelativeLayout implements
610
824
 
611
825
  boolean playlistNotTheSame(ReadableMap prop) {
612
826
  return prop.hasKey("playlist") && mPlaylistProp != prop.getArray("playlist") && !Arrays
613
- .deepEquals(new ReadableArray[] { mPlaylistProp }, new ReadableArray[] { prop.getArray("playlist") });
827
+ .deepEquals(new ReadableArray[]{mPlaylistProp}, new ReadableArray[]{prop.getArray("playlist")});
614
828
  }
615
829
 
616
830
  private void setupPlayer(ReadableMap prop) {
@@ -621,7 +835,7 @@ public class RNJWPlayerView extends RelativeLayout implements
621
835
  PlayerConfig jwConfig = null;
622
836
  Boolean forceLegacy = prop.hasKey("forceLegacyConfig") ? prop.getBoolean("forceLegacyConfig") : false;
623
837
  Boolean isJwConfig = false;
624
- if(!forceLegacy){
838
+ if (!forceLegacy) {
625
839
  try {
626
840
  obj = MapUtil.toJSONObject(prop);
627
841
  jwConfig = JsonHelper.parseConfigJson(obj);
@@ -761,6 +975,18 @@ public class RNJWPlayerView extends RelativeLayout implements
761
975
  mPlayerView.fullScreenOnLandscape = fullScreenOnLandscape;
762
976
  }
763
977
 
978
+ if (prop.hasKey("landscapeOnFullScreen")) {
979
+ landscapeOnFullScreen = prop.getBoolean("landscapeOnFullScreen");
980
+ }
981
+
982
+ if (prop.hasKey("portraitOnExitFullScreen")) {
983
+ portraitOnExitFullScreen = prop.getBoolean("fullScreenOnLandscape");
984
+ }
985
+
986
+ if (prop.hasKey("playerInModal")) {
987
+ playerInModal = prop.getBoolean("playerInModal");
988
+ }
989
+
764
990
  if (prop.hasKey("exitFullScreenOnPortrait")) {
765
991
  exitFullScreenOnPortrait = prop.getBoolean("exitFullScreenOnPortrait");
766
992
  mPlayerView.exitFullScreenOnPortrait = exitFullScreenOnPortrait;
@@ -778,9 +1004,11 @@ public class RNJWPlayerView extends RelativeLayout implements
778
1004
  if (mActivity != null && prop.hasKey("pipEnabled")) {
779
1005
  boolean pipEnabled = prop.getBoolean("pipEnabled");
780
1006
  if (pipEnabled) {
1007
+ registerReceiver();
781
1008
  mPlayer.registerActivityForPip(mActivity, mActivity.getSupportActionBar());
782
1009
  } else {
783
1010
  mPlayer.deregisterActivityForPip();
1011
+ unRegisterReceiver();
784
1012
  }
785
1013
  }
786
1014
 
@@ -1269,6 +1497,13 @@ public class RNJWPlayerView extends RelativeLayout implements
1269
1497
  doBindService();
1270
1498
  requestAudioFocus();
1271
1499
  }
1500
+ WritableMap onFirstFrame = Arguments.createMap();
1501
+ onFirstFrame.putString("message", "onLoaded");
1502
+ onFirstFrame.putDouble("loadTime", firstFrameEvent.getLoadTime());
1503
+ getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(
1504
+ getId(),
1505
+ "topFirstFrame",
1506
+ onFirstFrame);
1272
1507
  }
1273
1508
 
1274
1509
  @Override
@@ -1457,7 +1692,7 @@ public class RNJWPlayerView extends RelativeLayout implements
1457
1692
  } else {
1458
1693
  if (backgroundAudioEnabled) {
1459
1694
  mMediaServiceController = new MediaServiceController.Builder((AppCompatActivity) mActivity, mPlayer)
1460
- .build();
1695
+ .build();
1461
1696
  doBindService();
1462
1697
  }
1463
1698
  }
@@ -159,6 +159,10 @@ public class RNJWPlayerViewManager extends SimpleViewManager<RNJWPlayerView> {
159
159
  MapBuilder.of(
160
160
  "phasedRegistrationNames",
161
161
  MapBuilder.of("bubbled", "onCasting")))
162
+ .put("topFirstFrame",
163
+ MapBuilder.of(
164
+ "phasedRegistrationNames",
165
+ MapBuilder.of("bubbled", "onLoaded")))
162
166
  .build();
163
167
  }
164
168
 
@@ -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"><title>version: 1.0.1</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.1</text><text x="695" y="140" transform="scale(.1)" fill="#fff" textLength="290">1.0.1</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.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>
package/index.d.ts CHANGED
@@ -448,6 +448,7 @@ declare module "@jwplayer/jwplayer-react-native" {
448
448
  landscapeOnFullScreen?: boolean;
449
449
  portraitOnExitFullScreen?: boolean;
450
450
  exitFullScreenOnPortrait?: boolean;
451
+ playerInModal?: boolean;
451
452
  playlist?: PlaylistItem[];
452
453
  stretching?: string;
453
454
  related?: Related;
@@ -486,6 +487,9 @@ declare module "@jwplayer/jwplayer-react-native" {
486
487
  interface PlaylistEventProps {
487
488
  playlist: PlaylistItem[]
488
489
  }
490
+ interface LoadEventProps {
491
+ loadTime: number;
492
+ }
489
493
  interface PlaylistItemEventProps {
490
494
  playlistItem: PlaylistItem
491
495
  }
@@ -511,6 +515,7 @@ declare module "@jwplayer/jwplayer-react-native" {
511
515
  forceLegacyConfig?: boolean;
512
516
  onPlayerReady?: () => void;
513
517
  onPlaylist?: (event: BaseEvent<PlaylistEventProps>) => void;
518
+ onLoaded? : (event: BaseEvent<LoadEventProps>) => void;
514
519
  onBeforePlay?: () => void;
515
520
  onBeforeComplete?: () => void;
516
521
  onComplete?: () => void;
package/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  findNodeHandle,
7
7
  } from 'react-native';
8
8
  import PropTypes from 'prop-types';
9
- import _ from 'lodash';
9
+ import isEqualWith from 'lodash.isequalwith';
10
10
 
11
11
  const RNJWPlayerManager =
12
12
  Platform.OS === 'ios'
@@ -306,6 +306,7 @@ export default class JWPlayer extends Component {
306
306
  offlineImage: PropTypes.string,
307
307
  forceFullScreenOnLandscape: PropTypes.bool,
308
308
  forceLandscapeOnFullScreen: PropTypes.bool,
309
+ playerInModal: PropTypes.bool,
309
310
  enableLockScreenControls: PropTypes.bool,
310
311
  stretching: PropTypes.oneOf([
311
312
  'uniform',
@@ -319,6 +320,7 @@ export default class JWPlayer extends Component {
319
320
  }),
320
321
  onPlayerReady: PropTypes.func,
321
322
  onPlaylist: PropTypes.func,
323
+ onLoaded: PropTypes.func,
322
324
  changePlaylist: PropTypes.func,
323
325
  play: PropTypes.func,
324
326
  pause: PropTypes.func,
@@ -389,7 +391,7 @@ export default class JWPlayer extends Component {
389
391
  var { config, controls } = nextProps;
390
392
  var thisConfig = this.props.config || {};
391
393
 
392
- var result = !_.isEqualWith(
394
+ var result = !isEqualWith(
393
395
  config,
394
396
  thisConfig,
395
397
  (value1, value2, key) => {
@@ -1192,7 +1192,7 @@ class RNJWPlayerView : UIView, JWPlayerDelegate, JWPlayerStateDelegate, JWAdDele
1192
1192
  }
1193
1193
 
1194
1194
  func jwplayer(_ player:JWPlayer, didFinishLoadingWithTime loadTime:TimeInterval) {
1195
- self.onLoaded?([:])
1195
+ self.onLoaded?(["loadTime":loadTime])
1196
1196
  }
1197
1197
 
1198
1198
  func jwplayer(_ player:JWPlayer, isAttemptingToPlay playlistItem:JWPlayerItem, reason:JWPlayReason) {
@@ -280,7 +280,7 @@ class RNJWPlayerViewController : JWPlayerViewController, JWPlayerViewControllerD
280
280
 
281
281
  override func jwplayer(_ player:JWPlayer, didFinishLoadingWithTime loadTime:TimeInterval) {
282
282
  super.jwplayer(player, didFinishLoadingWithTime:loadTime)
283
- parentView?.onLoaded?([:])
283
+ parentView?.onLoaded?(["loadTime":loadTime])
284
284
  }
285
285
 
286
286
  override func jwplayer(_ player:JWPlayer, isAttemptingToPlay playlistItem:JWPlayerItem, reason:JWPlayReason) {
@@ -93,6 +93,8 @@ RCT_EXTERN_METHOD(setVolume: (nonnull NSNumber *)reactTag :(nonnull NSNumber *)v
93
93
 
94
94
  RCT_EXTERN_METHOD(togglePIP: (nonnull NSNumber *)reactTag)
95
95
 
96
+ #if USE_GOOGLE_CAST
97
+
96
98
  RCT_EXTERN_METHOD(setUpCastController: (nonnull NSNumber *)reactTag)
97
99
 
98
100
  RCT_EXTERN_METHOD(presentCastDialog: (nonnull NSNumber *)reactTag)
@@ -103,6 +105,8 @@ RCT_EXTERN_METHOD(availableDevices: (nonnull NSNumber *)reactTag: (RCTPromiseRes
103
105
 
104
106
  RCT_EXTERN_METHOD(castState: (nonnull NSNumber *)reactTag :(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject)
105
107
 
108
+ #endif
109
+
106
110
  RCT_EXTERN_METHOD(getAudioTracks: (nonnull NSNumber *)reactTag: (RCTPromiseResolveBlock)resolve: (RCTPromiseRejectBlock)reject)
107
111
 
108
112
  RCT_EXTERN_METHOD(getCurrentAudioTrack: (nonnull NSNumber *)reactTag :(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject)
@@ -172,7 +172,7 @@ class RNJWPlayerViewManager: RCTViewManager {
172
172
  }
173
173
  }
174
174
 
175
- @objc func setVolume(_ reactTag: NSNumber, _ volume: Double) {
175
+ @objc func setVolume(_ reactTag: NSNumber, _ volume: NSNumber) {
176
176
  self.bridge.uiManager.addUIBlock { uiManager, viewRegistry in
177
177
  guard let view = viewRegistry?[reactTag] as? RNJWPlayerView else {
178
178
  print("Invalid view returned from registry, expecting RNJWPlayerView, got: \(String(describing: viewRegistry?[reactTag]))")
@@ -180,9 +180,9 @@ class RNJWPlayerViewManager: RCTViewManager {
180
180
  }
181
181
 
182
182
  if let playerView = view.playerView {
183
- playerView.player.volume = volume
183
+ playerView.player.volume = volume.doubleValue
184
184
  } else if let playerViewController = view.playerViewController {
185
- playerViewController.player.volume = volume
185
+ playerViewController.player.volume = volume.doubleValue
186
186
  }
187
187
  }
188
188
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jwplayer/jwplayer-react-native",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
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",
@@ -35,11 +35,11 @@
35
35
  "license": "MIT",
36
36
  "homepage": "https://github.com/jwplayer/jwplayer-react-native#readme",
37
37
  "devDependencies": {
38
- "lodash": ">= 4.17.21",
38
+ "badge-maker": "^3.3.1",
39
39
  "react": ">= 18.2.0",
40
40
  "react-native": ">= 0.72.5"
41
41
  },
42
42
  "dependencies": {
43
- "badge-maker": "^3.3.1"
43
+ "lodash.isequalwith": "^4.4.0"
44
44
  }
45
45
  }
package/.idea/modules.xml DELETED
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ProjectModuleManager">
4
- <modules>
5
- <module fileurl="file://$PROJECT_DIR$/.idea/jwplayer-react-native.iml" filepath="$PROJECT_DIR$/.idea/jwplayer-react-native.iml" />
6
- </modules>
7
- </component>
8
- </project>
package/.idea/vcs.xml DELETED
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="VcsDirectoryMappings">
4
- <mapping directory="" vcs="Git" />
5
- </component>
6
- </project>
File without changes
@@ -1,2 +0,0 @@
1
- #Fri Apr 19 10:10:39 PDT 2024
2
- java.home=/Applications/Android Studio.app/Contents/jbr/Contents/Home
Binary file
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="CompilerConfiguration">
4
- <bytecodeTargetLevel target="17" />
5
- </component>
6
- </project>
@@ -1,18 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="GradleMigrationSettings" migrationVersion="1" />
4
- <component name="GradleSettings">
5
- <option name="linkedExternalProjectsSettings">
6
- <GradleProjectSettings>
7
- <option name="externalProjectPath" value="$PROJECT_DIR$" />
8
- <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
9
- <option name="modules">
10
- <set>
11
- <option value="$PROJECT_DIR$" />
12
- </set>
13
- </option>
14
- <option name="resolveExternalAnnotations" value="false" />
15
- </GradleProjectSettings>
16
- </option>
17
- </component>
18
- </project>
@@ -1,10 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ProjectMigrations">
4
- <option name="MigrateToGradleLocalJavaHome">
5
- <set>
6
- <option value="$PROJECT_DIR$" />
7
- </set>
8
- </option>
9
- </component>
10
- </project>
@@ -1,10 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ExternalStorageConfigurationManager" enabled="true" />
4
- <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
5
- <output url="file://$PROJECT_DIR$/build/classes" />
6
- </component>
7
- <component name="ProjectType">
8
- <option name="id" value="Android" />
9
- </component>
10
- </project>
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="VcsDirectoryMappings">
4
- <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
5
- </component>
6
- </project>
@@ -1,8 +0,0 @@
1
- ## This file must *NOT* be checked into Version Control Systems,
2
- # as it contains information specific to your local configuration.
3
- #
4
- # Location of the SDK. This is only used by Gradle.
5
- # For customization when using a Version Control System, please read the
6
- # header note.
7
- #Fri Apr 19 10:10:39 PDT 2024
8
- sdk.dir=/Users/jmilham/Library/Android/sdk
@@ -1,7 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <Workspace
3
- version = "1.0">
4
- <FileRef
5
- location = "self:">
6
- </FileRef>
7
- </Workspace>
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>IDEDidComputeMac32BitWarning</key>
6
- <true/>
7
- </dict>
8
- </plist>
@@ -1,14 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>SchemeUserState</key>
6
- <dict>
7
- <key>RNJWPlayer.xcscheme_^#shared#^_</key>
8
- <dict>
9
- <key>orderHint</key>
10
- <integer>0</integer>
11
- </dict>
12
- </dict>
13
- </dict>
14
- </plist>
File without changes