@picsa/capacitor-video-player 8.0.0 → 8.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.
- package/android/src/main/java/com/jeep/plugin/capacitor/capacitorvideoplayer/CapacitorVideoPlayer.java +21 -21
- package/android/src/main/java/com/jeep/plugin/capacitor/capacitorvideoplayer/CapacitorVideoPlayerPlugin.java +53 -43
- package/android/src/main/java/com/jeep/plugin/capacitor/capacitorvideoplayer/FullscreenExoPlayerFragment.java +126 -72
- package/android/src/main/java/com/jeep/plugin/capacitor/capacitorvideoplayer/PickerVideo/PickerVideoFragment.java +10 -1
- package/android/src/main/java/com/jeep/plugin/capacitor/capacitorvideoplayer/Utilities/FragmentUtils.java +27 -11
- package/package.json +1 -1
|
@@ -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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
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)
|
|
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)
|
|
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)
|
|
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 (
|
|
258
|
+
if ("landscape".equalsIgnoreCase(displayMode)) {
|
|
246
259
|
mAct.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
|
247
260
|
}
|
|
248
|
-
if (
|
|
261
|
+
if ("portrait".equalsIgnoreCase(displayMode)) {
|
|
249
262
|
mAct.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
|
250
263
|
}
|
|
251
|
-
|
|
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
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
389
|
-
|
|
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
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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
|
-
|
|
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
|
|
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) {
|
|
@@ -860,6 +892,12 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
860
892
|
public void onResume() {
|
|
861
893
|
super.onResume();
|
|
862
894
|
//if (chromecast && castContext != null) castContext.addCastStateListener(castStateListener);
|
|
895
|
+
// Skip resume handling when the view was never inflated (orphaned fragment
|
|
896
|
+
// recreation after process death returns an empty View in onCreateView, so
|
|
897
|
+
// styledPlayerView stays null while async removal is still pending).
|
|
898
|
+
if (styledPlayerView == null) {
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
863
901
|
if (!isInPictureInPictureMode) {
|
|
864
902
|
hideSystemUi();
|
|
865
903
|
if ((Util.SDK_INT < 24 || player == null)) {
|
|
@@ -870,7 +908,8 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
870
908
|
if (
|
|
871
909
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
|
|
872
910
|
) {
|
|
873
|
-
|
|
911
|
+
boolean isShowControls = showControls != null ? showControls : true;
|
|
912
|
+
if (!isShowControls) {
|
|
874
913
|
styledPlayerView.setUseController(false);
|
|
875
914
|
} else {
|
|
876
915
|
styledPlayerView.setUseController(true);
|
|
@@ -910,6 +949,11 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
910
949
|
* Initialize the player
|
|
911
950
|
*/
|
|
912
951
|
private void initializePlayer() {
|
|
952
|
+
// Guard against orphaned fragment recreations where onCreateView returned
|
|
953
|
+
// an empty View without inflating the layout (styledPlayerView is null).
|
|
954
|
+
if (styledPlayerView == null) {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
913
957
|
if (player == null) {
|
|
914
958
|
ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
|
|
915
959
|
trackSelector = new DefaultTrackSelector(context, videoTrackSelectionFactory);
|
|
@@ -927,23 +971,26 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
927
971
|
|
|
928
972
|
styledPlayerView.setPlayer(player);
|
|
929
973
|
|
|
930
|
-
MediaSource mediaSource;
|
|
931
|
-
if (!isInternal) {
|
|
932
|
-
if (videoPath
|
|
974
|
+
MediaSource mediaSource = null;
|
|
975
|
+
if (!Boolean.TRUE.equals(isInternal)) {
|
|
976
|
+
if (videoPath != null && (videoPath.startsWith("file:///android_asset") || videoPath.startsWith("content://media"))) {
|
|
933
977
|
mediaSource = buildAssetMediaSource(uri);
|
|
934
|
-
} else {
|
|
978
|
+
} else if (videoPath != null && !videoPath.trim().isEmpty()) {
|
|
935
979
|
mediaSource = buildHttpMediaSource();
|
|
936
980
|
}
|
|
937
981
|
} else {
|
|
938
|
-
|
|
939
|
-
|
|
982
|
+
if (videoId != null && videoId > 0) {
|
|
983
|
+
Uri videoUri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, videoId);
|
|
984
|
+
mediaSource = buildInternalMediaSource(videoUri);
|
|
985
|
+
}
|
|
940
986
|
}
|
|
941
987
|
|
|
942
988
|
if (mediaSource != null) {
|
|
943
989
|
player.setAudioAttributes(AudioAttributes.DEFAULT, true);
|
|
944
990
|
player.addListener(listener);
|
|
945
991
|
player.prepare(mediaSource, false, false);
|
|
946
|
-
|
|
992
|
+
boolean isLoop = Boolean.TRUE.equals(loopOnEnd);
|
|
993
|
+
if (isLoop) {
|
|
947
994
|
player.setRepeatMode(player.REPEAT_MODE_ONE);
|
|
948
995
|
} else {
|
|
949
996
|
player.setRepeatMode(player.REPEAT_MODE_OFF);
|
|
@@ -1085,14 +1132,21 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1085
1132
|
|
|
1086
1133
|
private int getColorFromRGBA(String rgbaColor) {
|
|
1087
1134
|
int ret = 0;
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1135
|
+
if (rgbaColor == null || !rgbaColor.contains("(") || !rgbaColor.contains(")")) {
|
|
1136
|
+
return ret;
|
|
1137
|
+
}
|
|
1138
|
+
try {
|
|
1139
|
+
String color = rgbaColor.substring(rgbaColor.indexOf("(") + 1, rgbaColor.indexOf(")"));
|
|
1140
|
+
List<String> colors = Arrays.asList(color.split(","));
|
|
1141
|
+
if (colors.size() == 4) {
|
|
1142
|
+
ret =
|
|
1143
|
+
(Math.round(Float.parseFloat(colors.get(3).trim()) * 255) & 0xff) << 24 |
|
|
1144
|
+
(Integer.parseInt(colors.get(0).trim()) & 0xff) << 16 |
|
|
1145
|
+
(Integer.parseInt(colors.get(1).trim()) & 0xff) << 8 |
|
|
1146
|
+
(Integer.parseInt(colors.get(2).trim()) & 0xff);
|
|
1147
|
+
}
|
|
1148
|
+
} catch (Exception e) {
|
|
1149
|
+
Log.e(TAG, "Error parsing rgbaColor: " + rgbaColor, e);
|
|
1096
1150
|
}
|
|
1097
1151
|
return ret;
|
|
1098
1152
|
}
|
|
@@ -1208,7 +1262,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1208
1262
|
private void playInternal() {
|
|
1209
1263
|
if (player != null && !isFragmentDestroyed) {
|
|
1210
1264
|
try {
|
|
1211
|
-
PlaybackParameters param = new PlaybackParameters(videoRate);
|
|
1265
|
+
PlaybackParameters param = new PlaybackParameters(videoRate != null ? videoRate : 1.0f);
|
|
1212
1266
|
player.setPlaybackParameters(param);
|
|
1213
1267
|
|
|
1214
1268
|
/* If the user start the cast before the player is ready and playing, then the video will start
|
|
@@ -1318,7 +1372,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1318
1372
|
* @return float
|
|
1319
1373
|
*/
|
|
1320
1374
|
public float getRate() {
|
|
1321
|
-
return videoRate;
|
|
1375
|
+
return videoRate != null ? videoRate : 1.0f;
|
|
1322
1376
|
}
|
|
1323
1377
|
|
|
1324
1378
|
/**
|
|
@@ -1509,7 +1563,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1509
1563
|
public void onCastSessionAvailable() {
|
|
1510
1564
|
isCastSession = true;
|
|
1511
1565
|
final Long videoPosition = player.getCurrentPosition();
|
|
1512
|
-
if (pipEnabled) {
|
|
1566
|
+
if (Boolean.TRUE.equals(pipEnabled)) {
|
|
1513
1567
|
pipBtn.setVisibility(View.GONE);
|
|
1514
1568
|
}
|
|
1515
1569
|
resizeBtn.setVisibility(View.GONE);
|
|
@@ -1527,7 +1581,7 @@ public class FullscreenExoPlayerFragment extends Fragment {
|
|
|
1527
1581
|
public void onCastSessionUnavailable() {
|
|
1528
1582
|
isCastSession = false;
|
|
1529
1583
|
final Long videoPosition = castPlayer.getCurrentPosition();
|
|
1530
|
-
if (pipEnabled) {
|
|
1584
|
+
if (Boolean.TRUE.equals(pipEnabled)) {
|
|
1531
1585
|
pipBtn.setVisibility(View.VISIBLE);
|
|
1532
1586
|
}
|
|
1533
1587
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
}
|