@picsa/capacitor-video-player 8.0.0 → 8.0.1

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.
@@ -41,27 +41,27 @@ public class CapacitorVideoPlayer {
41
41
  ) {
42
42
  FullscreenExoPlayerFragment fsFragment = new FullscreenExoPlayerFragment();
43
43
 
44
- fsFragment.videoPath = videoPath;
45
- fsFragment.videoRate = videoRate;
46
- fsFragment.exitOnEnd = exitOnEnd;
47
- fsFragment.loopOnEnd = loopOnEnd;
48
- fsFragment.pipEnabled = pipEnabled;
49
- fsFragment.bkModeEnabled = bkModeEnabled;
50
- fsFragment.showControls = showControls;
51
- fsFragment.displayMode = displayMode;
52
- fsFragment.subTitle = subTitle;
53
- fsFragment.language = language;
54
- fsFragment.subTitleOptions = subTitleOptions;
55
- fsFragment.headers = headers;
56
- fsFragment.title = title;
57
- fsFragment.smallTitle = smallTitle;
58
- fsFragment.accentColor = accentColor;
59
- fsFragment.chromecast = chromecast;
60
- fsFragment.artwork = artwork;
61
- fsFragment.isTV = isTV;
62
- fsFragment.playerId = playerId;
63
- fsFragment.isInternal = isInternal;
64
- fsFragment.videoId = videoId;
44
+ fsFragment.videoPath = videoPath != null ? videoPath : "";
45
+ fsFragment.videoRate = videoRate != null ? videoRate : 1.0f;
46
+ fsFragment.exitOnEnd = exitOnEnd != null ? exitOnEnd : true;
47
+ fsFragment.loopOnEnd = loopOnEnd != null ? loopOnEnd : false;
48
+ fsFragment.pipEnabled = pipEnabled != null ? pipEnabled : true;
49
+ fsFragment.bkModeEnabled = bkModeEnabled != null ? bkModeEnabled : true;
50
+ fsFragment.showControls = showControls != null ? showControls : true;
51
+ fsFragment.displayMode = displayMode != null ? displayMode : "all";
52
+ fsFragment.subTitle = subTitle != null ? subTitle : "";
53
+ fsFragment.language = language != null ? language : "";
54
+ fsFragment.subTitleOptions = subTitleOptions != null ? subTitleOptions : new JSObject();
55
+ fsFragment.headers = headers != null ? headers : new JSObject();
56
+ fsFragment.title = title != null ? title : "";
57
+ fsFragment.smallTitle = smallTitle != null ? smallTitle : "";
58
+ fsFragment.accentColor = accentColor != null ? accentColor : "";
59
+ fsFragment.chromecast = chromecast != null ? chromecast : true;
60
+ fsFragment.artwork = artwork != null ? artwork : "";
61
+ fsFragment.isTV = isTV != null ? isTV : false;
62
+ fsFragment.playerId = playerId != null ? playerId : "";
63
+ fsFragment.isInternal = isInternal != null ? isInternal : false;
64
+ fsFragment.videoId = videoId != null ? videoId : 0L;
65
65
  return fsFragment;
66
66
  }
67
67
 
@@ -92,6 +92,25 @@ public class CapacitorVideoPlayerPlugin extends Plugin {
92
92
  implementation = new CapacitorVideoPlayer(this.context);
93
93
  this.filesUtils = new FilesUtils(this.context);
94
94
  this.fragmentUtils = new FragmentUtils(getBridge());
95
+ cleanUpOrphanedFragments();
96
+ }
97
+
98
+ private void cleanUpOrphanedFragments() {
99
+ try {
100
+ if (getBridge() != null && getBridge().getActivity() != null) {
101
+ androidx.fragment.app.FragmentManager fm = getBridge().getActivity().getSupportFragmentManager();
102
+ androidx.fragment.app.Fragment fs = fm.findFragmentById(frameLayoutViewId);
103
+ if (fs != null) {
104
+ fm.beginTransaction().remove(fs).commitAllowingStateLoss();
105
+ }
106
+ androidx.fragment.app.Fragment pk = fm.findFragmentById(pickerLayoutViewId);
107
+ if (pk != null) {
108
+ fm.beginTransaction().remove(pk).commitAllowingStateLoss();
109
+ }
110
+ }
111
+ } catch (Exception e) {
112
+ Log.e(TAG, "Error cleaning up orphaned fragments on load", e);
113
+ }
95
114
  }
