@picsa/capacitor-video-player 8.0.2 → 8.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -301,7 +301,9 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
301
301
|
new PlayerView.ControllerVisibilityListener() {
|
|
302
302
|
@Override
|
|
303
303
|
public void onVisibilityChanged(int visibility) {
|
|
304
|
-
linearLayout
|
|
304
|
+
if (linearLayout != null) {
|
|
305
|
+
linearLayout.setVisibility(visibility);
|
|
306
|
+
}
|
|
305
307
|
}
|
|
306
308
|
}
|
|
307
309
|
);
|
|
@@ -310,6 +312,9 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
310
312
|
new Player.Listener() {
|
|
311
313
|
@Override
|
|
312
314
|
public void onPlayerStateChanged(boolean playWhenReady, int state) {
|
|
315
|
+
if (isFragmentDestroyed || player == null || styledPlayerView == null) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
313
318
|
String stateString;
|
|
314
319
|
Map<String, Object> info = new HashMap<String, Object>() {
|
|
315
320
|
{
|
|
@@ -430,14 +435,17 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
430
435
|
}
|
|
431
436
|
if (uri != null || Boolean.TRUE.equals(isInternal)) {
|
|
432
437
|
// go fullscreen
|
|
433
|
-
getActivity()
|
|
434
|
-
|
|
438
|
+
Activity hostActivity = getActivity();
|
|
439
|
+
if (hostActivity != null && hostActivity.getWindow() != null) {
|
|
440
|
+
hostActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
|
441
|
+
hostActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
442
|
+
}
|
|
435
443
|
if (savedInstanceState != null) {
|
|
436
444
|
mCurrentPosition = savedInstanceState.getInt(PLAYBACK_TIME);
|
|
437
445
|
}
|
|
438
446
|
|
|
439
|
-
|
|
440
|
-
.runOnUiThread(
|
|
447
|
+
if (hostActivity != null) {
|
|
448
|
+
hostActivity.runOnUiThread(
|
|
441
449
|
new Runnable() {
|
|
442
450
|
@Override
|
|
443
451
|
public void run() {
|
|
@@ -511,10 +519,11 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
511
519
|
}
|
|
512
520
|
}
|
|
513
521
|
);
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
522
|
+
}
|
|
523
|
+
} else {
|
|
524
|
+
Log.d(TAG, "Video path wrong or type not supported");
|
|
525
|
+
Toast.makeText(context, "Video path wrong or type not supported", Toast.LENGTH_SHORT).show();
|
|
526
|
+
}
|
|
518
527
|
adjustAspectRatio();
|
|
519
528
|
return view;
|
|
520
529
|
}
|
|
@@ -526,7 +535,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
526
535
|
|
|
527
536
|
protected Bitmap doInBackground(Void... params) {
|
|
528
537
|
final String image = artwork;
|
|
529
|
-
if (image !=
|
|
538
|
+
if (image != null && !image.trim().isEmpty()) {
|
|
530
539
|
try {
|
|
531
540
|
URL url = new URL(image);
|
|
532
541
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
|
@@ -535,7 +544,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
535
544
|
InputStream input = connection.getInputStream();
|
|
536
545
|
Bitmap myBitmap = BitmapFactory.decodeStream(input);
|
|
537
546
|
return myBitmap;
|
|
538
|
-
} catch (IOException e) {
|
|
547
|
+
} catch (IOException | IllegalArgumentException e) {
|
|
539
548
|
e.printStackTrace();
|
|
540
549
|
return null;
|
|
541
550
|
}
|
|
@@ -546,7 +555,9 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
546
555
|
|
|
547
556
|
@Override
|
|
548
557
|
protected void onPostExecute(Bitmap result) {
|
|
549
|
-
cast_image
|
|
558
|
+
if (cast_image != null) {
|
|
559
|
+
cast_image.setImageBitmap(result);
|
|
560
|
+
}
|
|
550
561
|
}
|
|
551
562
|
}
|
|
552
563
|
|
|
@@ -554,14 +565,16 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
554
565
|
* Show controller
|
|
555
566
|
*/
|
|
556
567
|
public void showController() {
|
|
557
|
-
styledPlayerView
|
|
568
|
+
if (styledPlayerView != null) {
|
|
569
|
+
styledPlayerView.showController();
|
|
570
|
+
}
|
|
558
571
|
}
|
|
559
572
|
|
|
560
573
|
/**
|
|
561
574
|
* isControllerIsFullyVisible
|
|
562
575
|
*/
|
|
563
576
|
public boolean isControllerIsFullyVisible() {
|
|
564
|
-
return styledPlayerView.isControllerFullyVisible();
|
|
577
|
+
return styledPlayerView != null && styledPlayerView.isControllerFullyVisible();
|
|
565
578
|
}
|
|
566
579
|
|
|
567
580
|
/**
|
|
@@ -587,6 +600,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
587
600
|
if (
|
|
588
601
|
!isInPictureInPictureMode &&
|
|
589
602
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
|
|
603
|
+
packageManager != null &&
|
|
590
604
|
packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) &&
|
|
591
605
|
Boolean.TRUE.equals(isPIPModeeEnabled) &&
|
|
592
606
|
Boolean.TRUE.equals(pipEnabled) &&
|
|
@@ -599,18 +613,21 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
599
613
|
}
|
|
600
614
|
|
|
601
615
|
private void resizePressed() {
|
|
616
|
+
if (styledPlayerView == null) {
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
602
619
|
if (resizeStatus == AspectRatioFrameLayout.RESIZE_MODE_FIT) {
|
|
603
620
|
styledPlayerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FILL);
|
|
604
621
|
resizeStatus = AspectRatioFrameLayout.RESIZE_MODE_FILL;
|
|
605
|
-
resizeBtn.setImageResource(R.drawable.ic_zoom);
|
|
622
|
+
if (resizeBtn != null) resizeBtn.setImageResource(R.drawable.ic_zoom);
|
|
606
623
|
} else if (resizeStatus == AspectRatioFrameLayout.RESIZE_MODE_FILL) {
|
|
607
624
|
styledPlayerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM);
|
|
608
625
|
resizeStatus = AspectRatioFrameLayout.RESIZE_MODE_ZOOM;
|
|
609
|
-
resizeBtn.setImageResource(R.drawable.ic_fit);
|
|
626
|
+
if (resizeBtn != null) resizeBtn.setImageResource(R.drawable.ic_fit);
|
|
610
627
|
} else {
|
|
611
628
|
styledPlayerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT);
|
|
612
629
|
resizeStatus = AspectRatioFrameLayout.RESIZE_MODE_FIT;
|
|
613
|
-
resizeBtn.setImageResource(R.drawable.ic_expand);
|
|
630
|
+
if (resizeBtn != null) resizeBtn.setImageResource(R.drawable.ic_expand);
|
|
614
631
|
}
|
|
615
632
|
}
|
|
616
633
|
|
|
@@ -645,7 +662,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
645
662
|
* Perform pictureInPictureMode Action
|
|
646
663
|
*/
|
|
647
664
|
private void pictureInPictureMode() {
|
|
648
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
|
|
665
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && packageManager != null && packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
|
|
649
666
|
// Store activity reference to avoid multiple getActivity() calls and check for null
|
|
650
667
|
// Fragment may be detached from activity during transitions or configuration changes
|
|
651
668
|
Activity activity = getActivity();
|
|
@@ -653,9 +670,13 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
653
670
|
Log.v(TAG, "pictureInPictureMode: activity is null");
|
|
654
671
|
return;
|
|
655
672
|
}
|
|
656
|
-
styledPlayerView
|
|
657
|
-
|
|
658
|
-
|
|
673
|
+
if (styledPlayerView != null) {
|
|
674
|
+
styledPlayerView.setUseController(false);
|
|
675
|
+
styledPlayerView.setControllerAutoShow(false);
|
|
676
|
+
}
|
|
677
|
+
if (linearLayout != null) {
|
|
678
|
+
linearLayout.setVisibility(View.INVISIBLE);
|
|
679
|
+
}
|
|
659
680
|
Log.v(TAG, "PIP break 1");
|
|
660
681
|
// require android O or higher
|
|
661
682
|
if (
|
|
@@ -663,9 +684,13 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
663
684
|
) {
|
|
664
685
|
pictureInPictureParams = new PictureInPictureParams.Builder();
|
|
665
686
|
// setup aspect ratio of the PIP window; fall back to 16:9 for audio-only or not-yet-decoded video
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
687
|
+
if (player != null) {
|
|
688
|
+
VideoSize videoSize = player.getVideoSize();
|
|
689
|
+
if (videoSize != VideoSize.UNKNOWN) {
|
|
690
|
+
pictureInPictureParams.setAspectRatio(new Rational(videoSize.width, videoSize.height));
|
|
691
|
+
} else {
|
|
692
|
+
pictureInPictureParams.setAspectRatio(new Rational(16, 9));
|
|
693
|
+
}
|
|
669
694
|
} else {
|
|
670
695
|
pictureInPictureParams.setAspectRatio(new Rational(16, 9));
|
|
671
696
|
}
|
|
@@ -726,12 +751,10 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
726
751
|
if (castContext != null && Boolean.TRUE.equals(chromecast) && castPlayer != null && castPlayer.isCastSessionAvailable()) return;
|
|
727
752
|
|
|
728
753
|
initializePlayer();
|
|
729
|
-
if (player.getCurrentPosition() != 0) {
|
|
754
|
+
if (player != null && player.getCurrentPosition() != 0) {
|
|
730
755
|
firstReadyToPlay = false;
|
|
731
756
|
play();
|
|
732
757
|
}
|
|
733
|
-
} else {
|
|
734
|
-
getActivity().finishAndRemoveTask();
|
|
735
758
|
}
|
|
736
759
|
}
|
|
737
760
|
}
|
|
@@ -743,11 +766,16 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
743
766
|
public void onStop() {
|
|
744
767
|
super.onStop();
|
|
745
768
|
boolean isAppBackground = false;
|
|
746
|
-
if (Boolean.TRUE.equals(bkModeEnabled)) isAppBackground = isApplicationSentToBackground(context);
|
|
769
|
+
if (context != null && Boolean.TRUE.equals(bkModeEnabled)) isAppBackground = isApplicationSentToBackground(context);
|
|
747
770
|
if (isInPictureInPictureMode) {
|
|
748
|
-
linearLayout
|
|
771
|
+
if (linearLayout != null) {
|
|
772
|
+
linearLayout.setVisibility(View.VISIBLE);
|
|
773
|
+
}
|
|
749
774
|
playerExit();
|
|
750
|
-
getActivity()
|
|
775
|
+
Activity activity = getActivity();
|
|
776
|
+
if (activity != null) {
|
|
777
|
+
activity.finishAndRemoveTask();
|
|
778
|
+
}
|
|
751
779
|
}
|
|
752
780
|
}
|
|
753
781
|
|
|
@@ -756,12 +784,12 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
756
784
|
*/
|
|
757
785
|
@Override
|
|
758
786
|
public void onDestroy() {
|
|
759
|
-
isFragmentDestroyed
|
|
760
|
-
|
|
761
|
-
// Release player before calling super to ensure proper cleanup order
|
|
787
|
+
// Release player before setting isFragmentDestroyed and calling super to ensure proper cleanup order
|
|
762
788
|
releasePlayer();
|
|
789
|
+
isFragmentDestroyed = true;
|
|
763
790
|
|
|
764
|
-
// Clean up
|
|
791
|
+
// Clean up handlers
|
|
792
|
+
handler.removeCallbacks(mRunnable);
|
|
765
793
|
if (mainHandler != null) {
|
|
766
794
|
mainHandler.removeCallbacksAndMessages(null);
|
|
767
795
|
mainHandler = null;
|
|
@@ -781,6 +809,18 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
781
809
|
super.onDetach();
|
|
782
810
|
}
|
|
783
811
|
|
|
812
|
+
/**
|
|
813
|
+
* Perform onDestroyView Action
|
|
814
|
+
*/
|
|
815
|
+
@Override
|
|
816
|
+
public void onDestroyView() {
|
|
817
|
+
if (styledPlayerView != null) {
|
|
818
|
+
styledPlayerView.setPlayer(null);
|
|
819
|
+
styledPlayerView = null;
|
|
820
|
+
}
|
|
821
|
+
super.onDestroyView();
|
|
822
|
+
}
|
|
823
|
+
|
|
784
824
|
/**
|
|
785
825
|
* Perform onPause Action
|
|
786
826
|
*/
|
|
@@ -791,7 +831,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
791
831
|
castContext.removeCastStateListener(castStateListener);
|
|
792
832
|
}
|
|
793
833
|
boolean isAppBackground = false;
|
|
794
|
-
if (Boolean.TRUE.equals(bkModeEnabled)) isAppBackground = isApplicationSentToBackground(context);
|
|
834
|
+
if (context != null && Boolean.TRUE.equals(bkModeEnabled)) isAppBackground = isApplicationSentToBackground(context);
|
|
795
835
|
|
|
796
836
|
if (!isInPictureInPictureMode) {
|
|
797
837
|
if (Util.SDK_INT < 24) {
|
|
@@ -807,7 +847,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
807
847
|
}
|
|
808
848
|
}
|
|
809
849
|
} else {
|
|
810
|
-
if (linearLayout.getVisibility() == View.VISIBLE) {
|
|
850
|
+
if (linearLayout != null && linearLayout.getVisibility() == View.VISIBLE) {
|
|
811
851
|
linearLayout.setVisibility(View.INVISIBLE);
|
|
812
852
|
}
|
|
813
853
|
if ((isInPictureInPictureMode || isAppBackground) && player != null) play();
|
|
@@ -843,6 +883,9 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
843
883
|
}
|
|
844
884
|
|
|
845
885
|
private void releasePlayerInternal() {
|
|
886
|
+
if (styledPlayerView != null) {
|
|
887
|
+
styledPlayerView.setPlayer(null);
|
|
888
|
+
}
|
|
846
889
|
if (player != null && !isFragmentDestroyed) {
|
|
847
890
|
try {
|
|
848
891
|
playWhenReady = player.getPlayWhenReady();
|
|
@@ -906,7 +949,9 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
906
949
|
} else {
|
|
907
950
|
isInPictureInPictureMode = false;
|
|
908
951
|
if (
|
|
909
|
-
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
|
952
|
+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
|
953
|
+
packageManager != null &&
|
|
954
|
+
packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
|
|
910
955
|
) {
|
|
911
956
|
boolean isShowControls = showControls != null ? showControls : true;
|
|
912
957
|
if (!isShowControls) {
|
|
@@ -940,9 +985,15 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
940
985
|
* Leave the fullsreen mode and reset the status bar to visible
|
|
941
986
|
*/
|
|
942
987
|
private void showSystemUI() {
|
|
943
|
-
getActivity()
|
|
944
|
-
|
|
945
|
-
|
|
988
|
+
Activity activity = getActivity();
|
|
989
|
+
if (activity != null && activity.getWindow() != null) {
|
|
990
|
+
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
|
991
|
+
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
992
|
+
View decorView = activity.getWindow().getDecorView();
|
|
993
|
+
if (decorView != null) {
|
|
994
|
+
decorView.setSystemUiVisibility(View.VISIBLE);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
946
997
|
}
|
|
947
998
|
|
|
948
999
|
/**
|
|
@@ -954,14 +1005,19 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
954
1005
|
if (styledPlayerView == null) {
|
|
955
1006
|
return;
|
|
956
1007
|
}
|
|
1008
|
+
Context playerContext = context != null ? context : getContext();
|
|
1009
|
+
if (playerContext == null) {
|
|
1010
|
+
Log.w(TAG, "initializePlayer: context is null, skipping");
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
957
1013
|
if (player == null) {
|
|
958
1014
|
ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
|
|
959
|
-
trackSelector = new DefaultTrackSelector(
|
|
1015
|
+
trackSelector = new DefaultTrackSelector(playerContext, videoTrackSelectionFactory);
|
|
960
1016
|
LoadControl loadControl = new DefaultLoadControl();
|
|
961
1017
|
|
|
962
1018
|
// Media3 Migration Note: DefaultBandwidthMeter now configured automatically by default
|
|
963
1019
|
player =
|
|
964
|
-
new ExoPlayer.Builder(
|
|
1020
|
+
new ExoPlayer.Builder(playerContext)
|
|
965
1021
|
.setSeekBackIncrementMs(10000)
|
|
966
1022
|
.setSeekForwardIncrementMs(10000)
|
|
967
1023
|
.setTrackSelector(trackSelector)
|
|
@@ -973,6 +1029,24 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
973
1029
|
|
|
974
1030
|
MediaSource mediaSource = null;
|
|
975
1031
|
if (!Boolean.TRUE.equals(isInternal)) {
|
|
1032
|
+
// Re-derive uri/vType from videoPath on every init. They are cleared by
|
|
1033
|
+
// resetVariables() after release, and onStart/onResume can re-enter
|
|
1034
|
+
// initializePlayer() without going through onCreateView, which was the
|
|
1035
|
+
// sole place they were previously computed (NPE on vType.equals()).
|
|
1036
|
+
if (uri == null && videoPath != null && !videoPath.trim().isEmpty()) {
|
|
1037
|
+
try {
|
|
1038
|
+
uri = Uri.parse(videoPath);
|
|
1039
|
+
} catch (Exception e) {
|
|
1040
|
+
Log.e(TAG, "Invalid video path: " + videoPath, e);
|
|
1041
|
+
uri = null;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
if (vType == null && uri != null) {
|
|
1045
|
+
vType = getVideoType(uri);
|
|
1046
|
+
}
|
|
1047
|
+
if (vType == null) {
|
|
1048
|
+
vType = "";
|
|
1049
|
+
}
|
|
976
1050
|
if (videoPath != null && (videoPath.startsWith("file:///android_asset") || videoPath.startsWith("content://media"))) {
|
|
977
1051
|
mediaSource = buildAssetMediaSource(uri);
|
|
978
1052
|
} else if (videoPath != null && !videoPath.trim().isEmpty()) {
|
|
@@ -1006,24 +1080,31 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1006
1080
|
}
|
|
1007
1081
|
// Media3 Migration Note: MediaSessionConnector replaced with MediaSession.Builder; null check prevents duplicate session ID
|
|
1008
1082
|
if (mediaSession == null) {
|
|
1009
|
-
|
|
1010
|
-
.
|
|
1011
|
-
|
|
1083
|
+
try {
|
|
1084
|
+
mediaSession = new MediaSession.Builder(playerContext, player)
|
|
1085
|
+
.setId("capacitorvideoplayer")
|
|
1086
|
+
.build();
|
|
1087
|
+
} catch (Exception e) {
|
|
1088
|
+
Log.e(TAG, "Failed to build MediaSession", e);
|
|
1089
|
+
}
|
|
1012
1090
|
}
|
|
1013
1091
|
|
|
1014
1092
|
NotificationCenter.defaultCenter().postNotification("initializePlayer", info);
|
|
1015
1093
|
}
|
|
1016
1094
|
|
|
1017
1095
|
private void setSubtitle(boolean transparent) {
|
|
1096
|
+
if (styledPlayerView == null || styledPlayerView.getSubtitleView() == null) {
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1018
1099
|
int foreground;
|
|
1019
1100
|
int background;
|
|
1020
1101
|
if (!transparent) {
|
|
1021
1102
|
foreground = Color.WHITE;
|
|
1022
1103
|
background = Color.BLACK;
|
|
1023
|
-
if (stForeColor.length() > 4 && stForeColor.substring(0, 4).equals("rgba")) {
|
|
1104
|
+
if (stForeColor != null && stForeColor.length() > 4 && stForeColor.substring(0, 4).equals("rgba")) {
|
|
1024
1105
|
foreground = getColorFromRGBA(stForeColor);
|
|
1025
1106
|
}
|
|
1026
|
-
if (stBackColor.length() > 4 && stBackColor.substring(0, 4).equals("rgba")) {
|
|
1107
|
+
if (stBackColor != null && stBackColor.length() > 4 && stBackColor.substring(0, 4).equals("rgba")) {
|
|
1027
1108
|
background = getColorFromRGBA(stBackColor);
|
|
1028
1109
|
}
|
|
1029
1110
|
} else {
|
|
@@ -1035,7 +1116,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1035
1116
|
.setStyle(
|
|
1036
1117
|
new CaptionStyleCompat(foreground, background, Color.TRANSPARENT, CaptionStyleCompat.EDGE_TYPE_NONE, Color.WHITE, null)
|
|
1037
1118
|
);
|
|
1038
|
-
styledPlayerView.getSubtitleView().setFixedTextSize(TypedValue.COMPLEX_UNIT_DIP, stFontSize);
|
|
1119
|
+
styledPlayerView.getSubtitleView().setFixedTextSize(TypedValue.COMPLEX_UNIT_DIP, stFontSize != null ? stFontSize : 16);
|
|
1039
1120
|
styledPlayerView.setShowSubtitleButton(true);
|
|
1040
1121
|
}
|
|
1041
1122
|
|
|
@@ -1043,9 +1124,16 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1043
1124
|
* Build the Asset MediaSource
|
|
1044
1125
|
*/
|
|
1045
1126
|
private MediaSource buildAssetMediaSource(Uri uri) {
|
|
1127
|
+
if (uri == null) {
|
|
1128
|
+
return null;
|
|
1129
|
+
}
|
|
1130
|
+
Context dataContext = context != null ? context : getContext();
|
|
1131
|
+
if (dataContext == null) {
|
|
1132
|
+
return null;
|
|
1133
|
+
}
|
|
1046
1134
|
MediaSource mediaSource = null;
|
|
1047
1135
|
// Media3 Migration Note: DefaultDataSourceFactory replaced with DefaultDataSource.Factory
|
|
1048
|
-
DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(
|
|
1136
|
+
DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(dataContext);
|
|
1049
1137
|
mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(uri));
|
|
1050
1138
|
// Get the subtitles if any
|
|
1051
1139
|
if (sturi != null) {
|
|
@@ -1058,7 +1146,14 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1058
1146
|
* Build the Internal MediaSource
|
|
1059
1147
|
*/
|
|
1060
1148
|
private MediaSource buildInternalMediaSource(Uri uri) {
|
|
1061
|
-
|
|
1149
|
+
if (uri == null) {
|
|
1150
|
+
return null;
|
|
1151
|
+
}
|
|
1152
|
+
Context dataContext = context != null ? context : getContext();
|
|
1153
|
+
if (dataContext == null) {
|
|
1154
|
+
return null;
|
|
1155
|
+
}
|
|
1156
|
+
DataSource.Factory dataSourceFactory = new DefaultDataSource.Factory(dataContext);
|
|
1062
1157
|
return new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(uri));
|
|
1063
1158
|
}
|
|
1064
1159
|
|
|
@@ -1067,6 +1162,13 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1067
1162
|
* @return MediaSource
|
|
1068
1163
|
*/
|
|
1069
1164
|
private MediaSource buildHttpMediaSource() {
|
|
1165
|
+
if (uri == null) {
|
|
1166
|
+
Log.e(TAG, "buildHttpMediaSource: uri is null, cannot build media source");
|
|
1167
|
+
return null;
|
|
1168
|
+
}
|
|
1169
|
+
// vType may still be null if getVideoType was never called (e.g. re-init
|
|
1170
|
+
// after release). Default to progressive rather than NPE on equals().
|
|
1171
|
+
String type = vType != null ? vType : "";
|
|
1070
1172
|
MediaSource mediaSource = null;
|
|
1071
1173
|
|
|
1072
1174
|
DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory();
|
|
@@ -1076,38 +1178,57 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1076
1178
|
httpDataSourceFactory.setAllowCrossProtocolRedirects(true);
|
|
1077
1179
|
|
|
1078
1180
|
// If headers is not null and has data we pass them to the HttpDataSourceFactory
|
|
1079
|
-
if (headers != null
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1181
|
+
if (headers != null) {
|
|
1182
|
+
try {
|
|
1183
|
+
if (headers.length() > 0 && headers.names() != null) {
|
|
1184
|
+
// We map the headers(JSObject) to a Map<String, String>
|
|
1185
|
+
Map<String, String> headersMap = new HashMap<String, String>();
|
|
1186
|
+
for (int i = 0; i < headers.names().length(); i++) {
|
|
1187
|
+
try {
|
|
1188
|
+
String key = headers.names().getString(i);
|
|
1189
|
+
Object value = headers.get(key);
|
|
1190
|
+
if (key != null && value != null) {
|
|
1191
|
+
headersMap.put(key, value.toString());
|
|
1192
|
+
}
|
|
1193
|
+
} catch (JSONException e) {
|
|
1194
|
+
e.printStackTrace();
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
httpDataSourceFactory.setDefaultRequestProperties(headersMap);
|
|
1087
1198
|
}
|
|
1199
|
+
} catch (Exception e) {
|
|
1200
|
+
Log.e(TAG, "Error applying headers", e);
|
|
1088
1201
|
}
|
|
1089
|
-
httpDataSourceFactory.setDefaultRequestProperties(headersMap);
|
|
1090
1202
|
}
|
|
1091
1203
|
|
|
1092
|
-
DataSource.Factory dataSourceFactory
|
|
1204
|
+
DataSource.Factory dataSourceFactory;
|
|
1205
|
+
Context dataContext = context != null ? context : getContext();
|
|
1206
|
+
if (dataContext == null) {
|
|
1207
|
+
Log.e(TAG, "buildHttpMediaSource: context is null, cannot build media source");
|
|
1208
|
+
return null;
|
|
1209
|
+
}
|
|
1210
|
+
dataSourceFactory = new DefaultDataSource.Factory(dataContext, httpDataSourceFactory);
|
|
1093
1211
|
|
|
1094
1212
|
if (
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1213
|
+
"mp4".equals(type) ||
|
|
1214
|
+
"webm".equals(type) ||
|
|
1215
|
+
"ogv".equals(type) ||
|
|
1216
|
+
"3gp".equals(type) ||
|
|
1217
|
+
"flv".equals(type) ||
|
|
1218
|
+
"".equals(type)
|
|
1101
1219
|
) {
|
|
1102
1220
|
mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(uri));
|
|
1103
|
-
} else if (
|
|
1221
|
+
} else if ("dash".equals(type) || "mpd".equals(type)) {
|
|
1104
1222
|
/* adaptive streaming Dash stream */
|
|
1105
1223
|
DashMediaSource.Factory mediaSourceFactory = new DashMediaSource.Factory(dataSourceFactory);
|
|
1106
1224
|
mediaSource = mediaSourceFactory.createMediaSource(MediaItem.fromUri(uri));
|
|
1107
|
-
} else if (
|
|
1225
|
+
} else if ("m3u8".equals(type)) {
|
|
1108
1226
|
mediaSource = new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(uri));
|
|
1109
|
-
} else if (
|
|
1227
|
+
} else if ("ism".equals(type)) {
|
|
1110
1228
|
mediaSource = new SsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(uri));
|
|
1229
|
+
} else {
|
|
1230
|
+
Log.w(TAG, "buildHttpMediaSource: unsupported video type '" + type + "', falling back to progressive");
|
|
1231
|
+
mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(uri));
|
|
1111
1232
|
}
|
|
1112
1233
|
// Get the subtitles if any
|
|
1113
1234
|
if (sturi != null) {
|
|
@@ -1152,13 +1273,27 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1152
1273
|
}
|
|
1153
1274
|
|
|
1154
1275
|
private MediaSource getSubTitle(MediaSource mediaSource, Uri sturi, DataSource.Factory dataSourceFactory) {
|
|
1276
|
+
if (mediaSource == null || sturi == null) {
|
|
1277
|
+
return mediaSource;
|
|
1278
|
+
}
|
|
1155
1279
|
// Create mediaSource with subtitle
|
|
1156
1280
|
MediaSource[] mediaSources = new MediaSource[2];
|
|
1157
1281
|
mediaSources[0] = mediaSource;
|
|
1158
1282
|
String mimeType = getMimeType(sturi);
|
|
1283
|
+
if (mimeType == null || mimeType.isEmpty()) {
|
|
1284
|
+
Log.w(TAG, "getSubTitle: unknown subtitle mime type, skipping subtitles");
|
|
1285
|
+
return mediaSource;
|
|
1286
|
+
}
|
|
1159
1287
|
|
|
1160
1288
|
// We get the language label from the language code
|
|
1161
|
-
String
|
|
1289
|
+
String safeLanguage = language != null ? language : "";
|
|
1290
|
+
String languageLabel;
|
|
1291
|
+
try {
|
|
1292
|
+
languageLabel = Locale.forLanguageTag(safeLanguage).getDisplayLanguage();
|
|
1293
|
+
} catch (Exception e) {
|
|
1294
|
+
Log.e(TAG, "Invalid language tag: " + safeLanguage, e);
|
|
1295
|
+
languageLabel = safeLanguage;
|
|
1296
|
+
}
|
|
1162
1297
|
MediaItem.SubtitleConfiguration subConfig = new MediaItem.SubtitleConfiguration.Builder(sturi)
|
|
1163
1298
|
.setMimeType(mimeType)
|
|
1164
1299
|
.setUri(sturi)
|
|
@@ -1166,7 +1301,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1166
1301
|
.setLabel(languageLabel)
|
|
1167
1302
|
.setRoleFlags(C.ROLE_FLAG_CAPTION)
|
|
1168
1303
|
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
|
|
1169
|
-
.setLanguage(
|
|
1304
|
+
.setLanguage(safeLanguage)
|
|
1170
1305
|
.build();
|
|
1171
1306
|
|
|
1172
1307
|
SingleSampleMediaSource subtitleSource = new SingleSampleMediaSource.Factory(dataSourceFactory)
|
|
@@ -1179,16 +1314,29 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1179
1314
|
}
|
|
1180
1315
|
|
|
1181
1316
|
private String getMimeType(Uri sturi) {
|
|
1182
|
-
|
|
1183
|
-
|
|
1317
|
+
if (sturi == null) {
|
|
1318
|
+
return "";
|
|
1319
|
+
}
|
|
1320
|
+
String lastSegment;
|
|
1321
|
+
try {
|
|
1322
|
+
lastSegment = sturi.getLastPathSegment();
|
|
1323
|
+
} catch (Exception e) {
|
|
1324
|
+
Log.e(TAG, "getMimeType: failed to get last path segment", e);
|
|
1325
|
+
return "";
|
|
1326
|
+
}
|
|
1327
|
+
if (lastSegment == null || lastSegment.isEmpty()) {
|
|
1328
|
+
return "";
|
|
1329
|
+
}
|
|
1330
|
+
int dotIndex = lastSegment.lastIndexOf(".");
|
|
1331
|
+
String extension = (dotIndex != -1 ? lastSegment.substring(dotIndex + 1) : lastSegment).toLowerCase(Locale.ROOT);
|
|
1184
1332
|
String mimeType = "";
|
|
1185
|
-
if (
|
|
1333
|
+
if ("vtt".equals(extension)) {
|
|
1186
1334
|
mimeType = MimeTypes.TEXT_VTT;
|
|
1187
|
-
} else if (
|
|
1335
|
+
} else if ("srt".equals(extension)) {
|
|
1188
1336
|
mimeType = MimeTypes.APPLICATION_SUBRIP;
|
|
1189
|
-
} else if (
|
|
1337
|
+
} else if ("ssa".equals(extension) || "ass".equals(extension)) {
|
|
1190
1338
|
mimeType = MimeTypes.TEXT_SSA;
|
|
1191
|
-
} else if (
|
|
1339
|
+
} else if ("ttml".equals(extension) || "dfxp".equals(extension) || "xml".equals(extension)) {
|
|
1192
1340
|
mimeType = MimeTypes.APPLICATION_TTML;
|
|
1193
1341
|
}
|
|
1194
1342
|
return mimeType;
|
|
@@ -1198,7 +1346,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1198
1346
|
* Fast Forward TV
|
|
1199
1347
|
*/
|
|
1200
1348
|
private void fastForward(long position, int times) {
|
|
1201
|
-
if (position < mDuration - videoStep) {
|
|
1349
|
+
if (player != null && position < mDuration - videoStep) {
|
|
1202
1350
|
if (player.isPlaying()) {
|
|
1203
1351
|
player.setPlayWhenReady(false);
|
|
1204
1352
|
}
|
|
@@ -1211,7 +1359,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1211
1359
|
* Rewind TV
|
|
1212
1360
|
*/
|
|
1213
1361
|
private void rewind(long position, int times) {
|
|
1214
|
-
if (position > videoStep) {
|
|
1362
|
+
if (player != null && position > videoStep) {
|
|
1215
1363
|
if (player.isPlaying()) {
|
|
1216
1364
|
player.setPlayWhenReady(false);
|
|
1217
1365
|
}
|
|
@@ -1224,7 +1372,9 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1224
1372
|
* Play Pause TV
|
|
1225
1373
|
*/
|
|
1226
1374
|
private void play_pause() {
|
|
1227
|
-
|
|
1375
|
+
if (player != null) {
|
|
1376
|
+
player.setPlayWhenReady(!player.isPlaying());
|
|
1377
|
+
}
|
|
1228
1378
|
}
|
|
1229
1379
|
|
|
1230
1380
|
/**
|
|
@@ -1232,7 +1382,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1232
1382
|
* @return boolean
|
|
1233
1383
|
*/
|
|
1234
1384
|
public boolean isPlaying() {
|
|
1235
|
-
return player.isPlaying();
|
|
1385
|
+
return player != null && player.isPlaying();
|
|
1236
1386
|
}
|
|
1237
1387
|
|
|
1238
1388
|
/**
|
|
@@ -1312,6 +1462,9 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1312
1462
|
* @return int in seconds
|
|
1313
1463
|
*/
|
|
1314
1464
|
public int getDuration() {
|
|
1465
|
+
if (player == null) {
|
|
1466
|
+
return 0;
|
|
1467
|
+
}
|
|
1315
1468
|
return player.getDuration() == UNKNOWN_TIME ? 0 : (int) (player.getDuration() / 1000);
|
|
1316
1469
|
}
|
|
1317
1470
|
|
|
@@ -1338,8 +1491,12 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1338
1491
|
|
|
1339
1492
|
try {
|
|
1340
1493
|
if (isInPictureInPictureMode) {
|
|
1341
|
-
styledPlayerView
|
|
1342
|
-
|
|
1494
|
+
if (styledPlayerView != null) {
|
|
1495
|
+
styledPlayerView.setUseController(false);
|
|
1496
|
+
}
|
|
1497
|
+
if (linearLayout != null) {
|
|
1498
|
+
linearLayout.setVisibility(View.INVISIBLE);
|
|
1499
|
+
}
|
|
1343
1500
|
}
|
|
1344
1501
|
long seekPosition = player.getCurrentPosition() == UNKNOWN_TIME
|
|
1345
1502
|
? 0
|
|
@@ -1355,7 +1512,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1355
1512
|
* @return float
|
|
1356
1513
|
*/
|
|
1357
1514
|
public float getVolume() {
|
|
1358
|
-
return player.getVolume();
|
|
1515
|
+
return player != null ? player.getVolume() : curVolume;
|
|
1359
1516
|
}
|
|
1360
1517
|
|
|
1361
1518
|
/**
|
|
@@ -1364,7 +1521,10 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1364
1521
|
*/
|
|
1365
1522
|
public void setVolume(float _volume) {
|
|
1366
1523
|
float volume = Math.min(Math.max(0, _volume), 1L);
|
|
1367
|
-
|
|
1524
|
+
curVolume = volume;
|
|
1525
|
+
if (player != null) {
|
|
1526
|
+
player.setVolume(volume);
|
|
1527
|
+
}
|
|
1368
1528
|
}
|
|
1369
1529
|
|
|
1370
1530
|
/**
|
|
@@ -1381,8 +1541,10 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1381
1541
|
*/
|
|
1382
1542
|
public void setRate(float _rate) {
|
|
1383
1543
|
videoRate = _rate;
|
|
1384
|
-
|
|
1385
|
-
|
|
1544
|
+
if (player != null) {
|
|
1545
|
+
PlaybackParameters param = new PlaybackParameters(videoRate != null ? videoRate : 1.0f);
|
|
1546
|
+
player.setPlaybackParameters(param);
|
|
1547
|
+
}
|
|
1386
1548
|
}
|
|
1387
1549
|
|
|
1388
1550
|
/**
|
|
@@ -1391,11 +1553,13 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1391
1553
|
*/
|
|
1392
1554
|
public void setMuted(boolean _isMuted) {
|
|
1393
1555
|
isMuted = _isMuted;
|
|
1394
|
-
if (
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1556
|
+
if (player != null) {
|
|
1557
|
+
if (isMuted) {
|
|
1558
|
+
curVolume = player.getVolume();
|
|
1559
|
+
player.setVolume(0L);
|
|
1560
|
+
} else {
|
|
1561
|
+
player.setVolume(curVolume);
|
|
1562
|
+
}
|
|
1399
1563
|
}
|
|
1400
1564
|
}
|
|
1401
1565
|
|
|
@@ -1412,7 +1576,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1412
1576
|
* @param button
|
|
1413
1577
|
*/
|
|
1414
1578
|
private void mediaRouteButtonColorWhite(MediaRouteButton button) {
|
|
1415
|
-
if (button == null) return;
|
|
1579
|
+
if (button == null || getContext() == null) return;
|
|
1416
1580
|
Context castContext = new ContextThemeWrapper(getContext(), androidx.mediarouter.R.style.Theme_MediaRouter);
|
|
1417
1581
|
|
|
1418
1582
|
TypedArray a = castContext.obtainStyledAttributes(
|
|
@@ -1423,6 +1587,9 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1423
1587
|
);
|
|
1424
1588
|
Drawable drawable = a.getDrawable(androidx.mediarouter.R.styleable.MediaRouteButton_externalRouteEnabledDrawable);
|
|
1425
1589
|
a.recycle();
|
|
1590
|
+
if (drawable == null) {
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1426
1593
|
DrawableCompat.setTint(drawable, getContext().getResources().getColor(R.color.white));
|
|
1427
1594
|
drawable.setState(button.getDrawableState());
|
|
1428
1595
|
button.setRemoteIndicatorDrawable(drawable);
|
|
@@ -1434,6 +1601,9 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1434
1601
|
* @return video type
|
|
1435
1602
|
*/
|
|
1436
1603
|
private String getVideoType(Uri uri) {
|
|
1604
|
+
if (uri == null) {
|
|
1605
|
+
return "";
|
|
1606
|
+
}
|
|
1437
1607
|
String ret = null;
|
|
1438
1608
|
Object obj = uri.getLastPathSegment();
|
|
1439
1609
|
String lastSegment = (obj == null) ? "" : uri.getLastPathSegment();
|
|
@@ -1442,17 +1612,19 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1442
1612
|
if (lastSegment.length() > 0 && lastSegment.contains(type)) ret = type;
|
|
1443
1613
|
if (ret == null) {
|
|
1444
1614
|
List<String> segments = uri.getPathSegments();
|
|
1445
|
-
if (segments.size() > 0) {
|
|
1615
|
+
if (segments != null && segments.size() > 0) {
|
|
1446
1616
|
String segment;
|
|
1447
|
-
if (segments.get(segments.size() - 1).equals("manifest")) {
|
|
1617
|
+
if (segments.get(segments.size() - 1).equals("manifest") && segments.size() >= 2) {
|
|
1448
1618
|
segment = segments.get(segments.size() - 2);
|
|
1449
1619
|
} else {
|
|
1450
1620
|
segment = segments.get(segments.size() - 1);
|
|
1451
1621
|
}
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1622
|
+
if (segment != null) {
|
|
1623
|
+
for (String sType : supportedFormat) {
|
|
1624
|
+
if (segment.contains(sType)) {
|
|
1625
|
+
ret = sType;
|
|
1626
|
+
break;
|
|
1627
|
+
}
|
|
1456
1628
|
}
|
|
1457
1629
|
}
|
|
1458
1630
|
}
|
|
@@ -1467,7 +1639,6 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1467
1639
|
*/
|
|
1468
1640
|
private void resetVariables() {
|
|
1469
1641
|
vType = null;
|
|
1470
|
-
styledPlayerView = null;
|
|
1471
1642
|
playWhenReady = true;
|
|
1472
1643
|
firstReadyToPlay = true;
|
|
1473
1644
|
isEnded = false;
|
|
@@ -1485,8 +1656,14 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1485
1656
|
* @return boolean
|
|
1486
1657
|
*/
|
|
1487
1658
|
public boolean isApplicationSentToBackground(final Context context) {
|
|
1659
|
+
if (context == null) {
|
|
1660
|
+
return false;
|
|
1661
|
+
}
|
|
1488
1662
|
int pid = android.os.Process.myPid();
|
|
1489
1663
|
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
|
1664
|
+
if (am == null) {
|
|
1665
|
+
return false;
|
|
1666
|
+
}
|
|
1490
1667
|
List<ActivityManager.RunningAppProcessInfo> procInfos = am.getRunningAppProcesses();
|
|
1491
1668
|
if (procInfos != null) {
|
|
1492
1669
|
for (ActivityManager.RunningAppProcessInfo appProcess : procInfos) {
|
|
@@ -1509,6 +1686,9 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1509
1686
|
task.addOnCompleteListener(new OnCompleteListener<CastContext>() {
|
|
1510
1687
|
@Override
|
|
1511
1688
|
public void onComplete(Task<CastContext> task) {
|
|
1689
|
+
if (isFragmentDestroyed || !isAdded() || context == null || mediaRouteButton == null) {
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1512
1692
|
if (task.isSuccessful()) {
|
|
1513
1693
|
castContext = task.getResult();
|
|
1514
1694
|
castPlayer = new CastPlayer(castContext);
|
|
@@ -1525,6 +1705,9 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1525
1705
|
|
|
1526
1706
|
castStateListener =
|
|
1527
1707
|
state -> {
|
|
1708
|
+
if (mediaRouteButton == null) {
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1528
1711
|
if (state == CastState.NO_DEVICES_AVAILABLE) {
|
|
1529
1712
|
mediaRouteButton.setVisibility(View.GONE);
|
|
1530
1713
|
} else {
|
|
@@ -1536,13 +1719,17 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1536
1719
|
CastButtonFactory.setUpMediaRouteButton(context, mediaRouteButton);
|
|
1537
1720
|
|
|
1538
1721
|
MediaMetadata movieMetadata;
|
|
1539
|
-
if (artwork !=
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1722
|
+
if (artwork != null && !artwork.trim().isEmpty()) {
|
|
1723
|
+
MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder()
|
|
1724
|
+
.setTitle(title)
|
|
1725
|
+
.setSubtitle(smallTitle)
|
|
1726
|
+
.setMediaType(MediaMetadata.MEDIA_TYPE_MOVIE);
|
|
1727
|
+
try {
|
|
1728
|
+
metadataBuilder.setArtworkUri(Uri.parse(artwork));
|
|
1729
|
+
} catch (Exception e) {
|
|
1730
|
+
Log.e(TAG, "Invalid artwork uri: " + artwork, e);
|
|
1731
|
+
}
|
|
1732
|
+
movieMetadata = metadataBuilder.build();
|
|
1546
1733
|
new setCastImage().execute();
|
|
1547
1734
|
} else {
|
|
1548
1735
|
movieMetadata = new MediaMetadata.Builder()
|
|
@@ -1562,35 +1749,53 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1562
1749
|
@Override
|
|
1563
1750
|
public void onCastSessionAvailable() {
|
|
1564
1751
|
isCastSession = true;
|
|
1565
|
-
final Long videoPosition = player.getCurrentPosition();
|
|
1566
|
-
if (Boolean.TRUE.equals(pipEnabled)) {
|
|
1752
|
+
final Long videoPosition = player != null ? player.getCurrentPosition() : 0L;
|
|
1753
|
+
if (pipBtn != null && Boolean.TRUE.equals(pipEnabled)) {
|
|
1567
1754
|
pipBtn.setVisibility(View.GONE);
|
|
1568
1755
|
}
|
|
1569
|
-
resizeBtn
|
|
1570
|
-
|
|
1571
|
-
|
|
1756
|
+
if (resizeBtn != null) {
|
|
1757
|
+
resizeBtn.setVisibility(View.GONE);
|
|
1758
|
+
}
|
|
1759
|
+
if (player != null) {
|
|
1760
|
+
player.setPlayWhenReady(false);
|
|
1761
|
+
}
|
|
1762
|
+
if (cast_image != null) {
|
|
1763
|
+
cast_image.setVisibility(View.VISIBLE);
|
|
1764
|
+
}
|
|
1572
1765
|
castPlayer.setMediaItem(mediaItem, videoPosition);
|
|
1573
|
-
styledPlayerView
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1766
|
+
if (styledPlayerView != null) {
|
|
1767
|
+
styledPlayerView.setPlayer(castPlayer);
|
|
1768
|
+
styledPlayerView.setControllerShowTimeoutMs(0);
|
|
1769
|
+
styledPlayerView.setControllerHideOnTouch(false);
|
|
1770
|
+
//We perform a click because for some weird reason, the layout is black until the user clicks on it
|
|
1771
|
+
styledPlayerView.performClick();
|
|
1772
|
+
}
|
|
1578
1773
|
}
|
|
1579
1774
|
|
|
1580
1775
|
@Override
|
|
1581
1776
|
public void onCastSessionUnavailable() {
|
|
1582
1777
|
isCastSession = false;
|
|
1583
|
-
final Long videoPosition = castPlayer.getCurrentPosition();
|
|
1584
|
-
if (Boolean.TRUE.equals(pipEnabled)) {
|
|
1778
|
+
final Long videoPosition = castPlayer != null ? castPlayer.getCurrentPosition() : 0L;
|
|
1779
|
+
if (pipBtn != null && Boolean.TRUE.equals(pipEnabled)) {
|
|
1585
1780
|
pipBtn.setVisibility(View.VISIBLE);
|
|
1586
1781
|
}
|
|
1587
|
-
resizeBtn
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
styledPlayerView
|
|
1782
|
+
if (resizeBtn != null) {
|
|
1783
|
+
resizeBtn.setVisibility(View.VISIBLE);
|
|
1784
|
+
}
|
|
1785
|
+
if (cast_image != null) {
|
|
1786
|
+
cast_image.setVisibility(View.GONE);
|
|
1787
|
+
}
|
|
1788
|
+
if (styledPlayerView != null) {
|
|
1789
|
+
styledPlayerView.setPlayer(player);
|
|
1790
|
+
}
|
|
1791
|
+
if (player != null) {
|
|
1792
|
+
player.setPlayWhenReady(true);
|
|
1793
|
+
player.seekTo(videoPosition);
|
|
1794
|
+
}
|
|
1795
|
+
if (styledPlayerView != null) {
|
|
1796
|
+
styledPlayerView.setControllerShowTimeoutMs(3000);
|
|
1797
|
+
styledPlayerView.setControllerHideOnTouch(true);
|
|
1798
|
+
}
|
|
1594
1799
|
}
|
|
1595
1800
|
}
|
|
1596
1801
|
);
|
|
@@ -1602,12 +1807,12 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1602
1807
|
Map<String, Object> info = new HashMap<String, Object>() {
|
|
1603
1808
|
{
|
|
1604
1809
|
put("fromPlayerId", playerId);
|
|
1605
|
-
put("currentTime", String.valueOf(player.getCurrentPosition() / 1000));
|
|
1810
|
+
put("currentTime", String.valueOf(player != null ? player.getCurrentPosition() / 1000 : 0));
|
|
1606
1811
|
}
|
|
1607
1812
|
};
|
|
1608
1813
|
switch (state) {
|
|
1609
1814
|
case CastPlayer.STATE_READY:
|
|
1610
|
-
if (castPlayer.isPlaying()) {
|
|
1815
|
+
if (castPlayer != null && castPlayer.isPlaying()) {
|
|
1611
1816
|
NotificationCenter.defaultCenter().postNotification("playerItemPlay", info);
|
|
1612
1817
|
} else {
|
|
1613
1818
|
NotificationCenter.defaultCenter().postNotification("playerItemPause", info);
|
|
@@ -1621,11 +1826,17 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1621
1826
|
);
|
|
1622
1827
|
|
|
1623
1828
|
castContext.addCastStateListener(castStateListener);
|
|
1624
|
-
mRouter
|
|
1829
|
+
if (mRouter != null && mSelector != null && mCallback != null) {
|
|
1830
|
+
mRouter.addCallback(mSelector, mCallback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
|
|
1831
|
+
}
|
|
1625
1832
|
|
|
1626
1833
|
} else {
|
|
1627
1834
|
Exception e = task.getException();
|
|
1628
|
-
e
|
|
1835
|
+
if (e != null) {
|
|
1836
|
+
e.printStackTrace();
|
|
1837
|
+
} else {
|
|
1838
|
+
Log.e(TAG, "initializeCastService: CastContext task failed with no exception");
|
|
1839
|
+
}
|
|
1629
1840
|
}
|
|
1630
1841
|
}
|
|
1631
1842
|
});
|
|
@@ -1640,6 +1851,8 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1640
1851
|
}
|
|
1641
1852
|
|
|
1642
1853
|
private void adjustAspectRatio() {
|
|
1643
|
-
styledPlayerView
|
|
1854
|
+
if (styledPlayerView != null) {
|
|
1855
|
+
styledPlayerView.setResizeMode(resizeStatus);
|
|
1856
|
+
}
|
|
1644
1857
|
}
|
|
1645
1858
|
}
|
|
@@ -78,6 +78,9 @@ public class PickerVideoFragment extends Fragment {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
private void loadVideos() {
|
|
81
|
+
if (context == null) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
81
84
|
String[] projection = {
|
|
82
85
|
MediaStore.Video.Media._ID,
|
|
83
86
|
MediaStore.Video.Media.DISPLAY_NAME,
|
|
@@ -92,37 +95,46 @@ public class PickerVideoFragment extends Fragment {
|
|
|
92
95
|
collection = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
Cursor cursor =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
try (Cursor cursor = context.getContentResolver().query(collection, projection, null, null, sortOrder)) {
|
|
99
|
+
if (cursor != null) {
|
|
100
|
+
int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
|
|
101
|
+
int titleColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME);
|
|
102
|
+
int durationColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION);
|
|
100
103
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
while (cursor.moveToNext()) {
|
|
105
|
+
long id = cursor.getLong(idColumn);
|
|
106
|
+
String title = cursor.getString(titleColumn);
|
|
107
|
+
int duration = cursor.getInt(durationColumn);
|
|
105
108
|
|
|
106
|
-
|
|
109
|
+
Uri data = ContentUris.withAppendedId(collection, id);
|
|
107
110
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
111
|
+
String duration_formatted;
|
|
112
|
+
int sec = (duration / 1000) % 60;
|
|
113
|
+
int min = (duration / (1000 * 60)) % 60;
|
|
114
|
+
int hrs = duration / (1000 * 60 * 60);
|
|
112
115
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
if (hrs == 0) {
|
|
117
|
+
duration_formatted = String.valueOf(min).concat(":".concat(String.format(Locale.UK, "%02d", sec)));
|
|
118
|
+
} else {
|
|
119
|
+
duration_formatted = String.valueOf(hrs).concat(
|
|
120
|
+
":".concat(String.format(Locale.UK, "%02d", min).concat(":".concat(String.format(Locale.UK, "%02d", sec))))
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
Log.v(TAG, "**** video " + id + " " + title + " " + duration_formatted + " ****");
|
|
124
|
+
videosList.add(new ModelVideo(id, data, title, duration_formatted));
|
|
122
125
|
}
|
|
123
|
-
Log.v(TAG, "**** video " + id + " " + title + " " + duration_formatted + " ****");
|
|
124
|
-
videosList.add(new ModelVideo(id, data, title, duration_formatted));
|
|
125
126
|
}
|
|
127
|
+
} catch (Exception e) {
|
|
128
|
+
Log.e(TAG, "Error loading videos from media store", e);
|
|
126
129
|
}
|
|
127
130
|
}
|
|
131
|
+
|
|
132
|
+
@Override
|
|
133
|
+
public void onDestroyView() {
|
|
134
|
+
recyclerView = null;
|
|
135
|
+
recyclerViewLayoutManager = null;
|
|
136
|
+
adapterVideoList = null;
|
|
137
|
+
view = null;
|
|
138
|
+
super.onDestroyView();
|
|
139
|
+
}
|
|
128
140
|
}
|