@capgo/capacitor-video-player 7.0.0
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/CapgoCapacitorVideoPlayer.podspec +17 -0
- package/Package.swift +28 -0
- package/README.md +431 -0
- package/android/build.gradle +72 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/capgo/videoplayer/FullscreenExoPlayerFragment.java +1406 -0
- package/android/src/main/java/com/capgo/videoplayer/Notifications/MyRunnable.java +21 -0
- package/android/src/main/java/com/capgo/videoplayer/Notifications/NotificationCenter.java +61 -0
- package/android/src/main/java/com/capgo/videoplayer/PickerVideo/AdapterVideoList.java +47 -0
- package/android/src/main/java/com/capgo/videoplayer/PickerVideo/ModelVideo.java +49 -0
- package/android/src/main/java/com/capgo/videoplayer/PickerVideo/PickerVideoFragment.java +116 -0
- package/android/src/main/java/com/capgo/videoplayer/PickerVideo/VideoRecyclerViewHolder.java +65 -0
- package/android/src/main/java/com/capgo/videoplayer/Utilities/FilesUtils.java +38 -0
- package/android/src/main/java/com/capgo/videoplayer/Utilities/FragmentUtils.java +32 -0
- package/android/src/main/java/com/capgo/videoplayer/VideoPlayer.java +71 -0
- package/android/src/main/java/com/capgo/videoplayer/VideoPlayerPlugin.java +1239 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/android/src/main/res/drawable/bg_round_rect_white_50.xml +9 -0
- package/android/src/main/res/drawable/bg_rounded_rectangle_white_corner_rounded.xml +10 -0
- package/android/src/main/res/drawable/exo_close_btn.xml +12 -0
- package/android/src/main/res/drawable/gradient_transparent_middle.xml +12 -0
- package/android/src/main/res/drawable/ic_arrow_left.xml +5 -0
- package/android/src/main/res/drawable/ic_baseline_lq.xml +7 -0
- package/android/src/main/res/drawable/ic_exo_icon_fastforward.xml +35 -0
- package/android/src/main/res/drawable/ic_exo_icon_pause.xml +26 -0
- package/android/src/main/res/drawable/ic_exo_icon_play.xml +36 -0
- package/android/src/main/res/drawable/ic_exo_icon_rewind.xml +35 -0
- package/android/src/main/res/drawable/ic_expand.xml +5 -0
- package/android/src/main/res/drawable/ic_fit.xml +5 -0
- package/android/src/main/res/drawable/ic_image_background.xml +12 -0
- package/android/src/main/res/drawable/ic_img_16_9_background.xml +10 -0
- package/android/src/main/res/drawable/ic_img_9_16_background.xml +10 -0
- package/android/src/main/res/drawable/ic_outline_lock.xml +5 -0
- package/android/src/main/res/drawable/ic_outline_lock_open.xml +5 -0
- package/android/src/main/res/drawable/ic_pip_white.xml +5 -0
- package/android/src/main/res/drawable/ic_views.xml +18 -0
- package/android/src/main/res/drawable/ic_zoom.xml +5 -0
- package/android/src/main/res/layout/bridge_layout_main.xml +15 -0
- package/android/src/main/res/layout/exo_playback_control_view.xml +287 -0
- package/android/src/main/res/layout/exoplayer_layout_youtube.xml +361 -0
- package/android/src/main/res/layout/fragment_fs_exoplayer.xml +50 -0
- package/android/src/main/res/layout/fragment_picker_video.xml +21 -0
- package/android/src/main/res/layout/row_video.xml +76 -0
- package/android/src/main/res/values/colors.xml +14 -0
- package/android/src/main/res/values/strings.xml +3 -0
- package/android/src/main/res/values/styles.xml +3 -0
- package/dist/docs.json +686 -0
- package/dist/esm/definitions.d.ts +307 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web-utils/video-types.d.ts +4 -0
- package/dist/esm/web-utils/video-types.js +9 -0
- package/dist/esm/web-utils/video-types.js.map +1 -0
- package/dist/esm/web-utils/videoplayer.d.ts +30 -0
- package/dist/esm/web-utils/videoplayer.js +323 -0
- package/dist/esm/web-utils/videoplayer.js.map +1 -0
- package/dist/esm/web.d.ts +121 -0
- package/dist/esm/web.js +675 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +1019 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +1021 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/VideoPlayerPlugin/VideoPlayer.swift +8 -0
- package/ios/Sources/VideoPlayerPlugin/VideoPlayerPlugin.swift +23 -0
- package/ios/Tests/VideoPlayerPluginTests/VideoPlayerPluginTests.swift +15 -0
- package/package.json +85 -0
|
@@ -0,0 +1,1406 @@
|
|
|
1
|
+
package com.capgo.videoplayer;
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint;
|
|
4
|
+
import android.app.Activity;
|
|
5
|
+
import android.app.ActivityManager;
|
|
6
|
+
import android.app.PictureInPictureParams;
|
|
7
|
+
import android.content.ContentUris;
|
|
8
|
+
import android.content.Context;
|
|
9
|
+
import android.content.pm.ActivityInfo;
|
|
10
|
+
import android.content.pm.PackageManager;
|
|
11
|
+
import android.content.res.Configuration;
|
|
12
|
+
import android.content.res.TypedArray;
|
|
13
|
+
import android.graphics.Bitmap;
|
|
14
|
+
import android.graphics.BitmapFactory;
|
|
15
|
+
import android.graphics.Color;
|
|
16
|
+
import android.graphics.drawable.Drawable;
|
|
17
|
+
import android.net.Uri;
|
|
18
|
+
import android.os.AsyncTask;
|
|
19
|
+
import android.os.Build;
|
|
20
|
+
import android.os.Bundle;
|
|
21
|
+
import android.os.Handler;
|
|
22
|
+
import android.provider.MediaStore;
|
|
23
|
+
import android.support.v4.media.session.MediaSessionCompat;
|
|
24
|
+
import android.util.Log;
|
|
25
|
+
import android.util.Rational;
|
|
26
|
+
import android.util.TypedValue;
|
|
27
|
+
import android.view.KeyEvent;
|
|
28
|
+
import android.view.LayoutInflater;
|
|
29
|
+
import android.view.View;
|
|
30
|
+
import android.view.ViewGroup;
|
|
31
|
+
import android.view.WindowManager;
|
|
32
|
+
import android.widget.ImageButton;
|
|
33
|
+
import android.widget.ImageView;
|
|
34
|
+
import android.widget.LinearLayout;
|
|
35
|
+
import android.widget.ProgressBar;
|
|
36
|
+
import android.widget.TextView;
|
|
37
|
+
import android.widget.Toast;
|
|
38
|
+
import androidx.annotation.RequiresApi;
|
|
39
|
+
import androidx.appcompat.view.ContextThemeWrapper;
|
|
40
|
+
import androidx.constraintlayout.widget.ConstraintLayout;
|
|
41
|
+
import androidx.core.graphics.drawable.DrawableCompat;
|
|
42
|
+
import androidx.fragment.app.Fragment;
|
|
43
|
+
import androidx.mediarouter.app.MediaRouteButton;
|
|
44
|
+
import androidx.mediarouter.media.MediaControlIntent;
|
|
45
|
+
import androidx.mediarouter.media.MediaRouteSelector;
|
|
46
|
+
import androidx.mediarouter.media.MediaRouter;
|
|
47
|
+
import com.capgo.videoplayer.Notifications.NotificationCenter;
|
|
48
|
+
import com.getcapacitor.JSObject;
|
|
49
|
+
import com.google.android.exoplayer2.C;
|
|
50
|
+
import com.google.android.exoplayer2.DefaultLoadControl;
|
|
51
|
+
import com.google.android.exoplayer2.ExoPlayer;
|
|
52
|
+
import com.google.android.exoplayer2.LoadControl;
|
|
53
|
+
import com.google.android.exoplayer2.MediaItem;
|
|
54
|
+
import com.google.android.exoplayer2.MediaMetadata;
|
|
55
|
+
import com.google.android.exoplayer2.PlaybackParameters;
|
|
56
|
+
import com.google.android.exoplayer2.Player;
|
|
57
|
+
import com.google.android.exoplayer2.audio.AudioAttributes;
|
|
58
|
+
import com.google.android.exoplayer2.ext.cast.CastPlayer;
|
|
59
|
+
import com.google.android.exoplayer2.ext.cast.SessionAvailabilityListener;
|
|
60
|
+
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
|
|
61
|
+
import com.google.android.exoplayer2.source.MediaSource;
|
|
62
|
+
import com.google.android.exoplayer2.source.MergingMediaSource;
|
|
63
|
+
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
|
64
|
+
import com.google.android.exoplayer2.source.SingleSampleMediaSource;
|
|
65
|
+
import com.google.android.exoplayer2.source.dash.DashMediaSource;
|
|
66
|
+
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
|
|
67
|
+
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
|
|
68
|
+
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
|
|
69
|
+
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
|
70
|
+
import com.google.android.exoplayer2.trackselection.ExoTrackSelection;
|
|
71
|
+
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
|
72
|
+
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
|
73
|
+
import com.google.android.exoplayer2.ui.CaptionStyleCompat;
|
|
74
|
+
import com.google.android.exoplayer2.ui.DefaultTimeBar;
|
|
75
|
+
import com.google.android.exoplayer2.ui.PlayerControlView;
|
|
76
|
+
import com.google.android.exoplayer2.ui.StyledPlayerView;
|
|
77
|
+
import com.google.android.exoplayer2.upstream.DataSource;
|
|
78
|
+
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
|
79
|
+
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
|
80
|
+
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
|
|
81
|
+
import com.google.android.exoplayer2.util.MimeTypes;
|
|
82
|
+
import com.google.android.exoplayer2.util.Util;
|
|
83
|
+
import com.google.android.gms.cast.framework.CastButtonFactory;
|
|
84
|
+
import com.google.android.gms.cast.framework.CastContext;
|
|
85
|
+
import com.google.android.gms.cast.framework.CastState;
|
|
86
|
+
import com.google.android.gms.cast.framework.CastStateListener;
|
|
87
|
+
import com.google.android.gms.tasks.OnCompleteListener;
|
|
88
|
+
import com.google.android.gms.tasks.Task;
|
|
89
|
+
import java.io.IOException;
|
|
90
|
+
import java.io.InputStream;
|
|
91
|
+
import java.net.HttpURLConnection;
|
|
92
|
+
import java.net.URL;
|
|
93
|
+
import java.util.Arrays;
|
|
94
|
+
import java.util.HashMap;
|
|
95
|
+
import java.util.List;
|
|
96
|
+
import java.util.Locale;
|
|
97
|
+
import java.util.Map;
|
|
98
|
+
import java.util.concurrent.Executor;
|
|
99
|
+
import java.util.concurrent.Executors;
|
|
100
|
+
import org.json.JSONException;
|
|
101
|
+
|
|
102
|
+
public class FullscreenExoPlayerFragment extends Fragment {
|
|
103
|
+
|
|
104
|
+
public String videoPath;
|
|
105
|
+
public Float videoRate;
|
|
106
|
+
public String playerId;
|
|
107
|
+
public String subTitle;
|
|
108
|
+
public String language;
|
|
109
|
+
public JSObject subTitleOptions;
|
|
110
|
+
public JSObject headers;
|
|
111
|
+
public Boolean isTV;
|
|
112
|
+
public Boolean isInternal;
|
|
113
|
+
public Long videoId;
|
|
114
|
+
public Boolean exitOnEnd;
|
|
115
|
+
public Boolean loopOnEnd;
|
|
116
|
+
public Boolean pipEnabled;
|
|
117
|
+
public Boolean bkModeEnabled;
|
|
118
|
+
public Boolean showControls;
|
|
119
|
+
public String displayMode = "all";
|
|
120
|
+
public String title;
|
|
121
|
+
public String smallTitle;
|
|
122
|
+
public String accentColor;
|
|
123
|
+
public Boolean chromecast;
|
|
124
|
+
public String artwork;
|
|
125
|
+
|
|
126
|
+
private static final String TAG = FullscreenExoPlayerFragment.class.getName();
|
|
127
|
+
public static final long UNKNOWN_TIME = -1L;
|
|
128
|
+
private final List<String> supportedFormat = Arrays.asList(
|
|
129
|
+
new String[] { "mp4", "webm", "ogv", "3gp", "flv", "dash", "mpd", "m3u8", "ism", "ytube", "" }
|
|
130
|
+
);
|
|
131
|
+
private Player.Listener listener;
|
|
132
|
+
private StyledPlayerView styledPlayerView;
|
|
133
|
+
private String vType = null;
|
|
134
|
+
private static ExoPlayer player;
|
|
135
|
+
private boolean playWhenReady = true;
|
|
136
|
+
private boolean firstReadyToPlay = true;
|
|
137
|
+
private boolean isEnded = false;
|
|
138
|
+
private int currentWindow = 0;
|
|
139
|
+
private long playbackPosition = 0;
|
|
140
|
+
private Uri uri = null;
|
|
141
|
+
private Uri sturi = null;
|
|
142
|
+
private ProgressBar Pbar;
|
|
143
|
+
private View view;
|
|
144
|
+
private ImageButton closeBtn;
|
|
145
|
+
private ImageButton pipBtn;
|
|
146
|
+
private ImageButton resizeBtn;
|
|
147
|
+
private ConstraintLayout constLayout;
|
|
148
|
+
private LinearLayout linearLayout;
|
|
149
|
+
private TextView header_tv;
|
|
150
|
+
private TextView header_below;
|
|
151
|
+
private static ImageView cast_image;
|
|
152
|
+
private DefaultTimeBar exo_progress;
|
|
153
|
+
private TextView exo_position;
|
|
154
|
+
private TextView exo_duration;
|
|
155
|
+
private TextView exo_label_separation;
|
|
156
|
+
private TextView live_text;
|
|
157
|
+
private Context context;
|
|
158
|
+
private boolean isMuted = false;
|
|
159
|
+
private float curVolume = (float) 0.5;
|
|
160
|
+
private String stForeColor = "";
|
|
161
|
+
private String stBackColor = "";
|
|
162
|
+
private Integer stFontSize = 16;
|
|
163
|
+
private boolean isInPictureInPictureMode = false;
|
|
164
|
+
private TrackSelector trackSelector;
|
|
165
|
+
// Current playback position (in milliseconds).
|
|
166
|
+
private int mCurrentPosition;
|
|
167
|
+
private int mDuration;
|
|
168
|
+
private static final int videoStep = 10000;
|
|
169
|
+
private boolean isCastSession = false;
|
|
170
|
+
|
|
171
|
+
// Tag for the instance state bundle.
|
|
172
|
+
private static final String PLAYBACK_TIME = "play_time";
|
|
173
|
+
|
|
174
|
+
private PictureInPictureParams.Builder pictureInPictureParams;
|
|
175
|
+
private MediaSessionCompat mediaSession;
|
|
176
|
+
private MediaSessionConnector mediaSessionConnector;
|
|
177
|
+
private PlayerControlView.VisibilityListener visibilityListener;
|
|
178
|
+
private PackageManager packageManager;
|
|
179
|
+
private Boolean isPIPModeeEnabled = true;
|
|
180
|
+
final Handler handler = new Handler();
|
|
181
|
+
final Runnable mRunnable = new Runnable() {
|
|
182
|
+
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
183
|
+
public void run() {
|
|
184
|
+
checkPIPPermission();
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
private Integer resizeStatus = AspectRatioFrameLayout.RESIZE_MODE_FIT;
|
|
189
|
+
private MediaRouteButton mediaRouteButton;
|
|
190
|
+
private CastContext castContext;
|
|
191
|
+
private CastPlayer castPlayer;
|
|
192
|
+
private MediaItem mediaItem;
|
|
193
|
+
private MediaRouter mRouter;
|
|
194
|
+
private MediaRouter.Callback mCallback = new EmptyCallback();
|
|
195
|
+
private MediaRouteSelector mSelector;
|
|
196
|
+
private CastStateListener castStateListener = null;
|
|
197
|
+
private Boolean playerReady = false;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Create Fragment View
|
|
201
|
+
* @param inflater
|
|
202
|
+
* @param container
|
|
203
|
+
* @param savedInstanceState
|
|
204
|
+
* @return View
|
|
205
|
+
*/
|
|
206
|
+
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
207
|
+
context = container.getContext();
|
|
208
|
+
packageManager = context.getPackageManager();
|
|
209
|
+
view = inflater.inflate(R.layout.fragment_fs_exoplayer, container, false);
|
|
210
|
+
constLayout = view.findViewById(R.id.fsExoPlayer);
|
|
211
|
+
linearLayout = view.findViewById(R.id.linearLayout);
|
|
212
|
+
styledPlayerView = view.findViewById(R.id.videoViewId);
|
|
213
|
+
header_tv = view.findViewById(R.id.header_tv);
|
|
214
|
+
header_below = view.findViewById(R.id.header_below);
|
|
215
|
+
Pbar = view.findViewById(R.id.indeterminateBar);
|
|
216
|
+
exo_progress = view.findViewById(R.id.exo_progress);
|
|
217
|
+
exo_progress.setVisibility(View.GONE);
|
|
218
|
+
exo_position = view.findViewById(R.id.exo_position);
|
|
219
|
+
exo_position.setVisibility(View.GONE);
|
|
220
|
+
exo_duration = view.findViewById(R.id.exo_duration);
|
|
221
|
+
exo_duration.setVisibility(View.GONE);
|
|
222
|
+
exo_label_separation = view.findViewById(R.id.exo_label_separation);
|
|
223
|
+
exo_label_separation.setVisibility(View.GONE);
|
|
224
|
+
live_text = view.findViewById(R.id.live_text);
|
|
225
|
+
resizeBtn = view.findViewById(R.id.exo_resize);
|
|
226
|
+
cast_image = view.findViewById(R.id.cast_image);
|
|
227
|
+
mediaRouteButton = view.findViewById(R.id.media_route_button);
|
|
228
|
+
styledPlayerView.setShowPreviousButton(false);
|
|
229
|
+
styledPlayerView.setShowNextButton(false);
|
|
230
|
+
styledPlayerView.setShowFastForwardButton(false);
|
|
231
|
+
styledPlayerView.setShowRewindButton(false);
|
|
232
|
+
|
|
233
|
+
Activity mAct = getActivity();
|
|
234
|
+
if (displayMode.equals("landscape")) {
|
|
235
|
+
mAct.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
|
236
|
+
}
|
|
237
|
+
if (displayMode.equals("portrait")) {
|
|
238
|
+
mAct.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
|
239
|
+
}
|
|
240
|
+
if (!showControls) {
|
|
241
|
+
styledPlayerView.setUseController(false);
|
|
242
|
+
} else {
|
|
243
|
+
styledPlayerView.setUseController(true);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (!chromecast) {
|
|
247
|
+
mediaRouteButton.setVisibility(View.GONE);
|
|
248
|
+
} else {
|
|
249
|
+
initializeCastService();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (title != "") {
|
|
253
|
+
header_tv.setText(title);
|
|
254
|
+
}
|
|
255
|
+
if (smallTitle != "") {
|
|
256
|
+
header_below.setText(smallTitle);
|
|
257
|
+
}
|
|
258
|
+
if (accentColor != "") {
|
|
259
|
+
Pbar.getIndeterminateDrawable().setColorFilter(Color.parseColor(accentColor), android.graphics.PorterDuff.Mode.MULTIPLY);
|
|
260
|
+
exo_progress.setPlayedColor(Color.parseColor(accentColor));
|
|
261
|
+
exo_progress.setScrubberColor(Color.parseColor(accentColor));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
closeBtn = view.findViewById(R.id.exo_close);
|
|
265
|
+
pipBtn = view.findViewById(R.id.exo_pip);
|
|
266
|
+
styledPlayerView.requestFocus();
|
|
267
|
+
linearLayout.setVisibility(View.INVISIBLE);
|
|
268
|
+
styledPlayerView.setControllerShowTimeoutMs(3000);
|
|
269
|
+
styledPlayerView.setControllerVisibilityListener(
|
|
270
|
+
new StyledPlayerView.ControllerVisibilityListener() {
|
|
271
|
+
@Override
|
|
272
|
+
public void onVisibilityChanged(int visibility) {
|
|
273
|
+
linearLayout.setVisibility(visibility);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
listener = new Player.Listener() {
|
|
279
|
+
@Override
|
|
280
|
+
public void onPlayerStateChanged(boolean playWhenReady, int state) {
|
|
281
|
+
String stateString;
|
|
282
|
+
Map<String, Object> info = new HashMap<String, Object>() {
|
|
283
|
+
{
|
|
284
|
+
put("fromPlayerId", playerId);
|
|
285
|
+
put("currentTime", String.valueOf(player.getCurrentPosition() / 1000));
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
switch (state) {
|
|
290
|
+
case ExoPlayer.STATE_IDLE:
|
|
291
|
+
stateString = "ExoPlayer.STATE_IDLE -";
|
|
292
|
+
Toast.makeText(context, "Video Url not found", Toast.LENGTH_SHORT).show();
|
|
293
|
+
playerExit();
|
|
294
|
+
break;
|
|
295
|
+
case ExoPlayer.STATE_BUFFERING:
|
|
296
|
+
stateString = "ExoPlayer.STATE_BUFFERING -";
|
|
297
|
+
Pbar.setVisibility(View.VISIBLE);
|
|
298
|
+
break;
|
|
299
|
+
case ExoPlayer.STATE_READY:
|
|
300
|
+
stateString = "ExoPlayer.STATE_READY -";
|
|
301
|
+
Pbar.setVisibility(View.GONE);
|
|
302
|
+
playerReady = true;
|
|
303
|
+
if (!showControls) {
|
|
304
|
+
styledPlayerView.setUseController(false);
|
|
305
|
+
} else {
|
|
306
|
+
styledPlayerView.setUseController(true);
|
|
307
|
+
}
|
|
308
|
+
linearLayout.setVisibility(View.INVISIBLE);
|
|
309
|
+
Log.v(TAG, "**** in ExoPlayer.STATE_READY firstReadyToPlay " + firstReadyToPlay);
|
|
310
|
+
|
|
311
|
+
if (firstReadyToPlay) {
|
|
312
|
+
firstReadyToPlay = false;
|
|
313
|
+
NotificationCenter.defaultCenter().postNotification("playerItemReady", info);
|
|
314
|
+
play();
|
|
315
|
+
Log.v(TAG, "**** in ExoPlayer.STATE_READY firstReadyToPlay player.isPlaying" + player.isPlaying());
|
|
316
|
+
player.seekTo(currentWindow, playbackPosition);
|
|
317
|
+
|
|
318
|
+
// We show progress bar, position and duration only when the video is not live
|
|
319
|
+
if (!player.isCurrentMediaItemLive()) {
|
|
320
|
+
exo_progress.setVisibility(View.VISIBLE);
|
|
321
|
+
exo_position.setVisibility(View.VISIBLE);
|
|
322
|
+
exo_duration.setVisibility(View.VISIBLE);
|
|
323
|
+
exo_label_separation.setVisibility(View.VISIBLE);
|
|
324
|
+
styledPlayerView.setShowFastForwardButton(true);
|
|
325
|
+
styledPlayerView.setShowRewindButton(true);
|
|
326
|
+
} else {
|
|
327
|
+
live_text.setVisibility(View.VISIBLE);
|
|
328
|
+
}
|
|
329
|
+
} else {
|
|
330
|
+
Log.v(TAG, "**** in ExoPlayer.STATE_READY isPlaying " + player.isPlaying());
|
|
331
|
+
if (player.isPlaying()) {
|
|
332
|
+
Log.v(TAG, "**** in ExoPlayer.STATE_READY going to notify playerItemPlay ");
|
|
333
|
+
NotificationCenter.defaultCenter().postNotification("playerItemPlay", info);
|
|
334
|
+
resizeBtn.setVisibility(View.VISIBLE);
|
|
335
|
+
|
|
336
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && pipEnabled) {
|
|
337
|
+
pipBtn.setVisibility(View.VISIBLE);
|
|
338
|
+
}
|
|
339
|
+
} else {
|
|
340
|
+
Log.v(TAG, "**** in ExoPlayer.STATE_READY going to notify playerItemPause ");
|
|
341
|
+
NotificationCenter.defaultCenter().postNotification("playerItemPause", info);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
break;
|
|
345
|
+
case ExoPlayer.STATE_ENDED:
|
|
346
|
+
stateString = "ExoPlayer.STATE_ENDED -";
|
|
347
|
+
Log.v(TAG, "**** in ExoPlayer.STATE_ENDED going to notify playerItemEnd ");
|
|
348
|
+
|
|
349
|
+
player.seekTo(0);
|
|
350
|
+
player.setVolume(curVolume);
|
|
351
|
+
player.setPlayWhenReady(false);
|
|
352
|
+
if (exitOnEnd) {
|
|
353
|
+
releasePlayer();
|
|
354
|
+
/*
|
|
355
|
+
Activity mAct = getActivity();
|
|
356
|
+
int mOrient = mAct.getRequestedOrientation();
|
|
357
|
+
if (mOrient == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
|
|
358
|
+
mAct.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
|
359
|
+
}
|
|
360
|
+
*/
|
|
361
|
+
NotificationCenter.defaultCenter().postNotification("playerItemEnd", info);
|
|
362
|
+
}
|
|
363
|
+
break;
|
|
364
|
+
default:
|
|
365
|
+
stateString = "UNKNOWN_STATE -";
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
if (isTV) {
|
|
372
|
+
Toast.makeText(context, "Device is a TV ", Toast.LENGTH_SHORT).show();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (!isInternal) {
|
|
376
|
+
uri = Uri.parse(videoPath);
|
|
377
|
+
sturi = subTitle != null ? Uri.parse(subTitle) : null;
|
|
378
|
+
|
|
379
|
+
stForeColor = subTitleOptions.has("foregroundColor") ? subTitleOptions.getString("foregroundColor") : "rgba(255,255,255,1)";
|
|
380
|
+
stBackColor = subTitleOptions.has("backgroundColor") ? subTitleOptions.getString("backgroundColor") : "rgba(0,0,0,1)";
|
|
381
|
+
stFontSize = subTitleOptions.has("fontSize") ? subTitleOptions.getInteger("fontSize") : 16;
|
|
382
|
+
// get video type
|
|
383
|
+
vType = getVideoType(uri);
|
|
384
|
+
Log.v(TAG, "display url: " + uri);
|
|
385
|
+
Log.v(TAG, "display subtitle url: " + sturi);
|
|
386
|
+
Log.v(TAG, "display isTV: " + isTV);
|
|
387
|
+
Log.v(TAG, "display vType: " + vType);
|
|
388
|
+
}
|
|
389
|
+
if (uri != null || isInternal) {
|
|
390
|
+
// go fullscreen
|
|
391
|
+
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
|
392
|
+
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
393
|
+
if (savedInstanceState != null) {
|
|
394
|
+
mCurrentPosition = savedInstanceState.getInt(PLAYBACK_TIME);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
getActivity().runOnUiThread(
|
|
398
|
+
new Runnable() {
|
|
399
|
+
@Override
|
|
400
|
+
public void run() {
|
|
401
|
+
// Set the onKey listener
|
|
402
|
+
view.setFocusableInTouchMode(true);
|
|
403
|
+
view.requestFocus();
|
|
404
|
+
view.setOnKeyListener(
|
|
405
|
+
new View.OnKeyListener() {
|
|
406
|
+
@Override
|
|
407
|
+
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
|
408
|
+
if (event.getAction() == KeyEvent.ACTION_UP) {
|
|
409
|
+
long videoPosition = player.getCurrentPosition();
|
|
410
|
+
Log.v(TAG, "$$$$ onKey " + keyCode + " $$$$");
|
|
411
|
+
if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME) {
|
|
412
|
+
Log.v(TAG, "$$$$ Going to backpress $$$$");
|
|
413
|
+
backPressed();
|
|
414
|
+
} else if (isTV) {
|
|
415
|
+
switch (keyCode) {
|
|
416
|
+
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
|
417
|
+
fastForward(videoPosition, 1);
|
|
418
|
+
break;
|
|
419
|
+
case KeyEvent.KEYCODE_DPAD_LEFT:
|
|
420
|
+
rewind(videoPosition, 1);
|
|
421
|
+
break;
|
|
422
|
+
case KeyEvent.KEYCODE_DPAD_CENTER:
|
|
423
|
+
play_pause();
|
|
424
|
+
break;
|
|
425
|
+
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
|
|
426
|
+
fastForward(videoPosition, 2);
|
|
427
|
+
break;
|
|
428
|
+
case KeyEvent.KEYCODE_MEDIA_REWIND:
|
|
429
|
+
rewind(videoPosition, 2);
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return true;
|
|
434
|
+
} else {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
// initialize the player
|
|
442
|
+
initializePlayer();
|
|
443
|
+
|
|
444
|
+
closeBtn.setOnClickListener(
|
|
445
|
+
new View.OnClickListener() {
|
|
446
|
+
@Override
|
|
447
|
+
public void onClick(View view) {
|
|
448
|
+
playerExit();
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
);
|
|
452
|
+
pipBtn.setOnClickListener(
|
|
453
|
+
new View.OnClickListener() {
|
|
454
|
+
@Override
|
|
455
|
+
public void onClick(View view) {
|
|
456
|
+
pictureInPictureMode();
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
);
|
|
460
|
+
resizeBtn.setOnClickListener(
|
|
461
|
+
new View.OnClickListener() {
|
|
462
|
+
@Override
|
|
463
|
+
public void onClick(View view) {
|
|
464
|
+
resizePressed();
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
);
|
|
471
|
+
} else {
|
|
472
|
+
Log.d(TAG, "Video path wrong or type not supported");
|
|
473
|
+
Toast.makeText(context, "Video path wrong or type not supported", Toast.LENGTH_SHORT).show();
|
|
474
|
+
}
|
|
475
|
+
adjustAspectRatio();
|
|
476
|
+
return view;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Sets the cast image in playerView when it is connected to a cast device
|
|
481
|
+
*/
|
|
482
|
+
private class setCastImage extends AsyncTask<Void, Void, Bitmap> {
|
|
483
|
+
|
|
484
|
+
protected Bitmap doInBackground(Void... params) {
|
|
485
|
+
final String image = artwork;
|
|
486
|
+
if (image != "") {
|
|
487
|
+
try {
|
|
488
|
+
URL url = new URL(image);
|
|
489
|
+
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
|
490
|
+
connection.setDoInput(true);
|
|
491
|
+
connection.connect();
|
|
492
|
+
InputStream input = connection.getInputStream();
|
|
493
|
+
Bitmap myBitmap = BitmapFactory.decodeStream(input);
|
|
494
|
+
return myBitmap;
|
|
495
|
+
} catch (IOException e) {
|
|
496
|
+
e.printStackTrace();
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
@Override
|
|
505
|
+
protected void onPostExecute(Bitmap result) {
|
|
506
|
+
cast_image.setImageBitmap(result);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Show controller
|
|
512
|
+
*/
|
|
513
|
+
public void showController() {
|
|
514
|
+
styledPlayerView.showController();
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* isControllerIsFullyVisible
|
|
519
|
+
*/
|
|
520
|
+
public boolean isControllerIsFullyVisible() {
|
|
521
|
+
return styledPlayerView.isControllerFullyVisible();
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Perform backPressed Action
|
|
526
|
+
*/
|
|
527
|
+
private void backPressed() {
|
|
528
|
+
if (isCastSession) {
|
|
529
|
+
playerExit();
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (
|
|
533
|
+
!isInPictureInPictureMode &&
|
|
534
|
+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
|
|
535
|
+
packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) &&
|
|
536
|
+
isPIPModeeEnabled &&
|
|
537
|
+
pipEnabled &&
|
|
538
|
+
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)
|
|
539
|
+
) {
|
|
540
|
+
pictureInPictureMode();
|
|
541
|
+
} else {
|
|
542
|
+
playerExit();
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
private void resizePressed() {
|
|
547
|
+
if (resizeStatus == AspectRatioFrameLayout.RESIZE_MODE_FIT) {
|
|
548
|
+
styledPlayerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FILL);
|
|
549
|
+
resizeStatus = AspectRatioFrameLayout.RESIZE_MODE_FILL;
|
|
550
|
+
resizeBtn.setImageResource(R.drawable.ic_zoom);
|
|
551
|
+
} else if (resizeStatus == AspectRatioFrameLayout.RESIZE_MODE_FILL) {
|
|
552
|
+
styledPlayerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_ZOOM);
|
|
553
|
+
resizeStatus = AspectRatioFrameLayout.RESIZE_MODE_ZOOM;
|
|
554
|
+
resizeBtn.setImageResource(R.drawable.ic_fit);
|
|
555
|
+
} else {
|
|
556
|
+
styledPlayerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT);
|
|
557
|
+
resizeStatus = AspectRatioFrameLayout.RESIZE_MODE_FIT;
|
|
558
|
+
resizeBtn.setImageResource(R.drawable.ic_expand);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
public void playerExit() {
|
|
563
|
+
Map<String, Object> info = new HashMap<String, Object>() {
|
|
564
|
+
{
|
|
565
|
+
put("dismiss", "1");
|
|
566
|
+
put("currentTime", getCurrentTime());
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
if (player != null) {
|
|
570
|
+
player.seekTo(0);
|
|
571
|
+
player.setVolume(curVolume);
|
|
572
|
+
}
|
|
573
|
+
releasePlayer();
|
|
574
|
+
/*
|
|
575
|
+
Activity mAct = getActivity();
|
|
576
|
+
int mOrient = mAct.getRequestedOrientation();
|
|
577
|
+
if (mOrient == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
|
|
578
|
+
mAct.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
|
579
|
+
}
|
|
580
|
+
*/
|
|
581
|
+
// We control if the user lock the screen when the player is in pip mode
|
|
582
|
+
try {
|
|
583
|
+
NotificationCenter.defaultCenter().postNotification("playerFullscreenDismiss", info);
|
|
584
|
+
} catch (Exception e) {
|
|
585
|
+
Log.e(TAG, "Error in posting notification");
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Perform pictureInPictureMode Action
|
|
591
|
+
*/
|
|
592
|
+
private void pictureInPictureMode() {
|
|
593
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
|
|
594
|
+
styledPlayerView.setUseController(false);
|
|
595
|
+
styledPlayerView.setControllerAutoShow(false);
|
|
596
|
+
linearLayout.setVisibility(View.INVISIBLE);
|
|
597
|
+
Log.v(TAG, "PIP break 1");
|
|
598
|
+
// require android O or higher
|
|
599
|
+
if (
|
|
600
|
+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
|
|
601
|
+
) {
|
|
602
|
+
pictureInPictureParams = new PictureInPictureParams.Builder();
|
|
603
|
+
// setup height and width of the PIP window
|
|
604
|
+
Rational aspectRatio = new Rational(player.getVideoFormat().width, player.getVideoFormat().height);
|
|
605
|
+
pictureInPictureParams.setAspectRatio(aspectRatio).build();
|
|
606
|
+
getActivity().enterPictureInPictureMode(pictureInPictureParams.build());
|
|
607
|
+
Log.v(TAG, "PIP break 2");
|
|
608
|
+
} else {
|
|
609
|
+
getActivity().enterPictureInPictureMode();
|
|
610
|
+
Log.v(TAG, "PIP break 3");
|
|
611
|
+
}
|
|
612
|
+
isInPictureInPictureMode = getActivity().isInPictureInPictureMode();
|
|
613
|
+
if (sturi != null) {
|
|
614
|
+
setSubtitle(true);
|
|
615
|
+
}
|
|
616
|
+
if (player != null) play();
|
|
617
|
+
|
|
618
|
+
handler.postDelayed(mRunnable, 100);
|
|
619
|
+
Log.v(TAG, "PIP break 4");
|
|
620
|
+
} else {
|
|
621
|
+
Log.v(TAG, "pictureInPictureMode: doesn't support PIP");
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
626
|
+
private void checkPIPPermission() {
|
|
627
|
+
isPIPModeeEnabled = isInPictureInPictureMode;
|
|
628
|
+
if (!isInPictureInPictureMode) {
|
|
629
|
+
backPressed();
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Perform onStart Action
|
|
635
|
+
*/
|
|
636
|
+
@Override
|
|
637
|
+
public void onStart() {
|
|
638
|
+
super.onStart();
|
|
639
|
+
//if (chromecast && castContext != null) mRouter.addCallback(mSelector, mCallback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
|
|
640
|
+
|
|
641
|
+
if (Util.SDK_INT >= 24) {
|
|
642
|
+
if (styledPlayerView != null) {
|
|
643
|
+
// If cast is playing then it doesn't start the local player once get backs from background
|
|
644
|
+
if (castContext != null && chromecast && castPlayer.isCastSessionAvailable()) return;
|
|
645
|
+
|
|
646
|
+
initializePlayer();
|
|
647
|
+
if (player.getCurrentPosition() != 0) {
|
|
648
|
+
firstReadyToPlay = false;
|
|
649
|
+
play();
|
|
650
|
+
}
|
|
651
|
+
} else {
|
|
652
|
+
getActivity().finishAndRemoveTask();
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Perform onStop Action
|
|
659
|
+
*/
|
|
660
|
+
@Override
|
|
661
|
+
public void onStop() {
|
|
662
|
+
super.onStop();
|
|
663
|
+
boolean isAppBackground = false;
|
|
664
|
+
if (bkModeEnabled) isAppBackground = isApplicationSentToBackground(context);
|
|
665
|
+
if (isInPictureInPictureMode) {
|
|
666
|
+
linearLayout.setVisibility(View.VISIBLE);
|
|
667
|
+
playerExit();
|
|
668
|
+
getActivity().finishAndRemoveTask();
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Perform onDestroy Action
|
|
674
|
+
*/
|
|
675
|
+
@Override
|
|
676
|
+
public void onDestroy() {
|
|
677
|
+
super.onDestroy();
|
|
678
|
+
if (chromecast) mRouter.removeCallback(mCallback);
|
|
679
|
+
releasePlayer();
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Perform onPause Action
|
|
684
|
+
*/
|
|
685
|
+
@Override
|
|
686
|
+
public void onPause() {
|
|
687
|
+
super.onPause();
|
|
688
|
+
if (chromecast) castContext.removeCastStateListener(castStateListener);
|
|
689
|
+
boolean isAppBackground = false;
|
|
690
|
+
if (bkModeEnabled) isAppBackground = isApplicationSentToBackground(context);
|
|
691
|
+
|
|
692
|
+
if (!isInPictureInPictureMode) {
|
|
693
|
+
if (Util.SDK_INT < 24) {
|
|
694
|
+
if (player != null) player.setPlayWhenReady(false);
|
|
695
|
+
releasePlayer();
|
|
696
|
+
} else {
|
|
697
|
+
if (isAppBackground) {
|
|
698
|
+
if (player != null) {
|
|
699
|
+
if (player.isPlaying()) play();
|
|
700
|
+
}
|
|
701
|
+
} else {
|
|
702
|
+
pause();
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
} else {
|
|
706
|
+
if (linearLayout.getVisibility() == View.VISIBLE) {
|
|
707
|
+
linearLayout.setVisibility(View.INVISIBLE);
|
|
708
|
+
}
|
|
709
|
+
if ((isInPictureInPictureMode || isAppBackground) && player != null) play();
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Release the player
|
|
715
|
+
*/
|
|
716
|
+
public void releasePlayer() {
|
|
717
|
+
if (player != null) {
|
|
718
|
+
playWhenReady = player.getPlayWhenReady();
|
|
719
|
+
playbackPosition = player.getCurrentPosition();
|
|
720
|
+
currentWindow = player.getCurrentWindowIndex();
|
|
721
|
+
mediaSessionConnector.setPlayer(null);
|
|
722
|
+
mediaSession.setActive(false);
|
|
723
|
+
player.setRepeatMode(player.REPEAT_MODE_OFF);
|
|
724
|
+
player.removeListener(listener);
|
|
725
|
+
player.release();
|
|
726
|
+
player = null;
|
|
727
|
+
showSystemUI();
|
|
728
|
+
resetVariables();
|
|
729
|
+
if (chromecast) {
|
|
730
|
+
castPlayer.release();
|
|
731
|
+
castPlayer = null;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Perform onResume Action
|
|
738
|
+
*/
|
|
739
|
+
@Override
|
|
740
|
+
public void onResume() {
|
|
741
|
+
super.onResume();
|
|
742
|
+
//if (chromecast && castContext != null) castContext.addCastStateListener(castStateListener);
|
|
743
|
+
if (!isInPictureInPictureMode) {
|
|
744
|
+
hideSystemUi();
|
|
745
|
+
if ((Util.SDK_INT < 24 || player == null)) {
|
|
746
|
+
initializePlayer();
|
|
747
|
+
}
|
|
748
|
+
} else {
|
|
749
|
+
isInPictureInPictureMode = false;
|
|
750
|
+
if (
|
|
751
|
+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
|
|
752
|
+
) {
|
|
753
|
+
if (!showControls) {
|
|
754
|
+
styledPlayerView.setUseController(false);
|
|
755
|
+
} else {
|
|
756
|
+
styledPlayerView.setUseController(true);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
if (sturi != null) {
|
|
760
|
+
setSubtitle(false);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Hide System UI
|
|
767
|
+
*/
|
|
768
|
+
@SuppressLint("InlinedApi")
|
|
769
|
+
private void hideSystemUi() {
|
|
770
|
+
if (styledPlayerView != null) styledPlayerView.setSystemUiVisibility(
|
|
771
|
+
View.SYSTEM_UI_FLAG_LOW_PROFILE |
|
|
772
|
+
View.SYSTEM_UI_FLAG_FULLSCREEN |
|
|
773
|
+
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
|
774
|
+
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
|
|
775
|
+
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
|
776
|
+
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Leave the fullsreen mode and reset the status bar to visible
|
|
782
|
+
*/
|
|
783
|
+
private void showSystemUI() {
|
|
784
|
+
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
|
785
|
+
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
786
|
+
getActivity().getWindow().getDecorView().setSystemUiVisibility(View.VISIBLE);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* Initialize the player
|
|
791
|
+
*/
|
|
792
|
+
private void initializePlayer() {
|
|
793
|
+
if (player == null) {
|
|
794
|
+
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter.Builder(context).build();
|
|
795
|
+
ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
|
|
796
|
+
trackSelector = new DefaultTrackSelector(context, videoTrackSelectionFactory);
|
|
797
|
+
LoadControl loadControl = new DefaultLoadControl();
|
|
798
|
+
player = new ExoPlayer.Builder(context)
|
|
799
|
+
.setSeekBackIncrementMs(10000)
|
|
800
|
+
.setSeekForwardIncrementMs(10000)
|
|
801
|
+
.setTrackSelector(trackSelector)
|
|
802
|
+
.setLoadControl(loadControl)
|
|
803
|
+
.setBandwidthMeter(bandwidthMeter)
|
|
804
|
+
.build();
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
styledPlayerView.setPlayer(player);
|
|
808
|
+
|
|
809
|
+
MediaSource mediaSource;
|
|
810
|
+
if (!isInternal) {
|
|
811
|
+
if (videoPath.substring(0, 21).equals("file:///android_asset") || videoPath.substring(0, 15).equals("content://media")) {
|
|
812
|
+
mediaSource = buildAssetMediaSource(uri);
|
|
813
|
+
} else {
|
|
814
|
+
mediaSource = buildHttpMediaSource();
|
|
815
|
+
}
|
|
816
|
+
} else {
|
|
817
|
+
Uri videoUri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, videoId);
|
|
818
|
+
mediaSource = buildInternalMediaSource(videoUri);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (mediaSource != null) {
|
|
822
|
+
player.setAudioAttributes(AudioAttributes.DEFAULT, true);
|
|
823
|
+
player.addListener(listener);
|
|
824
|
+
player.prepare(mediaSource, false, false);
|
|
825
|
+
if (loopOnEnd) {
|
|
826
|
+
player.setRepeatMode(player.REPEAT_MODE_ONE);
|
|
827
|
+
} else {
|
|
828
|
+
player.setRepeatMode(player.REPEAT_MODE_OFF);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
Map<String, Object> info = new HashMap<String, Object>() {
|
|
832
|
+
{
|
|
833
|
+
put("fromPlayerId", playerId);
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
if (sturi != null) {
|
|
837
|
+
setSubtitle(false);
|
|
838
|
+
}
|
|
839
|
+
//Use Media Session Connector from the EXT library to enable MediaSession Controls in PIP.
|
|
840
|
+
mediaSession = new MediaSessionCompat(context, "capacitorvideoplayer");
|
|
841
|
+
mediaSessionConnector = new MediaSessionConnector(mediaSession);
|
|
842
|
+
mediaSessionConnector.setPlayer(player);
|
|
843
|
+
mediaSession.setActive(true);
|
|
844
|
+
|
|
845
|
+
NotificationCenter.defaultCenter().postNotification("initializePlayer", info);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
private void setSubtitle(boolean transparent) {
|
|
849
|
+
int foreground;
|
|
850
|
+
int background;
|
|
851
|
+
if (!transparent) {
|
|
852
|
+
foreground = Color.WHITE;
|
|
853
|
+
background = Color.BLACK;
|
|
854
|
+
if (stForeColor.length() > 4 && stForeColor.substring(0, 4).equals("rgba")) {
|
|
855
|
+
foreground = getColorFromRGBA(stForeColor);
|
|
856
|
+
}
|
|
857
|
+
if (stBackColor.length() > 4 && stBackColor.substring(0, 4).equals("rgba")) {
|
|
858
|
+
background = getColorFromRGBA(stBackColor);
|
|
859
|
+
}
|
|
860
|
+
} else {
|
|
861
|
+
foreground = Color.TRANSPARENT;
|
|
862
|
+
background = Color.TRANSPARENT;
|
|
863
|
+
}
|
|
864
|
+
styledPlayerView
|
|
865
|
+
.getSubtitleView()
|
|
866
|
+
.setStyle(
|
|
867
|
+
new CaptionStyleCompat(foreground, background, Color.TRANSPARENT, CaptionStyleCompat.EDGE_TYPE_NONE, Color.WHITE, null)
|
|
868
|
+
);
|
|
869
|
+
styledPlayerView.getSubtitleView().setFixedTextSize(TypedValue.COMPLEX_UNIT_DIP, stFontSize);
|
|
870
|
+
styledPlayerView.setShowSubtitleButton(true);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Build the Asset MediaSource
|
|
875
|
+
*/
|
|
876
|
+
private MediaSource buildAssetMediaSource(Uri uri) {
|
|
877
|
+
MediaSource mediaSource = null;
|
|
878
|
+
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context, "jeep-exoplayer-plugin");
|
|
879
|
+
mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(uri));
|
|
880
|
+
// Get the subtitles if any
|
|
881
|
+
if (sturi != null) {
|
|
882
|
+
mediaSource = getSubTitle(mediaSource, sturi, dataSourceFactory);
|
|
883
|
+
}
|
|
884
|
+
return mediaSource;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* Build the Internal MediaSource
|
|
889
|
+
*/
|
|
890
|
+
private MediaSource buildInternalMediaSource(Uri uri) {
|
|
891
|
+
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context, "jeep-exoplayer-plugin");
|
|
892
|
+
return new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(uri));
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Build the Http MediaSource
|
|
897
|
+
* @return MediaSource
|
|
898
|
+
*/
|
|
899
|
+
private MediaSource buildHttpMediaSource() {
|
|
900
|
+
MediaSource mediaSource = null;
|
|
901
|
+
|
|
902
|
+
DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory();
|
|
903
|
+
httpDataSourceFactory.setUserAgent("jeep-exoplayer-plugin");
|
|
904
|
+
httpDataSourceFactory.setConnectTimeoutMs(DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS);
|
|
905
|
+
httpDataSourceFactory.setReadTimeoutMs(1800000);
|
|
906
|
+
httpDataSourceFactory.setAllowCrossProtocolRedirects(true);
|
|
907
|
+
|
|
908
|
+
// If headers is not null and has data we pass them to the HttpDataSourceFactory
|
|
909
|
+
if (headers != null && headers.length() > 0) {
|
|
910
|
+
// We map the headers(JSObject) to a Map<String, String>
|
|
911
|
+
Map<String, String> headersMap = new HashMap<String, String>();
|
|
912
|
+
for (int i = 0; i < headers.names().length(); i++) {
|
|
913
|
+
try {
|
|
914
|
+
headersMap.put(headers.names().getString(i), headers.get(headers.names().getString(i)).toString());
|
|
915
|
+
} catch (JSONException e) {
|
|
916
|
+
e.printStackTrace();
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
httpDataSourceFactory.setDefaultRequestProperties(headersMap);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context, httpDataSourceFactory);
|
|
923
|
+
|
|
924
|
+
if (
|
|
925
|
+
vType.equals("mp4") ||
|
|
926
|
+
vType.equals("webm") ||
|
|
927
|
+
vType.equals("ogv") ||
|
|
928
|
+
vType.equals("3gp") ||
|
|
929
|
+
vType.equals("flv") ||
|
|
930
|
+
vType.equals("")
|
|
931
|
+
) {
|
|
932
|
+
mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(uri));
|
|
933
|
+
} else if (vType.equals("dash") || vType.equals("mpd")) {
|
|
934
|
+
/* adaptive streaming Dash stream */
|
|
935
|
+
DashMediaSource.Factory mediaSourceFactory = new DashMediaSource.Factory(dataSourceFactory);
|
|
936
|
+
mediaSource = mediaSourceFactory.createMediaSource(MediaItem.fromUri(uri));
|
|
937
|
+
} else if (vType.equals("m3u8")) {
|
|
938
|
+
mediaSource = new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(uri));
|
|
939
|
+
} else if (vType.equals("ism")) {
|
|
940
|
+
mediaSource = new SsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(uri));
|
|
941
|
+
}
|
|
942
|
+
// Get the subtitles if any
|
|
943
|
+
if (sturi != null) {
|
|
944
|
+
mediaSource = getSubTitle(mediaSource, sturi, dataSourceFactory);
|
|
945
|
+
}
|
|
946
|
+
return mediaSource;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* Save instance state
|
|
951
|
+
*/
|
|
952
|
+
@Override
|
|
953
|
+
public void onSaveInstanceState(Bundle outState) {
|
|
954
|
+
super.onSaveInstanceState(outState);
|
|
955
|
+
|
|
956
|
+
// Save the current playback position (in milliseconds) to the
|
|
957
|
+
// instance state bundle.
|
|
958
|
+
if (player != null) {
|
|
959
|
+
outState.putInt(PLAYBACK_TIME, (int) player.getCurrentPosition());
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
private int getColorFromRGBA(String rgbaColor) {
|
|
964
|
+
int ret = 0;
|
|
965
|
+
String color = rgbaColor.substring(rgbaColor.indexOf("(") + 1, rgbaColor.indexOf(")"));
|
|
966
|
+
List<String> colors = Arrays.asList(color.split(","));
|
|
967
|
+
if (colors.size() == 4) {
|
|
968
|
+
ret =
|
|
969
|
+
((Math.round(Float.parseFloat(colors.get(3).trim()) * 255) & 0xff) << 24) |
|
|
970
|
+
((Integer.parseInt(colors.get(0).trim()) & 0xff) << 16) |
|
|
971
|
+
((Integer.parseInt(colors.get(1).trim()) & 0xff) << 8) |
|
|
972
|
+
(Integer.parseInt(colors.get(2).trim()) & 0xff);
|
|
973
|
+
}
|
|
974
|
+
return ret;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
private MediaSource getSubTitle(MediaSource mediaSource, Uri sturi, DataSource.Factory dataSourceFactory) {
|
|
978
|
+
// Create mediaSource with subtitle
|
|
979
|
+
MediaSource[] mediaSources = new MediaSource[2];
|
|
980
|
+
mediaSources[0] = mediaSource;
|
|
981
|
+
String mimeType = getMimeType(sturi);
|
|
982
|
+
|
|
983
|
+
// We get the language label from the language code
|
|
984
|
+
String languageLabel = Locale.forLanguageTag(language).getDisplayLanguage();
|
|
985
|
+
MediaItem.SubtitleConfiguration subConfig = new MediaItem.SubtitleConfiguration.Builder(sturi)
|
|
986
|
+
.setMimeType(mimeType)
|
|
987
|
+
.setUri(sturi)
|
|
988
|
+
.setId(subTitle)
|
|
989
|
+
.setLabel(languageLabel)
|
|
990
|
+
.setRoleFlags(C.ROLE_FLAG_CAPTION)
|
|
991
|
+
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
|
|
992
|
+
.setLanguage(language)
|
|
993
|
+
.build();
|
|
994
|
+
|
|
995
|
+
SingleSampleMediaSource subtitleSource = new SingleSampleMediaSource.Factory(dataSourceFactory).createMediaSource(
|
|
996
|
+
subConfig,
|
|
997
|
+
C.TIME_UNSET
|
|
998
|
+
);
|
|
999
|
+
|
|
1000
|
+
mediaSources[1] = subtitleSource;
|
|
1001
|
+
|
|
1002
|
+
mediaSource = new MergingMediaSource(mediaSources);
|
|
1003
|
+
return mediaSource;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
private String getMimeType(Uri sturi) {
|
|
1007
|
+
String lastSegment = sturi.getLastPathSegment();
|
|
1008
|
+
String extension = lastSegment.substring(lastSegment.lastIndexOf(".") + 1);
|
|
1009
|
+
String mimeType = "";
|
|
1010
|
+
if (extension.equals("vtt")) {
|
|
1011
|
+
mimeType = MimeTypes.TEXT_VTT;
|
|
1012
|
+
} else if (extension.equals("srt")) {
|
|
1013
|
+
mimeType = MimeTypes.APPLICATION_SUBRIP;
|
|
1014
|
+
} else if (extension.equals("ssa") || extension.equals("ass")) {
|
|
1015
|
+
mimeType = MimeTypes.TEXT_SSA;
|
|
1016
|
+
} else if (extension.equals("ttml") || extension.equals("dfxp") || extension.equals("xml")) {
|
|
1017
|
+
mimeType = MimeTypes.APPLICATION_TTML;
|
|
1018
|
+
}
|
|
1019
|
+
return mimeType;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
/**
|
|
1023
|
+
* Fast Forward TV
|
|
1024
|
+
*/
|
|
1025
|
+
private void fastForward(long position, int times) {
|
|
1026
|
+
if (position < mDuration - videoStep) {
|
|
1027
|
+
if (player.isPlaying()) {
|
|
1028
|
+
player.setPlayWhenReady(false);
|
|
1029
|
+
}
|
|
1030
|
+
player.seekTo(position + (long) times * videoStep);
|
|
1031
|
+
play();
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
/**
|
|
1036
|
+
* Rewind TV
|
|
1037
|
+
*/
|
|
1038
|
+
private void rewind(long position, int times) {
|
|
1039
|
+
if (position > videoStep) {
|
|
1040
|
+
if (player.isPlaying()) {
|
|
1041
|
+
player.setPlayWhenReady(false);
|
|
1042
|
+
}
|
|
1043
|
+
player.seekTo(position - (long) times * videoStep);
|
|
1044
|
+
play();
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* Play Pause TV
|
|
1050
|
+
*/
|
|
1051
|
+
private void play_pause() {
|
|
1052
|
+
player.setPlayWhenReady(!player.isPlaying());
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* Check if the player is playing
|
|
1057
|
+
* @return boolean
|
|
1058
|
+
*/
|
|
1059
|
+
public boolean isPlaying() {
|
|
1060
|
+
return player.isPlaying();
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* Start the player
|
|
1065
|
+
*/
|
|
1066
|
+
public void play() {
|
|
1067
|
+
PlaybackParameters param = new PlaybackParameters(videoRate);
|
|
1068
|
+
player.setPlaybackParameters(param);
|
|
1069
|
+
|
|
1070
|
+
/* If the user start the cast before the player is ready and playing, then the video will start
|
|
1071
|
+
in the device and chromecast at the same time. This is to avoid that behaviour.*/
|
|
1072
|
+
if (!isCastSession) player.setPlayWhenReady(true);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/**
|
|
1076
|
+
* Pause the player
|
|
1077
|
+
*/
|
|
1078
|
+
public void pause() {
|
|
1079
|
+
if (player != null) player.setPlayWhenReady(false);
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
/**
|
|
1083
|
+
* Get the player duration
|
|
1084
|
+
* @return int in seconds
|
|
1085
|
+
*/
|
|
1086
|
+
public int getDuration() {
|
|
1087
|
+
return player.getDuration() == UNKNOWN_TIME ? 0 : (int) (player.getDuration() / 1000);
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* Get the player current position
|
|
1092
|
+
* @return int in seconds
|
|
1093
|
+
*/
|
|
1094
|
+
public int getCurrentTime() {
|
|
1095
|
+
return player.getCurrentPosition() == UNKNOWN_TIME ? 0 : (int) (player.getCurrentPosition() / 1000);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* Set the player current position
|
|
1100
|
+
* @param timeSecond int
|
|
1101
|
+
*/
|
|
1102
|
+
public void setCurrentTime(int timeSecond) {
|
|
1103
|
+
if (isInPictureInPictureMode) {
|
|
1104
|
+
styledPlayerView.setUseController(false);
|
|
1105
|
+
linearLayout.setVisibility(View.INVISIBLE);
|
|
1106
|
+
}
|
|
1107
|
+
long seekPosition = player.getCurrentPosition() == UNKNOWN_TIME
|
|
1108
|
+
? 0
|
|
1109
|
+
: Math.min(Math.max(0, timeSecond * 1000), player.getDuration());
|
|
1110
|
+
player.seekTo(seekPosition);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* Return the player volume
|
|
1115
|
+
* @return float
|
|
1116
|
+
*/
|
|
1117
|
+
public float getVolume() {
|
|
1118
|
+
return player.getVolume();
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* Set the player volume
|
|
1123
|
+
* @param _volume float range 0,1
|
|
1124
|
+
*/
|
|
1125
|
+
public void setVolume(float _volume) {
|
|
1126
|
+
float volume = Math.min(Math.max(0, _volume), 1L);
|
|
1127
|
+
player.setVolume(volume);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
/**
|
|
1131
|
+
* Return the player rate
|
|
1132
|
+
* @return float
|
|
1133
|
+
*/
|
|
1134
|
+
public float getRate() {
|
|
1135
|
+
return videoRate;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/**
|
|
1139
|
+
* Set the player rate
|
|
1140
|
+
* @param _rate float range [0.25f, 0.5f, 0.75f, 1f, 2f, 4f]
|
|
1141
|
+
*/
|
|
1142
|
+
public void setRate(float _rate) {
|
|
1143
|
+
videoRate = _rate;
|
|
1144
|
+
PlaybackParameters param = new PlaybackParameters(videoRate);
|
|
1145
|
+
player.setPlaybackParameters(param);
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* Switch Off/On the player volume
|
|
1150
|
+
* @param _isMuted boolean
|
|
1151
|
+
*/
|
|
1152
|
+
public void setMuted(boolean _isMuted) {
|
|
1153
|
+
isMuted = _isMuted;
|
|
1154
|
+
if (isMuted) {
|
|
1155
|
+
curVolume = player.getVolume();
|
|
1156
|
+
player.setVolume(0L);
|
|
1157
|
+
} else {
|
|
1158
|
+
player.setVolume(curVolume);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
/**
|
|
1163
|
+
* Check if the player is muted
|
|
1164
|
+
* @return boolean
|
|
1165
|
+
*/
|
|
1166
|
+
public boolean getMuted() {
|
|
1167
|
+
return isMuted;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
/**
|
|
1171
|
+
* Apply white color to MediaRouteButton
|
|
1172
|
+
* @param button
|
|
1173
|
+
*/
|
|
1174
|
+
private void mediaRouteButtonColorWhite(MediaRouteButton button) {
|
|
1175
|
+
if (button == null) return;
|
|
1176
|
+
Context castContext = new ContextThemeWrapper(getContext(), androidx.mediarouter.R.style.Theme_MediaRouter);
|
|
1177
|
+
|
|
1178
|
+
TypedArray a = castContext.obtainStyledAttributes(
|
|
1179
|
+
null,
|
|
1180
|
+
androidx.mediarouter.R.styleable.MediaRouteButton,
|
|
1181
|
+
androidx.mediarouter.R.attr.mediaRouteButtonStyle,
|
|
1182
|
+
0
|
|
1183
|
+
);
|
|
1184
|
+
Drawable drawable = a.getDrawable(androidx.mediarouter.R.styleable.MediaRouteButton_externalRouteEnabledDrawable);
|
|
1185
|
+
a.recycle();
|
|
1186
|
+
DrawableCompat.setTint(drawable, getContext().getResources().getColor(R.color.white));
|
|
1187
|
+
drawable.setState(button.getDrawableState());
|
|
1188
|
+
button.setRemoteIndicatorDrawable(drawable);
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* Get video Type from Uri
|
|
1193
|
+
* @param uri
|
|
1194
|
+
* @return video type
|
|
1195
|
+
*/
|
|
1196
|
+
private String getVideoType(Uri uri) {
|
|
1197
|
+
String ret = null;
|
|
1198
|
+
Object obj = uri.getLastPathSegment();
|
|
1199
|
+
String lastSegment = (obj == null) ? "" : uri.getLastPathSegment();
|
|
1200
|
+
for (String type : supportedFormat) {
|
|
1201
|
+
if (ret != null) break;
|
|
1202
|
+
if (lastSegment.length() > 0 && lastSegment.contains(type)) ret = type;
|
|
1203
|
+
if (ret == null) {
|
|
1204
|
+
List<String> segments = uri.getPathSegments();
|
|
1205
|
+
if (segments.size() > 0) {
|
|
1206
|
+
String segment;
|
|
1207
|
+
if (segments.get(segments.size() - 1).equals("manifest")) {
|
|
1208
|
+
segment = segments.get(segments.size() - 2);
|
|
1209
|
+
} else {
|
|
1210
|
+
segment = segments.get(segments.size() - 1);
|
|
1211
|
+
}
|
|
1212
|
+
for (String sType : supportedFormat) {
|
|
1213
|
+
if (segment.contains(sType)) {
|
|
1214
|
+
ret = sType;
|
|
1215
|
+
break;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
ret = (ret != null) ? ret : "";
|
|
1222
|
+
return ret;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
/**
|
|
1226
|
+
* Reset Variables for multiple runs
|
|
1227
|
+
*/
|
|
1228
|
+
private void resetVariables() {
|
|
1229
|
+
vType = null;
|
|
1230
|
+
styledPlayerView = null;
|
|
1231
|
+
playWhenReady = true;
|
|
1232
|
+
firstReadyToPlay = true;
|
|
1233
|
+
isEnded = false;
|
|
1234
|
+
currentWindow = 0;
|
|
1235
|
+
playbackPosition = 0;
|
|
1236
|
+
uri = null;
|
|
1237
|
+
isMuted = false;
|
|
1238
|
+
curVolume = (float) 0.5;
|
|
1239
|
+
mCurrentPosition = 0;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
/**
|
|
1243
|
+
* Check if the application has been sent to the background
|
|
1244
|
+
* @param context
|
|
1245
|
+
* @return boolean
|
|
1246
|
+
*/
|
|
1247
|
+
public boolean isApplicationSentToBackground(final Context context) {
|
|
1248
|
+
int pid = android.os.Process.myPid();
|
|
1249
|
+
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
|
1250
|
+
List<ActivityManager.RunningAppProcessInfo> procInfos = am.getRunningAppProcesses();
|
|
1251
|
+
if (procInfos != null) {
|
|
1252
|
+
for (ActivityManager.RunningAppProcessInfo appProcess : procInfos) {
|
|
1253
|
+
if (appProcess.pid == pid) {
|
|
1254
|
+
return true;
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
return false;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
/**
|
|
1262
|
+
* Function to initialize the cast service and everything related to it
|
|
1263
|
+
* @return void
|
|
1264
|
+
*/
|
|
1265
|
+
private void initializeCastService() {
|
|
1266
|
+
Executor executor = Executors.newSingleThreadExecutor();
|
|
1267
|
+
Task<CastContext> task = CastContext.getSharedInstance(context, executor);
|
|
1268
|
+
|
|
1269
|
+
task.addOnCompleteListener(
|
|
1270
|
+
new OnCompleteListener<CastContext>() {
|
|
1271
|
+
@Override
|
|
1272
|
+
public void onComplete(Task<CastContext> task) {
|
|
1273
|
+
if (task.isSuccessful()) {
|
|
1274
|
+
castContext = task.getResult();
|
|
1275
|
+
castPlayer = new CastPlayer(castContext);
|
|
1276
|
+
mRouter = MediaRouter.getInstance(context);
|
|
1277
|
+
mSelector = new MediaRouteSelector.Builder()
|
|
1278
|
+
.addControlCategories(
|
|
1279
|
+
Arrays.asList(MediaControlIntent.CATEGORY_LIVE_AUDIO, MediaControlIntent.CATEGORY_LIVE_VIDEO)
|
|
1280
|
+
)
|
|
1281
|
+
.build();
|
|
1282
|
+
|
|
1283
|
+
mediaRouteButtonColorWhite(mediaRouteButton);
|
|
1284
|
+
if (
|
|
1285
|
+
castContext != null && castContext.getCastState() != CastState.NO_DEVICES_AVAILABLE
|
|
1286
|
+
) mediaRouteButton.setVisibility(View.VISIBLE);
|
|
1287
|
+
|
|
1288
|
+
castStateListener = (state) -> {
|
|
1289
|
+
if (state == CastState.NO_DEVICES_AVAILABLE) {
|
|
1290
|
+
mediaRouteButton.setVisibility(View.GONE);
|
|
1291
|
+
} else {
|
|
1292
|
+
if (mediaRouteButton.getVisibility() == View.GONE) {
|
|
1293
|
+
mediaRouteButton.setVisibility(View.VISIBLE);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1297
|
+
CastButtonFactory.setUpMediaRouteButton(context, mediaRouteButton);
|
|
1298
|
+
|
|
1299
|
+
MediaMetadata movieMetadata;
|
|
1300
|
+
if (artwork != "") {
|
|
1301
|
+
movieMetadata = new MediaMetadata.Builder()
|
|
1302
|
+
.setTitle(title)
|
|
1303
|
+
.setSubtitle(smallTitle)
|
|
1304
|
+
.setMediaType(MediaMetadata.MEDIA_TYPE_MOVIE)
|
|
1305
|
+
.setArtworkUri(Uri.parse(artwork))
|
|
1306
|
+
.build();
|
|
1307
|
+
new setCastImage().execute();
|
|
1308
|
+
} else {
|
|
1309
|
+
movieMetadata = new MediaMetadata.Builder().setTitle(title).setSubtitle(smallTitle).build();
|
|
1310
|
+
}
|
|
1311
|
+
mediaItem = new MediaItem.Builder()
|
|
1312
|
+
.setUri(videoPath)
|
|
1313
|
+
.setMimeType(MimeTypes.VIDEO_UNKNOWN)
|
|
1314
|
+
.setMediaMetadata(movieMetadata)
|
|
1315
|
+
.build();
|
|
1316
|
+
|
|
1317
|
+
castPlayer.setSessionAvailabilityListener(
|
|
1318
|
+
new SessionAvailabilityListener() {
|
|
1319
|
+
@Override
|
|
1320
|
+
public void onCastSessionAvailable() {
|
|
1321
|
+
isCastSession = true;
|
|
1322
|
+
final Long videoPosition = player.getCurrentPosition();
|
|
1323
|
+
if (pipEnabled) {
|
|
1324
|
+
pipBtn.setVisibility(View.GONE);
|
|
1325
|
+
}
|
|
1326
|
+
resizeBtn.setVisibility(View.GONE);
|
|
1327
|
+
player.setPlayWhenReady(false);
|
|
1328
|
+
cast_image.setVisibility(View.VISIBLE);
|
|
1329
|
+
castPlayer.setMediaItem(mediaItem, videoPosition);
|
|
1330
|
+
styledPlayerView.setPlayer(castPlayer);
|
|
1331
|
+
styledPlayerView.setControllerShowTimeoutMs(0);
|
|
1332
|
+
styledPlayerView.setControllerHideOnTouch(false);
|
|
1333
|
+
//We perform a click because for some weird reason, the layout is black until the user clicks on it
|
|
1334
|
+
styledPlayerView.performClick();
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
@Override
|
|
1338
|
+
public void onCastSessionUnavailable() {
|
|
1339
|
+
isCastSession = false;
|
|
1340
|
+
final Long videoPosition = castPlayer.getCurrentPosition();
|
|
1341
|
+
if (pipEnabled) {
|
|
1342
|
+
pipBtn.setVisibility(View.VISIBLE);
|
|
1343
|
+
}
|
|
1344
|
+
resizeBtn.setVisibility(View.VISIBLE);
|
|
1345
|
+
cast_image.setVisibility(View.GONE);
|
|
1346
|
+
styledPlayerView.setPlayer(player);
|
|
1347
|
+
player.setPlayWhenReady(true);
|
|
1348
|
+
player.seekTo(videoPosition);
|
|
1349
|
+
styledPlayerView.setControllerShowTimeoutMs(3000);
|
|
1350
|
+
styledPlayerView.setControllerHideOnTouch(true);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
);
|
|
1354
|
+
|
|
1355
|
+
castPlayer.addListener(
|
|
1356
|
+
new Player.Listener() {
|
|
1357
|
+
@Override
|
|
1358
|
+
public void onPlayerStateChanged(boolean playWhenReady, int state) {
|
|
1359
|
+
Map<String, Object> info = new HashMap<String, Object>() {
|
|
1360
|
+
{
|
|
1361
|
+
put("fromPlayerId", playerId);
|
|
1362
|
+
put("currentTime", String.valueOf(player.getCurrentPosition() / 1000));
|
|
1363
|
+
}
|
|
1364
|
+
};
|
|
1365
|
+
switch (state) {
|
|
1366
|
+
case CastPlayer.STATE_READY:
|
|
1367
|
+
if (castPlayer.isPlaying()) {
|
|
1368
|
+
NotificationCenter.defaultCenter().postNotification("playerItemPlay", info);
|
|
1369
|
+
} else {
|
|
1370
|
+
NotificationCenter.defaultCenter().postNotification("playerItemPause", info);
|
|
1371
|
+
}
|
|
1372
|
+
break;
|
|
1373
|
+
default:
|
|
1374
|
+
break;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
);
|
|
1379
|
+
|
|
1380
|
+
castContext.addCastStateListener(castStateListener);
|
|
1381
|
+
mRouter.addCallback(mSelector, mCallback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
|
|
1382
|
+
} else {
|
|
1383
|
+
Exception e = task.getException();
|
|
1384
|
+
e.printStackTrace();
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
private final class EmptyCallback extends MediaRouter.Callback {}
|
|
1392
|
+
|
|
1393
|
+
@Override
|
|
1394
|
+
public void onConfigurationChanged(Configuration newConfig) {
|
|
1395
|
+
super.onConfigurationChanged(newConfig);
|
|
1396
|
+
adjustAspectRatio();
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
private void adjustAspectRatio() {
|
|
1400
|
+
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
|
1401
|
+
styledPlayerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FILL);
|
|
1402
|
+
} else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
|
|
1403
|
+
styledPlayerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|