96
115
 
97
116
  @PermissionCallback
@@ -151,36 +170,24 @@ public class CapacitorVideoPlayerPlugin extends Plugin {
151
170
  videoRate = mRate;
152
171
  }
153
172
  }
154
- Boolean _exitOnEnd = true;
155
- if (call.getData().has("exitOnEnd")) {
156
- _exitOnEnd = call.getBoolean("exitOnEnd");
157
- }
158
- exitOnEnd = _exitOnEnd;
159
- Boolean _loopOnEnd = false;
160
- if (call.getData().has("loopOnEnd")) {
161
- _loopOnEnd = call.getBoolean("loopOnEnd");
162
- }
173
+ Boolean _exitOnEnd = call.getBoolean("exitOnEnd", true);
174
+ exitOnEnd = _exitOnEnd != null ? _exitOnEnd : true;
175
+
176
+ Boolean _loopOnEnd = call.getBoolean("loopOnEnd", false);
177
+ _loopOnEnd = _loopOnEnd != null ? _loopOnEnd : false;
163
178
  if (!exitOnEnd) loopOnEnd = _loopOnEnd;
164
- Boolean _pipEnabled = true;
165
- if (call.getData().has("pipEnabled")) {
166
- _pipEnabled = call.getBoolean("pipEnabled");
167
- }
168
- pipEnabled = _pipEnabled;
169
- Boolean _bkModeEnabled = true;
170
- if (call.getData().has("bkmodeEnabled")) {
171
- _bkModeEnabled = call.getBoolean("bkmodeEnabled");
172
- }
173
- bkModeEnabled = _bkModeEnabled;
174
- Boolean _showControls = true;
175
- if (call.getData().has("showControls")) {
176
- _showControls = call.getBoolean("showControls");
177
- }
178
- showControls = _showControls;
179
- String _displayMode = "all";
180
- if (call.getData().has("displayMode")) {
181
- _displayMode = call.getString("displayMode");
182
- }
183
- displayMode = _displayMode;
179
+
180
+ Boolean _pipEnabled = call.getBoolean("pipEnabled", true);
181
+ pipEnabled = _pipEnabled != null ? _pipEnabled : true;
182
+
183
+ Boolean _bkModeEnabled = call.getBoolean("bkmodeEnabled", true);
184
+ bkModeEnabled = _bkModeEnabled != null ? _bkModeEnabled : true;
185
+
186
+ Boolean _showControls = call.getBoolean("showControls", true);
187
+ showControls = _showControls != null ? _showControls : true;
188
+
189
+ String _displayMode = call.getString("displayMode", "all");
190
+ displayMode = _displayMode != null ? _displayMode : "all";
184
191
  if ("fullscreen".equals(mode)) {
185
192
  fsPlayerId = playerId;
186
193
  url = call.getString("url");
@@ -220,11 +227,8 @@ public class CapacitorVideoPlayerPlugin extends Plugin {
220
227
  _accentColor = call.getString("accentColor");
221
228
  }
222
229
  accentColor = _accentColor;
223
- Boolean _chromecast = true;
224
- if (call.getData().has("chromecast")) {
225
- _chromecast = call.getBoolean("chromecast");
226
- }
227
- chromecast = _chromecast;
230
+ Boolean _chromecast = call.getBoolean("chromecast", true);
231
+ chromecast = _chromecast != null ? _chromecast : true;
228
232
  String _artwork = "";
229
233
  if (call.getData().has("artwork")) {
230
234
  _artwork = call.getString("artwork");
@@ -1033,11 +1037,13 @@ public class CapacitorVideoPlayerPlugin extends Plugin {
1033
1037
  public void run() {
1034
1038
  FrameLayout frameLayoutView = getBridge().getActivity().findViewById(frameLayoutViewId);
1035
1039
 
1036
- if (frameLayoutView != null) {
1037
- ((ViewGroup) getBridge().getWebView().getParent()).removeView(frameLayoutView);
1040
+ if (frameLayoutView != null && frameLayoutView.getParent() != null) {
1041
+ ((ViewGroup) frameLayoutView.getParent()).removeView(frameLayoutView);
1042
+ }
1043
+ if (fsFragment != null) {
1038
1044
  fragmentUtils.removeFragment(fsFragment);
1045
+ fsFragment = null;
1039
1046
  }
1040
- fsFragment = null;
1041
1047
  NotificationCenter.defaultCenter().removeAllNotifications();
1042
1048
  notifyListeners("jeepCapVideoPlayerEnded", data);
1043
1049
  }
@@ -1066,11 +1072,13 @@ public class CapacitorVideoPlayerPlugin extends Plugin {
1066
1072
  public void run() {
1067
1073
  FrameLayout frameLayoutView = getBridge().getActivity().findViewById(frameLayoutViewId);
1068
1074
 
1069
- if (frameLayoutView != null) {
1070
- ((ViewGroup) getBridge().getWebView().getParent()).removeView(frameLayoutView);
1075
+ if (frameLayoutView != null && frameLayoutView.getParent() != null) {
1076
+ ((ViewGroup) frameLayoutView.getParent()).removeView(frameLayoutView);
1077
+ }
1078
+ if (fsFragment != null) {
1071
1079
  fragmentUtils.removeFragment(fsFragment);
1080
+ fsFragment = null;
1072
1081
  }
1073
- fsFragment = null;
1074
1082
  NotificationCenter.defaultCenter().removeAllNotifications();
1075
1083
  notifyListeners("jeepCapVideoPlayerExit", data);
1076
1084
  }
@@ -1089,11 +1097,13 @@ public class CapacitorVideoPlayerPlugin extends Plugin {
1089
1097
  long videoId = (Long) this.getInfo().get("videoId");
1090
1098
  // Get the previously saved call
1091
1099
  FrameLayout pickerLayoutView = getBridge().getActivity().findViewById(pickerLayoutViewId);
1092
- if (pickerLayoutView != null) {
1093
- ((ViewGroup) getBridge().getWebView().getParent()).removeView(pickerLayoutView);
1100
+ if (pickerLayoutView != null && pickerLayoutView.getParent() != null) {
1101
+ ((ViewGroup) pickerLayoutView.getParent()).removeView(pickerLayoutView);
1102
+ }
1103
+ if (pkFragment != null) {
1094
1104
  fragmentUtils.removeFragment(pkFragment);
1105
+ pkFragment = null;
1095
1106
  }
1096
- pkFragment = null;
1097
1107
  if (videoId != -1) {
1098
1108
  Log.v(TAG, "§§§§ Notification videoPathInternalReady chromecast: " + chromecast);
1099
1109
  createFullScreenFragment(
@@ -102,27 +102,27 @@ import org.json.JSONException;
102
102
 
103
103
  public class FullscreenExoPlayerFragment extends Fragment {
104
104
 
105
- public String videoPath;
106
- public Float videoRate;
107
- public String playerId;
108
- public String subTitle;
109
- public String language;
110
- public JSObject subTitleOptions;
111
- public JSObject headers;
112
- public Boolean isTV;
113
- public Boolean isInternal;
114
- public Long videoId;
115
- public Boolean exitOnEnd;
116
- public Boolean loopOnEnd;
117
- public Boolean pipEnabled;
118
- public Boolean bkModeEnabled;
119
- public Boolean showControls;
105
+ public String videoPath = "";
106
+ public Float videoRate = 1.0f;
107
+ public String playerId = "";
108
+ public String subTitle = "";
109
+ public String language = "";
110
+ public JSObject subTitleOptions = new JSObject();
111
+ public JSObject headers = new JSObject();
112
+ public Boolean isTV = false;
113
+ public Boolean isInternal = false;
114
+ public Long videoId = 0L;
115
+ public Boolean exitOnEnd = true;
116
+ public Boolean loopOnEnd = false;
117
+ public Boolean pipEnabled = true;
118
+ public Boolean bkModeEnabled = true;
119
+ public Boolean showControls = true;
120
120
  public String displayMode = "all";
121
- public String title;
122
- public String smallTitle;
123
- public String accentColor;
124
- public Boolean chromecast;
125
- public String artwork;
121
+ public String title = "";
122
+ public String smallTitle = "";
123
+ public String accentColor = "";
124
+ public Boolean chromecast = true;
125
+ public String artwork = "";
126
126
 
127
127
  private static final String TAG = FullscreenExoPlayerFragment.class.getName();
128
128
  public static final long UNKNOWN_TIME = -1L;
@@ -212,6 +212,19 @@ public class FullscreenExoPlayerFragment extends Fragment {
212
212
  context = requireContext();
213
213
 
214
214
  packageManager = context.getPackageManager();
215
+
216
+ // Check if this fragment is an orphaned recreation (e.g. after process death / activity recreation)
217
+ // where no video path or internal media has been provided by the plugin.
218
+ boolean isInternalVideo = Boolean.TRUE.equals(isInternal);
219
+ if ((videoPath == null || videoPath.trim().isEmpty()) && !isInternalVideo) {
220
+ Log.w(TAG, "FullscreenExoPlayerFragment recreated without video source; removing orphaned fragment.");
221
+ try {
222
+ getParentFragmentManager().beginTransaction().remove(this).commitAllowingStateLoss();
223
+ } catch (Exception e) {
224
+ Log.e(TAG, "Failed to remove orphaned fragment", e);
225
+ }
226
+ return new View(context);
227
+ }
215
228
 
216
229
  // Initialize thread safety components
217
230
  mainHandler = new Handler(Looper.getMainLooper());
@@ -242,34 +255,41 @@ public class FullscreenExoPlayerFragment extends Fragment {
242
255
  styledPlayerView.setShowRewindButton(false);
243
256
 
244
257
  Activity mAct = requireActivity();
245
- if (displayMode.equals("landscape")) {
258
+ if ("landscape".equalsIgnoreCase(displayMode)) {
246
259
  mAct.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
247
260
  }
248
- if (displayMode.equals("portrait")) {
261
+ if ("portrait".equalsIgnoreCase(displayMode)) {
249
262
  mAct.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
250
263
  }
251
- if (!showControls) {
264
+ boolean isShowControls = showControls != null ? showControls : true;
265
+ if (!isShowControls) {
252
266
  styledPlayerView.setUseController(false);
253
267
  } else {
254
268
  styledPlayerView.setUseController(true);
255
269
  }
256
270
 
257
- if (!chromecast) {
271
+ boolean isChromecast = chromecast != null ? chromecast : true;
272
+ if (!isChromecast) {
258
273
  mediaRouteButton.setVisibility(View.GONE);
259
274
  } else {
260
275
  initializeCastService();
261
276
  }
262
277
 
263
- if (title != "") {
278
+ if (title != null && !title.trim().isEmpty()) {
264
279
  header_tv.setText(title);
265
280
  }
266
- if (smallTitle != "") {
281
+ if (smallTitle != null && !smallTitle.trim().isEmpty()) {
267
282
  header_below.setText(smallTitle);
268
283
  }
269
- if (accentColor != "") {
270
- Pbar.getIndeterminateDrawable().setColorFilter(Color.parseColor(accentColor), android.graphics.PorterDuff.Mode.MULTIPLY);
271
- exo_progress.setPlayedColor(Color.parseColor(accentColor));
272
- exo_progress.setScrubberColor(Color.parseColor(accentColor));
284
+ if (accentColor != null && !accentColor.trim().isEmpty()) {
285
+ try {
286
+ int parsedColor = Color.parseColor(accentColor);
287
+ Pbar.getIndeterminateDrawable().setColorFilter(parsedColor, android.graphics.PorterDuff.Mode.MULTIPLY);
288
+ exo_progress.setPlayedColor(parsedColor);
289
+ exo_progress.setScrubberColor(parsedColor);
290
+ } catch (Exception e) {
291
+ Log.e(TAG, "Invalid accent color: " + accentColor, e);
292
+ }
273
293
  }
274
294
 
275
295
  closeBtn = view.findViewById(R.id.exo_close);
@@ -312,7 +332,8 @@ public class FullscreenExoPlayerFragment extends Fragment {
312
332
  stateString = "ExoPlayer.STATE_READY -";
313
333
  Pbar.setVisibility(View.GONE);
314
334
  playerReady = true;
315
- if (!showControls) {
335
+ boolean isShowControls = showControls != null ? showControls : true;
336
+ if (!isShowControls) {
316
337
  styledPlayerView.setUseController(false);
317
338
  } else {
318
339
  styledPlayerView.setUseController(true);
@@ -345,7 +366,8 @@ public class FullscreenExoPlayerFragment extends Fragment {
345
366
  NotificationCenter.defaultCenter().postNotification("playerItemPlay", info);
346
367
  resizeBtn.setVisibility(View.VISIBLE);
347
368
 
348
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && pipEnabled) {
369
+ boolean isPip = Boolean.TRUE.equals(pipEnabled);
370
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && isPip) {
349
371
  pipBtn.setVisibility(View.VISIBLE);
350
372
  }
351
373
  } else {
@@ -361,7 +383,8 @@ public class FullscreenExoPlayerFragment extends Fragment {
361
383
  player.seekTo(0);
362
384
  player.setVolume(curVolume);
363
385
  player.setPlayWhenReady(false);
364
- if (exitOnEnd) {
386
+ boolean isExitOnEnd = exitOnEnd != null ? exitOnEnd : true;
387
+ if (isExitOnEnd) {
365
388
  releasePlayer();
366
389
  /*
367
390
  Activity mAct = getActivity();
@@ -380,25 +403,32 @@ public class FullscreenExoPlayerFragment extends Fragment {
380
403
  }
381
404
  };
382
405
 
383
- if (isTV) {
406
+ boolean isDeviceTV = Boolean.TRUE.equals(isTV);
407
+ if (isDeviceTV) {
384
408
  Toast.makeText(context, "Device is a TV ", Toast.LENGTH_SHORT).show();
385
409
  }
386
410
 
387
- if (!isInternal) {
388
- uri = Uri.parse(videoPath);
389
- sturi = subTitle != null ? Uri.parse(subTitle) : null;
411
+ if (!Boolean.TRUE.equals(isInternal)) {
412
+ if (videoPath != null && !videoPath.trim().isEmpty()) {
413
+ uri = Uri.parse(videoPath);
414
+ }
415
+ sturi = (subTitle != null && !subTitle.trim().isEmpty()) ? Uri.parse(subTitle) : null;
390
416
 
391
- stForeColor = subTitleOptions.has("foregroundColor") ? subTitleOptions.getString("foregroundColor") : "rgba(255,255,255,1)";
392
- stBackColor = subTitleOptions.has("backgroundColor") ? subTitleOptions.getString("backgroundColor") : "rgba(0,0,0,1)";
393
- stFontSize = subTitleOptions.has("fontSize") ? subTitleOptions.getInteger("fontSize") : 16;
417
+ if (subTitleOptions != null) {
418
+ stForeColor = subTitleOptions.has("foregroundColor") ? subTitleOptions.getString("foregroundColor") : "rgba(255,255,255,1)";
419
+ stBackColor = subTitleOptions.has("backgroundColor") ? subTitleOptions.getString("backgroundColor") : "rgba(0,0,0,1)";
420
+ stFontSize = subTitleOptions.has("fontSize") ? subTitleOptions.getInteger("fontSize") : 16;
421
+ }
394
422
  // get video type
395
- vType = getVideoType(uri);
423
+ if (uri != null) {
424
+ vType = getVideoType(uri);
425
+ }
396
426
  Log.v(TAG, "display url: " + uri);
397
427
  Log.v(TAG, "display subtitle url: " + sturi);
398
428
  Log.v(TAG, "display isTV: " + isTV);
399
429
  Log.v(TAG, "display vType: " + vType);
400
430
  }
401
- if (uri != null || isInternal) {
431
+ if (uri != null || Boolean.TRUE.equals(isInternal)) {
402
432
  // go fullscreen
403
433
  getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
404
434
  getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
@@ -423,7 +453,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
423
453
  if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME) {
424
454
  Log.v(TAG, "$$$$ Going to backpress $$$$");
425
455
  backPressed();
426
- } else if (isTV) {
456
+ } else if (Boolean.TRUE.equals(isTV)) {
427
457
  long videoPosition = (player == null) ? 0 : player.getCurrentPosition();
428
458
  switch (keyCode) {
429
459
  case KeyEvent.KEYCODE_DPAD_RIGHT:
@@ -558,9 +588,9 @@ public class FullscreenExoPlayerFragment extends Fragment {
558
588
  !isInPictureInPictureMode &&
559
589
  Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
560
590
  packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) &&
561
- isPIPModeeEnabled &&
562
- pipEnabled &&
563
- playerReady // <- playerReady: this prevents a crash if the user presses back before the player is ready (when enters in pip mode and tries to get the aspect ratio)
591
+ Boolean.TRUE.equals(isPIPModeeEnabled) &&
592
+ Boolean.TRUE.equals(pipEnabled) &&
593
+ Boolean.TRUE.equals(playerReady) // <- playerReady: this prevents a crash if the user presses back before the player is ready (when enters in pip mode and tries to get the aspect ratio)
564
594
  ) {
565
595
  pictureInPictureMode();
566
596
  } else {
@@ -693,7 +723,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
693
723
  if (Util.SDK_INT >= 24) {
694
724
  if (styledPlayerView != null) {
695
725
  // If cast is playing then it doesn't start the local player once get backs from background
696
- if (castContext != null && chromecast && castPlayer.isCastSessionAvailable()) return;
726
+ if (castContext != null && Boolean.TRUE.equals(chromecast) && castPlayer != null && castPlayer.isCastSessionAvailable()) return;
697
727
 
698
728
  initializePlayer();
699
729
  if (player.getCurrentPosition() != 0) {
@@ -713,7 +743,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
713
743
  public void onStop() {
714
744
  super.onStop();
715
745
  boolean isAppBackground = false;
716
- if (bkModeEnabled) isAppBackground = isApplicationSentToBackground(context);
746
+ if (Boolean.TRUE.equals(bkModeEnabled)) isAppBackground = isApplicationSentToBackground(context);
717
747
  if (isInPictureInPictureMode) {
718
748
  linearLayout.setVisibility(View.VISIBLE);
719
749
  playerExit();
@@ -737,7 +767,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
737
767
  mainHandler = null;
738
768
  }
739
769
 
740
- if (chromecast) mRouter.removeCallback(mCallback);
770
+ if (Boolean.TRUE.equals(chromecast) && mRouter != null) mRouter.removeCallback(mCallback);
741
771
 
742
772
  super.onDestroy();
743
773
  }
@@ -757,9 +787,11 @@ public class FullscreenExoPlayerFragment extends Fragment {
757
787
  @Override
758
788
  public void onPause() {
759
789
  super.onPause();
760
- if (chromecast) castContext.removeCastStateListener(castStateListener);
790
+ if (Boolean.TRUE.equals(chromecast) && castContext != null && castStateListener != null) {
791
+ castContext.removeCastStateListener(castStateListener);
792
+ }
761
793
  boolean isAppBackground = false;
762
- if (bkModeEnabled) isAppBackground = isApplicationSentToBackground(context);
794
+ if (Boolean.TRUE.equals(bkModeEnabled)) isAppBackground = isApplicationSentToBackground(context);
763
795
 
764
796
  if (!isInPictureInPictureMode) {
765
797
  if (Util.SDK_INT < 24) {
@@ -842,7 +874,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
842
874
 
843
875
  // Move chromecast handling outside the player null check
844
876
  // and add null check for castPlayer
845
- if (chromecast && castPlayer != null) {
877
+ if (Boolean.TRUE.equals(chromecast) && castPlayer != null) {
846
878
  try {
847
879
  castPlayer.release();
848
880
  } catch (Exception e) {
@@ -870,7 +902,8 @@ public class FullscreenExoPlayerFragment extends Fragment {
870
902
  if (
871
903
  Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
872
904
  ) {
873
- if (!showControls) {
905
+ boolean isShowControls = showControls != null ? showControls : true;
906
+ if (!isShowControls) {
874
907
  styledPlayerView.setUseController(false);
875
908
  } else {
876
909
  styledPlayerView.setUseController(true);
@@ -927,23 +960,26 @@ public class FullscreenExoPlayerFragment extends Fragment {
927
960
 
928
961
  styledPlayerView.setPlayer(player);
929
962
 
930
- MediaSource mediaSource;
931
- if (!isInternal) {
932
- if (videoPath.substring(0, 21).equals("file:///android_asset") || videoPath.substring(0, 15).equals("content://media")) {
963
+ MediaSource mediaSource = null;
964
+ if (!Boolean.TRUE.equals(isInternal)) {
965
+ if (videoPath != null && (videoPath.startsWith("file:///android_asset") || videoPath.startsWith("content://media"))) {
933
966
  mediaSource = buildAssetMediaSource(uri);
934
- } else {
967
+ } else if (videoPath != null && !videoPath.trim().isEmpty()) {
935
968
  mediaSource = buildHttpMediaSource();
936
969
  }
937
970
  } else {
938
- Uri videoUri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, videoId);
939
- mediaSource = buildInternalMediaSource(videoUri);
971
+ if (videoId != null && videoId > 0) {
972
+ Uri videoUri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, videoId);
973
+ mediaSource = buildInternalMediaSource(videoUri);
974
+ }
940
975
  }
941
976
 
942
977
  if (mediaSource != null) {
943
978
  player.setAudioAttributes(AudioAttributes.DEFAULT, true);
944
979
  player.addListener(listener);
945
980
  player.prepare(mediaSource, false, false);
946
- if (loopOnEnd) {
981
+ boolean isLoop = Boolean.TRUE.equals(loopOnEnd);
982
+ if (isLoop) {
947
983
  player.setRepeatMode(player.REPEAT_MODE_ONE);
948
984
  } else {
949
985
  player.setRepeatMode(player.REPEAT_MODE_OFF);
@@ -1085,14 +1121,21 @@ public class FullscreenExoPlayerFragment extends Fragment {
1085
1121
 
1086
1122
  private int getColorFromRGBA(String rgbaColor) {
1087
1123
  int ret = 0;
1088
- String color = rgbaColor.substring(rgbaColor.indexOf("(") + 1, rgbaColor.indexOf(")"));
1089
- List<String> colors = Arrays.asList(color.split(","));
1090
- if (colors.size() == 4) {
1091
- ret =
1092
- (Math.round(Float.parseFloat(colors.get(3).trim()) * 255) & 0xff) << 24 |
1093
- (Integer.parseInt(colors.get(0).trim()) & 0xff) << 16 |
1094
- (Integer.parseInt(colors.get(1).trim()) & 0xff) << 8 |
1095
- (Integer.parseInt(colors.get(2).trim()) & 0xff);
1124
+ if (rgbaColor == null || !rgbaColor.contains("(") || !rgbaColor.contains(")")) {
1125
+ return ret;
1126
+ }
1127
+ try {
1128
+ String color = rgbaColor.substring(rgbaColor.indexOf("(") + 1, rgbaColor.indexOf(")"));
1129
+ List<String> colors = Arrays.asList(color.split(","));
1130
+ if (colors.size() == 4) {
1131
+ ret =
1132
+ (Math.round(Float.parseFloat(colors.get(3).trim()) * 255) & 0xff) << 24 |
1133
+ (Integer.parseInt(colors.get(0).trim()) & 0xff) << 16 |
1134
+ (Integer.parseInt(colors.get(1).trim()) & 0xff) << 8 |
1135
+ (Integer.parseInt(colors.get(2).trim()) & 0xff);
1136
+ }
1137
+ } catch (Exception e) {
1138
+ Log.e(TAG, "Error parsing rgbaColor: " + rgbaColor, e);
1096
1139
  }
1097
1140
  return ret;
1098
1141
  }
@@ -1208,7 +1251,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
1208
1251
  private void playInternal() {
1209
1252
  if (player != null && !isFragmentDestroyed) {
1210
1253
  try {
1211
- PlaybackParameters param = new PlaybackParameters(videoRate);
1254
+ PlaybackParameters param = new PlaybackParameters(videoRate != null ? videoRate : 1.0f);
1212
1255
  player.setPlaybackParameters(param);
1213
1256
 
1214
1257
  /* If the user start the cast before the player is ready and playing, then the video will start
@@ -1318,7 +1361,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
1318
1361
  * @return float
1319
1362
  */
1320
1363
  public float getRate() {
1321
- return videoRate;
1364
+ return videoRate != null ? videoRate : 1.0f;
1322
1365
  }
1323
1366
 
1324
1367
  /**
@@ -1509,7 +1552,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
1509
1552
  public void onCastSessionAvailable() {
1510
1553
  isCastSession = true;
1511
1554
  final Long videoPosition = player.getCurrentPosition();
1512
- if (pipEnabled) {
1555
+ if (Boolean.TRUE.equals(pipEnabled)) {
1513
1556
  pipBtn.setVisibility(View.GONE);
1514
1557
  }
1515
1558
  resizeBtn.setVisibility(View.GONE);
@@ -1527,7 +1570,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
1527
1570
  public void onCastSessionUnavailable() {
1528
1571
  isCastSession = false;
1529
1572
  final Long videoPosition = castPlayer.getCurrentPosition();
1530
- if (pipEnabled) {
1573
+ if (Boolean.TRUE.equals(pipEnabled)) {
1531
1574
  pipBtn.setVisibility(View.VISIBLE);
1532
1575
  }
1533
1576
  resizeBtn.setVisibility(View.VISIBLE);
@@ -44,7 +44,16 @@ public class PickerVideoFragment extends Fragment {
44
44
  @SuppressWarnings("serial")
45
45
  @RequiresApi(api = Build.VERSION_CODES.M)
46
46
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
47
- context = container.getContext();
47
+ context = container != null ? container.getContext() : requireContext();
48
+ if (container == null) {
49
+ Log.w(TAG, "PickerVideoFragment recreated without container; removing orphaned fragment.");
50
+ try {
51
+ getParentFragmentManager().beginTransaction().remove(this).commitAllowingStateLoss();
52
+ } catch (Exception e) {
53
+ Log.e(TAG, "Failed to remove orphaned picker fragment", e);
54
+ }
55
+ return new View(context);
56
+ }
48
57
  // Inflate the layout for this fragment
49
58
  view = inflater.inflate(R.layout.fragment_picker_video, container, false);
50
59
  loadVideos();
@@ -1,5 +1,6 @@
1
1
  package com.jeep.plugin.capacitor.capacitorvideoplayer.Utilities;
2
2
 
3
+ import android.util.Log;
3
4
  import androidx.fragment.app.Fragment;
4
5
  import androidx.fragment.app.FragmentManager;
5
6
  import androidx.fragment.app.FragmentTransaction;
@@ -7,6 +8,7 @@ import com.getcapacitor.Bridge;
7
8
 
8
9
  public class FragmentUtils {
9
10
 
11
+ private static final String TAG = "FragmentUtils";
10
12
  private Bridge bridge;
11
13
 
12
14
  public FragmentUtils(Bridge bridge) {
@@ -14,19 +16,33 @@ public class FragmentUtils {
14
16
  }
15
17
 
16
18
  public void loadFragment(Fragment vpFragment, int frameLayoutId) {
17
- // create a FragmentManager
18
- FragmentManager fm = bridge.getActivity().getSupportFragmentManager();
19
- // create a FragmentTransaction to begin the transaction and replace the Fragment
20
- FragmentTransaction fragmentTransaction = fm.beginTransaction();
21
- // replace the FrameLayout with new Fragment
22
- fragmentTransaction.replace(frameLayoutId, vpFragment);
23
- fragmentTransaction.commit(); // save the changes
19
+ if (vpFragment == null || bridge == null || bridge.getActivity() == null) {
20
+ return;
21
+ }
22
+ try {
23
+ // create a FragmentManager
24
+ FragmentManager fm = bridge.getActivity().getSupportFragmentManager();
25
+ // create a FragmentTransaction to begin the transaction and replace the Fragment
26
+ FragmentTransaction fragmentTransaction = fm.beginTransaction();
27
+ // replace the FrameLayout with new Fragment
28
+ fragmentTransaction.replace(frameLayoutId, vpFragment);
29
+ fragmentTransaction.commitAllowingStateLoss(); // save the changes
30
+ } catch (Exception e) {
31
+ Log.e(TAG, "Error loading fragment", e);
32
+ }
24
33
  }
25
34
 
26
35
  public void removeFragment(/*VideoPlayerFragmentFullscreenExoPlayer*/Fragment vpFragment) {
27
- FragmentManager fm = bridge.getActivity().getSupportFragmentManager();
28
- FragmentTransaction fragmentTransaction = fm.beginTransaction();
29
- fragmentTransaction.remove(vpFragment);
30
- fragmentTransaction.commit();
36
+ if (vpFragment == null || bridge == null || bridge.getActivity() == null) {
37
+ return;
38
+ }
39
+ try {
40
+ FragmentManager fm = bridge.getActivity().getSupportFragmentManager();
41
+ FragmentTransaction fragmentTransaction = fm.beginTransaction();
42
+ fragmentTransaction.remove(vpFragment);
43
+ fragmentTransaction.commitAllowingStateLoss();
44
+ } catch (Exception e) {
45
+ Log.e(TAG, "Error removing fragment", e);
46
+ }
31
47
  }
32
48
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@picsa/capacitor-video-player",
3
- "version": "8.0.0",
3
+ "version": "8.0.1",
4
4
  "description": "custom Native Capacitor plugin to play a video",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",