Improve players
- Background player is using ExoPlayer internally now
This commit is contained in:
parent
4067ba5232
commit
2ac71c75c0
|
@ -40,7 +40,7 @@
|
||||||
android:label="@string/background_player_name" />
|
android:label="@string/background_player_name" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".player.ExoPlayerActivity"
|
android:name=".player.MainVideoPlayer"
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
|
|
|
@ -7,7 +7,6 @@ import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
@ -43,7 +42,6 @@ import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||||
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
|
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
|
||||||
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
|
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
|
||||||
|
|
||||||
import org.schabi.newpipe.ActivityCommunicator;
|
|
||||||
import org.schabi.newpipe.ImageErrorLoadingListener;
|
import org.schabi.newpipe.ImageErrorLoadingListener;
|
||||||
import org.schabi.newpipe.Localization;
|
import org.schabi.newpipe.Localization;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
@ -57,9 +55,7 @@ import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||||
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
import org.schabi.newpipe.fragments.OnItemSelectedListener;
|
||||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||||
import org.schabi.newpipe.player.AbstractPlayer;
|
import org.schabi.newpipe.player.MainVideoPlayer;
|
||||||
import org.schabi.newpipe.player.BackgroundPlayer;
|
|
||||||
import org.schabi.newpipe.player.ExoPlayerActivity;
|
|
||||||
import org.schabi.newpipe.player.PlayVideoActivity;
|
import org.schabi.newpipe.player.PlayVideoActivity;
|
||||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||||
import org.schabi.newpipe.report.ErrorActivity;
|
import org.schabi.newpipe.report.ErrorActivity;
|
||||||
|
@ -111,7 +107,6 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
private static final ImageLoader imageLoader = ImageLoader.getInstance();
|
private static final ImageLoader imageLoader = ImageLoader.getInstance();
|
||||||
private static final DisplayImageOptions displayImageOptions =
|
private static final DisplayImageOptions displayImageOptions =
|
||||||
new DisplayImageOptions.Builder().displayer(new FadeInBitmapDisplayer(400)).cacheInMemory(false).build();
|
new DisplayImageOptions.Builder().displayer(new FadeInBitmapDisplayer(400)).cacheInMemory(false).build();
|
||||||
private Bitmap streamThumbnail = null;
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Views
|
// Views
|
||||||
|
@ -409,25 +404,6 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
if (info.thumbnail_url != null && !info.thumbnail_url.isEmpty()) {
|
if (info.thumbnail_url != null && !info.thumbnail_url.isEmpty()) {
|
||||||
imageLoader.displayImage(info.thumbnail_url, thumbnailImageView,
|
imageLoader.displayImage(info.thumbnail_url, thumbnailImageView,
|
||||||
displayImageOptions, new SimpleImageLoadingListener() {
|
displayImageOptions, new SimpleImageLoadingListener() {
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
|
|
||||||
streamThumbnail = loadedImage;
|
|
||||||
|
|
||||||
if (streamThumbnail != null) {
|
|
||||||
// TODO: Change the thumbnail implementation
|
|
||||||
|
|
||||||
// When the thumbnail is not loaded yet, it not passes to the service in time
|
|
||||||
// so, I can notify the service through a broadcast, but the problem is
|
|
||||||
// when I click in another video, another thumbnail will be load, and will
|
|
||||||
// notify again, so I send the videoUrl and compare with the service's url
|
|
||||||
ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
|
|
||||||
Intent intent = new Intent(AbstractPlayer.ACTION_UPDATE_THUMB);
|
|
||||||
intent.putExtra(AbstractPlayer.VIDEO_URL, currentStreamInfo.webpage_url);
|
|
||||||
activity.sendBroadcast(intent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||||
ErrorActivity.reportError(activity,
|
ErrorActivity.reportError(activity,
|
||||||
|
@ -529,11 +505,9 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (streamThumbnail != null) ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
|
|
||||||
|
|
||||||
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
||||||
Intent mIntent = NavigationHelper.getOpenPlayerIntent(activity, PopupVideoPlayer.class, info, selectedStreamId);
|
Intent mIntent = NavigationHelper.getOpenVideoPlayerIntent(activity, PopupVideoPlayer.class, info, selectedStreamId);
|
||||||
if (info.start_position > 0) mIntent.putExtra(AbstractPlayer.START_POSITION, info.start_position * 1000);
|
|
||||||
activity.startService(mIntent);
|
activity.startService(mIntent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -624,24 +598,10 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
boolean useExternalAudioPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
|
boolean useExternalAudioPlayer = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
.getBoolean(activity.getString(R.string.use_external_audio_player_key), false);
|
.getBoolean(activity.getString(R.string.use_external_audio_player_key), false);
|
||||||
Intent intent;
|
Intent intent;
|
||||||
AudioStream audioStream =
|
AudioStream audioStream = info.audio_streams.get(Utils.getPreferredAudioFormat(activity, info.audio_streams));
|
||||||
info.audio_streams.get(Utils.getPreferredAudioFormat(activity, info.audio_streams));
|
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) {
|
||||||
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 18) {
|
activity.startService(NavigationHelper.getOpenBackgroundPlayerIntent(activity, info, audioStream));
|
||||||
//internal music player: explicit intent
|
Toast.makeText(activity, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show();
|
||||||
if (!BackgroundPlayer.isRunning && streamThumbnail != null) {
|
|
||||||
ActivityCommunicator.getCommunicator()
|
|
||||||
.backgroundPlayerThumbnail = streamThumbnail;
|
|
||||||
intent = new Intent(activity, BackgroundPlayer.class);
|
|
||||||
|
|
||||||
intent.setAction(Intent.ACTION_VIEW);
|
|
||||||
intent.setDataAndType(Uri.parse(audioStream.url),
|
|
||||||
MediaFormat.getMimeById(audioStream.format));
|
|
||||||
intent.putExtra(BackgroundPlayer.TITLE, info.title);
|
|
||||||
intent.putExtra(BackgroundPlayer.WEB_URL, info.webpage_url);
|
|
||||||
intent.putExtra(BackgroundPlayer.SERVICE_ID, serviceId);
|
|
||||||
intent.putExtra(BackgroundPlayer.CHANNEL_NAME, info.uploader);
|
|
||||||
activity.startService(intent);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
intent = new Intent();
|
intent = new Intent();
|
||||||
try {
|
try {
|
||||||
|
@ -829,9 +789,7 @@ public class VideoDetailFragment extends Fragment implements StreamExtractorWork
|
||||||
|| (Build.VERSION.SDK_INT < 16);
|
|| (Build.VERSION.SDK_INT < 16);
|
||||||
if (!useOldPlayer) {
|
if (!useOldPlayer) {
|
||||||
// ExoPlayer
|
// ExoPlayer
|
||||||
if (streamThumbnail != null) ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = streamThumbnail;
|
mIntent = NavigationHelper.getOpenVideoPlayerIntent(activity, MainVideoPlayer.class, info, actionBarHandler.getSelectedVideoStream());
|
||||||
mIntent = NavigationHelper.getOpenPlayerIntent(activity, ExoPlayerActivity.class, info, actionBarHandler.getSelectedVideoStream());
|
|
||||||
if (info.start_position > 0) mIntent.putExtra(AbstractPlayer.START_POSITION, info.start_position * 1000);
|
|
||||||
} else {
|
} else {
|
||||||
// Internal Player
|
// Internal Player
|
||||||
mIntent = new Intent(activity, PlayVideoActivity.class)
|
mIntent = new Intent(activity, PlayVideoActivity.class)
|
||||||
|
|
|
@ -4,584 +4,452 @@ import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.media.AudioManager;
|
|
||||||
import android.media.MediaPlayer;
|
|
||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.support.v7.app.NotificationCompat;
|
import android.support.annotation.IntRange;
|
||||||
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.ActivityCommunicator;
|
|
||||||
import org.schabi.newpipe.BuildConfig;
|
import org.schabi.newpipe.BuildConfig;
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
|
import org.schabi.newpipe.extractor.stream_info.AudioStream;
|
||||||
import org.schabi.newpipe.util.Constants;
|
import org.schabi.newpipe.util.Constants;
|
||||||
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Adam Howard on 08/11/15.
|
* Base players joining the common properties
|
||||||
* Copyright (c) Adam Howard <achdisposable1@gmail.com> 2015
|
|
||||||
*
|
*
|
||||||
* BackgroundPlayer.java is part of NewPipe.
|
* @author mauriciocolli
|
||||||
*
|
|
||||||
* NewPipe is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* NewPipe is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
*/
|
||||||
|
public class BackgroundPlayer extends Service {
|
||||||
/**Plays the audio stream of videos in the background.*/
|
|
||||||
public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPreparedListener*/ {
|
|
||||||
|
|
||||||
private static final String TAG = "BackgroundPlayer";
|
private static final String TAG = "BackgroundPlayer";
|
||||||
private static final String CLASSNAME = "org.schabi.newpipe.player.BackgroundPlayer";
|
private static final boolean DEBUG = BasePlayer.DEBUG;
|
||||||
private static final String ACTION_STOP = CLASSNAME + ".STOP";
|
|
||||||
private static final String ACTION_PLAYPAUSE = CLASSNAME + ".PLAYPAUSE";
|
|
||||||
private static final String ACTION_REWIND = CLASSNAME + ".REWIND";
|
|
||||||
private static final String ACTION_PLAYBACK_STATE = CLASSNAME + ".PLAYBACK_STATE";
|
|
||||||
private static final String EXTRA_PLAYBACK_STATE = CLASSNAME + ".extras.EXTRA_PLAYBACK_STATE";
|
|
||||||
|
|
||||||
// Extra intent arguments
|
public static final String ACTION_CLOSE = "org.schabi.newpipe.player.BackgroundPlayer.CLOSE";
|
||||||
public static final String TITLE = "title";
|
public static final String ACTION_PLAY_PAUSE = "org.schabi.newpipe.player.BackgroundPlayer.PLAY_PAUSE";
|
||||||
public static final String WEB_URL = "web_url";
|
public static final String ACTION_OPEN_DETAIL = "org.schabi.newpipe.player.BackgroundPlayer.OPEN_DETAIL";
|
||||||
public static final String SERVICE_ID = "service_id";
|
public static final String ACTION_REPEAT = "org.schabi.newpipe.player.BackgroundPlayer.REPEAT";
|
||||||
public static final String CHANNEL_NAME = "channel_name";
|
public static final String ACTION_FAST_REWIND = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_REWIND";
|
||||||
|
public static final String ACTION_FAST_FORWARD = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_FORWARD";
|
||||||
|
|
||||||
private volatile String webUrl = "";
|
public static final String AUDIO_STREAM = "video_only_audio_stream";
|
||||||
private volatile int serviceId = -1;
|
private AudioStream audioStream;
|
||||||
private volatile String channelName = "";
|
|
||||||
|
|
||||||
// Determines if the service is already running.
|
private BasePlayerImpl basePlayerImpl;
|
||||||
// Prevents launching the service twice.
|
private PowerManager powerManager;
|
||||||
public static volatile boolean isRunning;
|
private WifiManager wifiManager;
|
||||||
|
|
||||||
public BackgroundPlayer() {
|
private PowerManager.WakeLock wakeLock;
|
||||||
super();
|
private WifiManager.WifiLock wifiLock;
|
||||||
}
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Notification
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
private static final int NOTIFICATION_ID = 123789;
|
||||||
|
|
||||||
|
private NotificationManager notificationManager;
|
||||||
|
private NotificationCompat.Builder notBuilder;
|
||||||
|
private RemoteViews notRemoteView;
|
||||||
|
private RemoteViews bigNotRemoteView;
|
||||||
|
private final String setAlphaMethodName = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) ? "setImageAlpha" : "setAlpha";
|
||||||
|
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Service's LifeCycle
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
/*PendingIntent pi = PendingIntent.getActivity(this, 0,
|
if (DEBUG) Log.d(TAG, "onCreate() called");
|
||||||
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);*/
|
notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
|
||||||
super.onCreate();
|
powerManager = ((PowerManager) getSystemService(POWER_SERVICE));
|
||||||
|
wifiManager = ((WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE));
|
||||||
|
|
||||||
|
ThemeHelper.setTheme(this, false);
|
||||||
|
basePlayerImpl = new BasePlayerImpl(this);
|
||||||
|
basePlayerImpl.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
Toast.makeText(this, R.string.background_player_playing_toast,
|
if (DEBUG) Log.d(TAG, "onStartCommand() called with: intent = [" + intent + "], flags = [" + flags + "], startId = [" + startId + "]");
|
||||||
Toast.LENGTH_SHORT).show();
|
basePlayerImpl.handleIntent(intent);
|
||||||
|
|
||||||
String source = intent.getDataString();
|
|
||||||
//Log.i(TAG, "backgroundPLayer source:"+source);
|
|
||||||
String videoTitle = intent.getStringExtra(TITLE);
|
|
||||||
webUrl = intent.getStringExtra(WEB_URL);
|
|
||||||
serviceId = intent.getIntExtra(SERVICE_ID, -1);
|
|
||||||
channelName = intent.getStringExtra(CHANNEL_NAME);
|
|
||||||
|
|
||||||
//do nearly everything in a separate thread
|
|
||||||
PlayerThread player = new PlayerThread(source, videoTitle, this);
|
|
||||||
player.start();
|
|
||||||
|
|
||||||
isRunning = true;
|
|
||||||
|
|
||||||
// If we get killed after returning here, don't restart
|
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) {
|
public void onDestroy() {
|
||||||
// We don't provide binding (yet?), so return null
|
if (DEBUG) Log.d(TAG, "destroy() called");
|
||||||
return null;
|
releaseWifiAndCpu();
|
||||||
|
stopForeground(true);
|
||||||
|
if (basePlayerImpl != null) basePlayerImpl.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public IBinder onBind(Intent intent) {
|
||||||
//Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
|
return null;
|
||||||
isRunning = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Actions
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private class PlayerThread extends Thread {
|
public void onOpenDetail(Context context, String videoUrl, String videoTitle) {
|
||||||
MediaPlayer mediaPlayer;
|
if (DEBUG) Log.d(TAG, "onOpenDetail() called with: context = [" + context + "], videoUrl = [" + videoUrl + "]");
|
||||||
private String source;
|
Intent i = new Intent(context, MainActivity.class);
|
||||||
private String title;
|
i.putExtra(Constants.KEY_SERVICE_ID, 0);
|
||||||
private int noteID = TAG.hashCode();
|
i.putExtra(Constants.KEY_URL, videoUrl);
|
||||||
private BackgroundPlayer owner;
|
i.putExtra(Constants.KEY_TITLE, videoTitle);
|
||||||
private NotificationManager noteMgr;
|
i.putExtra(Constants.KEY_LINK_TYPE, StreamingService.LinkType.STREAM);
|
||||||
private WifiManager.WifiLock wifiLock;
|
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
private Bitmap videoThumbnail;
|
context.startActivity(i);
|
||||||
private NoteBuilder noteBuilder;
|
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
|
||||||
private volatile boolean donePlaying = false;
|
}
|
||||||
|
|
||||||
public PlayerThread(String src, String title, BackgroundPlayer owner) {
|
private void onClose() {
|
||||||
this.source = src;
|
if (basePlayerImpl != null) basePlayerImpl.destroyPlayer();
|
||||||
this.title = title;
|
stopForeground(true);
|
||||||
this.owner = owner;
|
releaseWifiAndCpu();
|
||||||
mediaPlayer = new MediaPlayer();
|
stopSelf();
|
||||||
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDonePlaying() {
|
private void onScreenOnOff(boolean on) {
|
||||||
return donePlaying;
|
if (DEBUG) Log.d(TAG, "onScreenOnOff() called with: on = [" + on + "]");
|
||||||
}
|
if (on) {
|
||||||
|
if (basePlayerImpl.isPlaying() && !basePlayerImpl.isProgressLoopRunning.get()) basePlayerImpl.startProgressLoop();
|
||||||
|
} else basePlayerImpl.stopProgressLoop();
|
||||||
|
|
||||||
private boolean isPlaying() {
|
}
|
||||||
try {
|
|
||||||
return mediaPlayer.isPlaying();
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
Log.w(TAG, "Unable to retrieve playing state", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setDonePlaying() {
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
donePlaying = true;
|
// Notification
|
||||||
synchronized (PlayerThread.this) {
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
PlayerThread.this.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized PlaybackState getPlaybackState() {
|
private NotificationCompat.Builder createNotification() {
|
||||||
try {
|
notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification);
|
||||||
return new PlaybackState(mediaPlayer.getDuration(), mediaPlayer.getCurrentPosition(), isPlaying());
|
bigNotRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification_expanded);
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
// This isn't that nice way to handle this.
|
|
||||||
// maybe there is a better way
|
|
||||||
Log.w(TAG, this + ": Got illegal state exception while creating playback state", e);
|
|
||||||
return PlaybackState.UNPREPARED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void broadcastState() {
|
setupNotification(notRemoteView);
|
||||||
PlaybackState state = getPlaybackState();
|
setupNotification(bigNotRemoteView);
|
||||||
if(state == null) return;
|
|
||||||
Intent intent = new Intent(ACTION_PLAYBACK_STATE);
|
|
||||||
intent.putExtra(EXTRA_PLAYBACK_STATE, state);
|
|
||||||
sendBroadcast(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
|
||||||
public void run() {
|
.setOngoing(true)
|
||||||
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);//cpu lock
|
.setSmallIcon(R.drawable.ic_play_circle_filled_white_24dp)
|
||||||
try {
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
mediaPlayer.setDataSource(source);
|
.setCustomContentView(notRemoteView)
|
||||||
//We are already in a separate worker thread,
|
.setCustomBigContentView(bigNotRemoteView);
|
||||||
//so calling the blocking prepare() method should be ok
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) builder.setPriority(Notification.PRIORITY_MAX);
|
||||||
mediaPlayer.prepare();
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
} catch (IOException ioe) {
|
private void setupNotification(RemoteViews remoteViews) {
|
||||||
ioe.printStackTrace();
|
//if (videoThumbnail != null) remoteViews.setImageViewBitmap(R.id.notificationCover, videoThumbnail);
|
||||||
Log.e(TAG, "video source:" + source);
|
///else remoteViews.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail);
|
||||||
Log.e(TAG, "video title:" + title);
|
remoteViews.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle());
|
||||||
//can't do anything useful without a file to play; exit early
|
remoteViews.setTextViewText(R.id.notificationArtist, basePlayerImpl.getChannelName());
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
remoteViews.setOnClickPendingIntent(R.id.notificationPlayPause,
|
||||||
videoThumbnail = ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail;
|
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
} catch (Exception e) {
|
remoteViews.setOnClickPendingIntent(R.id.notificationStop,
|
||||||
Log.e(TAG, "Could not get video thumbnail from ActivityCommunicator");
|
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
e.printStackTrace();
|
remoteViews.setOnClickPendingIntent(R.id.notificationContent,
|
||||||
}
|
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_OPEN_DETAIL), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
|
remoteViews.setOnClickPendingIntent(R.id.notificationRepeat,
|
||||||
|
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_REPEAT), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
|
|
||||||
WifiManager wifiMgr = (WifiManager)getSystemService(Context.WIFI_SERVICE);
|
remoteViews.setOnClickPendingIntent(R.id.notificationFRewind,
|
||||||
wifiLock = wifiMgr.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
|
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_FAST_REWIND), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
|
remoteViews.setOnClickPendingIntent(R.id.notificationFForward,
|
||||||
//listen for end of video
|
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_FAST_FORWARD), PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
mediaPlayer.setOnCompletionListener(new EndListener(wifiLock));
|
|
||||||
|
|
||||||
//get audio focus
|
|
||||||
/*
|
|
||||||
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
|
||||||
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
|
|
||||||
AudioManager.AUDIOFOCUS_GAIN);
|
|
||||||
|
|
||||||
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
|
|
||||||
// could not get audio focus.
|
|
||||||
}*/
|
|
||||||
wifiLock.acquire();
|
|
||||||
mediaPlayer.start();
|
|
||||||
|
|
||||||
IntentFilter filter = new IntentFilter();
|
|
||||||
filter.setPriority(Integer.MAX_VALUE);
|
|
||||||
filter.addAction(ACTION_PLAYPAUSE);
|
|
||||||
filter.addAction(ACTION_STOP);
|
|
||||||
filter.addAction(ACTION_REWIND);
|
|
||||||
filter.addAction(ACTION_PLAYBACK_STATE);
|
|
||||||
registerReceiver(broadcastReceiver, filter);
|
|
||||||
|
|
||||||
initNotificationBuilder();
|
|
||||||
startForeground(noteID, noteBuilder.build());
|
|
||||||
|
|
||||||
//currently decommissioned progressbar looping update code - works, but doesn't fit inside
|
|
||||||
//Notification.MediaStyle Notification layout.
|
|
||||||
noteMgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
|
|
||||||
|
|
||||||
//update every 2s or 4 times in the video, whichever is shorter
|
|
||||||
int vidLength = mediaPlayer.getDuration();
|
|
||||||
int sleepTime = Math.min(2000, (int)(vidLength / 4));
|
|
||||||
while(!isDonePlaying()) {
|
|
||||||
broadcastState();
|
|
||||||
try {
|
|
||||||
synchronized (this) {
|
|
||||||
wait(sleepTime);
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Log.e(TAG, "sleep failure", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**Handles button presses from the notification. */
|
|
||||||
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
String action = intent.getAction();
|
|
||||||
//Log.i(TAG, "received broadcast action:"+action);
|
|
||||||
switch (action) {
|
|
||||||
case ACTION_PLAYPAUSE: {
|
|
||||||
boolean isPlaying = mediaPlayer.isPlaying();
|
|
||||||
if(isPlaying) {
|
|
||||||
mediaPlayer.pause();
|
|
||||||
} else {
|
|
||||||
//reacquire CPU lock after auto-releasing it on pause
|
|
||||||
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
|
|
||||||
mediaPlayer.start();
|
|
||||||
}
|
|
||||||
synchronized (PlayerThread.this) {
|
|
||||||
PlayerThread.this.notifyAll();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ACTION_REWIND:
|
|
||||||
mediaPlayer.seekTo(0);
|
|
||||||
synchronized (PlayerThread.this) {
|
|
||||||
PlayerThread.this.notifyAll();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ACTION_STOP:
|
|
||||||
//this auto-releases CPU lock
|
|
||||||
mediaPlayer.stop();
|
|
||||||
afterPlayCleanup();
|
|
||||||
break;
|
|
||||||
case ACTION_PLAYBACK_STATE: {
|
|
||||||
PlaybackState playbackState = intent.getParcelableExtra(EXTRA_PLAYBACK_STATE);
|
|
||||||
if(!playbackState.equals(PlaybackState.UNPREPARED)) {
|
|
||||||
noteBuilder.setProgress(playbackState.getDuration(), playbackState.getPlayedTime(), false);
|
|
||||||
noteBuilder.setIsPlaying(playbackState.isPlaying());
|
|
||||||
} else {
|
|
||||||
noteBuilder.setProgress(0, 0, true);
|
|
||||||
}
|
|
||||||
noteMgr.notify(noteID, noteBuilder.build());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private void afterPlayCleanup() {
|
|
||||||
// Notify thread to stop
|
|
||||||
setDonePlaying();
|
|
||||||
//remove progress bar
|
|
||||||
//noteBuilder.setProgress(0, 0, false);
|
|
||||||
|
|
||||||
//remove notification
|
|
||||||
noteMgr.cancel(noteID);
|
|
||||||
unregisterReceiver(broadcastReceiver);
|
|
||||||
//release mediaPlayer's system resources
|
|
||||||
mediaPlayer.release();
|
|
||||||
|
|
||||||
//release wifilock
|
|
||||||
wifiLock.release();
|
|
||||||
//remove foreground status of service; make BackgroundPlayer killable
|
|
||||||
stopForeground(true);
|
|
||||||
try {
|
|
||||||
// Wait for thread to stop
|
|
||||||
PlayerThread.this.join();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Log.e(TAG, "unable to join player thread", e);
|
|
||||||
}
|
|
||||||
stopSelf();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class EndListener implements MediaPlayer.OnCompletionListener {
|
|
||||||
private WifiManager.WifiLock wl;
|
|
||||||
public EndListener(WifiManager.WifiLock wifiLock) {
|
|
||||||
this.wl = wifiLock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCompletion(MediaPlayer mp) {
|
|
||||||
afterPlayCleanup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initNotificationBuilder() {
|
|
||||||
Notification note;
|
|
||||||
Resources res = getApplicationContext().getResources();
|
|
||||||
|
|
||||||
/*
|
|
||||||
NotificationCompat.Action pauseButton = new NotificationCompat.Action.Builder
|
|
||||||
(R.drawable.ic_pause_white, "Pause", playPI).build();
|
|
||||||
*/
|
|
||||||
|
|
||||||
PendingIntent playPI = PendingIntent.getBroadcast(owner, noteID,
|
|
||||||
new Intent(ACTION_PLAYPAUSE), PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
PendingIntent stopPI = PendingIntent.getBroadcast(owner, noteID,
|
|
||||||
new Intent(ACTION_STOP), PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
PendingIntent rewindPI = PendingIntent.getBroadcast(owner, noteID,
|
|
||||||
new Intent(ACTION_REWIND), PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
|
|
||||||
//build intent to return to video, on tapping notification
|
|
||||||
Intent openDetailViewIntent = new Intent(getApplicationContext(), MainActivity.class);
|
|
||||||
openDetailViewIntent.putExtra(Constants.KEY_SERVICE_ID, serviceId);
|
|
||||||
openDetailViewIntent.putExtra(Constants.KEY_URL, webUrl);
|
|
||||||
openDetailViewIntent.putExtra(Constants.KEY_TITLE, title);
|
|
||||||
openDetailViewIntent.putExtra(Constants.KEY_LINK_TYPE, StreamingService.LinkType.STREAM);
|
|
||||||
openDetailViewIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
PendingIntent openDetailView = PendingIntent.getActivity(owner, noteID,
|
|
||||||
openDetailViewIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
noteBuilder = new NoteBuilder(owner, playPI, stopPI, rewindPI, openDetailView);
|
|
||||||
noteBuilder
|
|
||||||
.setTitle(title)
|
|
||||||
.setArtist(channelName)
|
|
||||||
.setOngoing(true)
|
|
||||||
.setDeleteIntent(stopPI)
|
|
||||||
//doesn't fit with Notification.MediaStyle
|
|
||||||
//.setProgress(vidLength, 0, false)
|
|
||||||
.setSmallIcon(R.drawable.ic_play_circle_filled_white_24dp)
|
|
||||||
.setContentIntent(PendingIntent.getActivity(getApplicationContext(),
|
|
||||||
noteID, openDetailViewIntent,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT))
|
|
||||||
.setContentIntent(openDetailView)
|
|
||||||
.setCategory(Notification.CATEGORY_TRANSPORT)
|
|
||||||
//Make notification appear on lockscreen
|
|
||||||
.setVisibility(Notification.VISIBILITY_PUBLIC);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notification builder which works like the real builder but uses a custom view.
|
|
||||||
*/
|
|
||||||
class NoteBuilder extends NotificationCompat.Builder {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param context
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public NoteBuilder(Context context, PendingIntent playPI, PendingIntent stopPI,
|
|
||||||
PendingIntent rewindPI, PendingIntent openDetailView) {
|
|
||||||
super(context);
|
|
||||||
setCustomContentView(createCustomContentView(playPI, stopPI, rewindPI, openDetailView));
|
|
||||||
setCustomBigContentView(createCustomBigContentView(playPI, stopPI, rewindPI, openDetailView));
|
|
||||||
}
|
|
||||||
|
|
||||||
private RemoteViews createCustomBigContentView(PendingIntent playPI,
|
|
||||||
PendingIntent stopPI,
|
|
||||||
PendingIntent rewindPI,
|
|
||||||
PendingIntent openDetailView) {
|
|
||||||
//possibly found the expandedView problem,
|
|
||||||
//but can't test it as I don't have a 5.0 device. -medavox
|
|
||||||
RemoteViews expandedView =
|
|
||||||
new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification_expanded);
|
|
||||||
expandedView.setImageViewBitmap(R.id.notificationCover, videoThumbnail);
|
|
||||||
expandedView.setOnClickPendingIntent(R.id.notificationStop, stopPI);
|
|
||||||
expandedView.setOnClickPendingIntent(R.id.notificationPlayPause, playPI);
|
|
||||||
expandedView.setOnClickPendingIntent(R.id.notificationRewind, rewindPI);
|
|
||||||
expandedView.setOnClickPendingIntent(R.id.notificationContent, openDetailView);
|
|
||||||
return expandedView;
|
|
||||||
}
|
|
||||||
|
|
||||||
private RemoteViews createCustomContentView(PendingIntent playPI, PendingIntent stopPI,
|
|
||||||
PendingIntent rewindPI,
|
|
||||||
PendingIntent openDetailView) {
|
|
||||||
RemoteViews view = new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification);
|
|
||||||
view.setImageViewBitmap(R.id.notificationCover, videoThumbnail);
|
|
||||||
view.setOnClickPendingIntent(R.id.notificationStop, stopPI);
|
|
||||||
view.setOnClickPendingIntent(R.id.notificationPlayPause, playPI);
|
|
||||||
view.setOnClickPendingIntent(R.id.notificationRewind, rewindPI);
|
|
||||||
view.setOnClickPendingIntent(R.id.notificationContent, openDetailView);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the title of the stream
|
|
||||||
* @param title the title of the stream
|
|
||||||
* @return this builder for chaining
|
|
||||||
*/
|
|
||||||
NoteBuilder setTitle(String title) {
|
|
||||||
setContentTitle(title);
|
|
||||||
getContentView().setTextViewText(R.id.notificationSongName, title);
|
|
||||||
getBigContentView().setTextViewText(R.id.notificationSongName, title);
|
|
||||||
setTicker(String.format(getBaseContext().getString(
|
|
||||||
R.string.background_player_time_text), title));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the artist of the stream
|
|
||||||
* @param artist the artist of the stream
|
|
||||||
* @return this builder for chaining
|
|
||||||
*/
|
|
||||||
NoteBuilder setArtist(String artist) {
|
|
||||||
setSubText(artist);
|
|
||||||
getContentView().setTextViewText(R.id.notificationArtist, artist);
|
|
||||||
getBigContentView().setTextViewText(R.id.notificationArtist, artist);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public android.support.v4.app.NotificationCompat.Builder setProgress(int max, int progress, boolean indeterminate) {
|
|
||||||
super.setProgress(max, progress, indeterminate);
|
|
||||||
getBigContentView().setProgressBar(R.id.playbackProgress, max, progress, indeterminate);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the isPlaying state
|
|
||||||
* @param isPlaying the is playing state
|
|
||||||
*/
|
|
||||||
public void setIsPlaying(boolean isPlaying) {
|
|
||||||
RemoteViews views = getContentView(), bigViews = getBigContentView();
|
|
||||||
int imageSrc;
|
|
||||||
if(isPlaying) {
|
|
||||||
imageSrc = R.drawable.ic_pause_white;
|
|
||||||
} else {
|
|
||||||
imageSrc = R.drawable.ic_play_circle_filled_white_24dp;
|
|
||||||
}
|
|
||||||
views.setImageViewResource(R.id.notificationPlayPause, imageSrc);
|
|
||||||
bigViews.setImageViewResource(R.id.notificationPlayPause, imageSrc);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
switch (basePlayerImpl.getCurrentRepeatMode()) {
|
||||||
|
case REPEAT_DISABLED:
|
||||||
|
remoteViews.setInt(R.id.notificationRepeat, setAlphaMethodName, 77);
|
||||||
|
break;
|
||||||
|
case REPEAT_ONE:
|
||||||
|
remoteViews.setInt(R.id.notificationRepeat, setAlphaMethodName, 255);
|
||||||
|
break;
|
||||||
|
case REPEAT_ALL:
|
||||||
|
// Waiting :)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the state of the player.
|
* Updates the notification, and the play/pause button in it.
|
||||||
|
* Used for changes on the remoteView
|
||||||
|
*
|
||||||
|
* @param drawableId if != -1, sets the drawable with that id on the play/pause button
|
||||||
*/
|
*/
|
||||||
public static class PlaybackState implements Parcelable {
|
private void updateNotification(int drawableId) {
|
||||||
|
if (DEBUG) Log.d(TAG, "updateNotification() called with: drawableId = [" + drawableId + "]");
|
||||||
private static final int INDEX_IS_PLAYING = 0;
|
if (notBuilder == null) return;
|
||||||
private static final int INDEX_IS_PREPARED= 1;
|
if (drawableId != -1) {
|
||||||
private static final int INDEX_HAS_ERROR = 2;
|
if (notRemoteView != null) notRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId);
|
||||||
private final int duration;
|
if (bigNotRemoteView != null) bigNotRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId);
|
||||||
private final int played;
|
|
||||||
private final boolean[] booleanValues = new boolean[3];
|
|
||||||
|
|
||||||
static final PlaybackState UNPREPARED = new PlaybackState(false, false, false);
|
|
||||||
static final PlaybackState FAILED = new PlaybackState(false, false, true);
|
|
||||||
|
|
||||||
|
|
||||||
PlaybackState(Parcel in) {
|
|
||||||
duration = in.readInt();
|
|
||||||
played = in.readInt();
|
|
||||||
in.readBooleanArray(booleanValues);
|
|
||||||
}
|
}
|
||||||
|
notificationManager.notify(NOTIFICATION_ID, notBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
PlaybackState(int duration, int played, boolean isPlaying) {
|
private void setControlsOpacity(@IntRange(from = 0, to = 255) int opacity) {
|
||||||
this.played = played;
|
if (notRemoteView != null) notRemoteView.setInt(R.id.notificationPlayPause, setAlphaMethodName, opacity);
|
||||||
this.duration = duration;
|
if (bigNotRemoteView != null) bigNotRemoteView.setInt(R.id.notificationPlayPause, setAlphaMethodName, opacity);
|
||||||
this.booleanValues[INDEX_IS_PLAYING] = isPlaying;
|
if (notRemoteView != null) notRemoteView.setInt(R.id.notificationFForward, setAlphaMethodName, opacity);
|
||||||
this.booleanValues[INDEX_IS_PREPARED] = true;
|
if (bigNotRemoteView != null) bigNotRemoteView.setInt(R.id.notificationFForward, setAlphaMethodName, opacity);
|
||||||
this.booleanValues[INDEX_HAS_ERROR] = false;
|
if (notRemoteView != null) notRemoteView.setInt(R.id.notificationFRewind, setAlphaMethodName, opacity);
|
||||||
}
|
if (bigNotRemoteView != null) bigNotRemoteView.setInt(R.id.notificationFRewind, setAlphaMethodName, opacity);
|
||||||
|
}
|
||||||
|
|
||||||
private PlaybackState(boolean isPlaying, boolean isPrepared, boolean hasErrors) {
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
this.played = 0;
|
// Utils
|
||||||
this.duration = 0;
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
this.booleanValues[INDEX_IS_PLAYING] = isPlaying;
|
|
||||||
this.booleanValues[INDEX_IS_PREPARED] = isPrepared;
|
|
||||||
this.booleanValues[INDEX_HAS_ERROR] = hasErrors;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getDuration() {
|
private void lockWifiAndCpu() {
|
||||||
return duration;
|
if (DEBUG) Log.d(TAG, "lockWifiAndCpu() called");
|
||||||
}
|
if (wakeLock != null && wakeLock.isHeld() && wifiLock != null && wifiLock.isHeld()) return;
|
||||||
|
|
||||||
int getPlayedTime() {
|
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
|
||||||
return played;
|
wifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
|
||||||
}
|
|
||||||
|
|
||||||
boolean isPlaying() {
|
if (wakeLock != null) wakeLock.acquire();
|
||||||
return booleanValues[INDEX_IS_PLAYING];
|
if (wifiLock != null) wifiLock.acquire();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isPrepared() {
|
private void releaseWifiAndCpu() {
|
||||||
return booleanValues[INDEX_IS_PREPARED];
|
if (DEBUG) Log.d(TAG, "releaseWifiAndCpu() called");
|
||||||
}
|
if (wakeLock != null && wakeLock.isHeld()) wakeLock.release();
|
||||||
|
if (wifiLock != null && wifiLock.isHeld()) wifiLock.release();
|
||||||
|
|
||||||
boolean hasErrors() {
|
wakeLock = null;
|
||||||
return booleanValues[INDEX_HAS_ERROR];
|
wifiLock = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Override
|
private class BasePlayerImpl extends BasePlayer {
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
|
||||||
dest.writeInt(duration);
|
BasePlayerImpl(Context context) {
|
||||||
dest.writeInt(played);
|
super(context);
|
||||||
dest.writeBooleanArray(booleanValues);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int describeContents() {
|
public void handleIntent(Intent intent) {
|
||||||
return 0;
|
super.handleIntent(intent);
|
||||||
|
Serializable serializable = intent.getSerializableExtra(BackgroundPlayer.AUDIO_STREAM);
|
||||||
|
if (serializable instanceof AudioStream) audioStream = (AudioStream) serializable;
|
||||||
|
playUrl(audioStream.url, MediaFormat.getSuffixById(audioStream.format), true);
|
||||||
|
|
||||||
|
if (bigNotRemoteView != null) bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false);
|
||||||
|
if (notRemoteView != null) notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Creator<PlaybackState> CREATOR = new Creator<PlaybackState>() {
|
@Override
|
||||||
@Override
|
public void initThumbnail() {
|
||||||
public PlaybackState createFromParcel(Parcel in) {
|
if (notRemoteView != null) notRemoteView.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail);
|
||||||
return new PlaybackState(in);
|
if (bigNotRemoteView != null) bigNotRemoteView.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail);
|
||||||
|
updateNotification(-1);
|
||||||
|
super.initThumbnail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onThumbnailReceived(Bitmap thumbnail) {
|
||||||
|
super.onThumbnailReceived(thumbnail);
|
||||||
|
if (thumbnail != null) {
|
||||||
|
if (notRemoteView != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, thumbnail);
|
||||||
|
if (bigNotRemoteView != null) bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, thumbnail);
|
||||||
|
updateNotification(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public PlaybackState[] newArray(int size) {
|
|
||||||
return new PlaybackState[size];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
|
|
||||||
PlaybackState that = (PlaybackState) o;
|
|
||||||
|
|
||||||
if (duration != that.duration) return false;
|
|
||||||
if (played != that.played) return false;
|
|
||||||
return Arrays.equals(booleanValues, that.booleanValues);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public void playUrl(String url, String format, boolean autoPlay) {
|
||||||
if(this == UNPREPARED) return 1;
|
super.playUrl(url, format, autoPlay);
|
||||||
if(this == FAILED) return 2;
|
|
||||||
int result = duration;
|
notBuilder = createNotification();
|
||||||
result = 31 * result + played;
|
startForeground(NOTIFICATION_ID, notBuilder.build());
|
||||||
result = 31 * result + Arrays.hashCode(booleanValues);
|
}
|
||||||
return result + 2;
|
|
||||||
|
@Override
|
||||||
|
public void onPrepared(boolean playWhenReady) {
|
||||||
|
super.onPrepared(playWhenReady);
|
||||||
|
if (simpleExoPlayer.getDuration() < 15000) {
|
||||||
|
PROGRESS_LOOP_INTERVAL = 1000;
|
||||||
|
FAST_FORWARD_REWIND_AMOUNT = 2000;
|
||||||
|
} else if (simpleExoPlayer.getDuration() > 60 * 60 * 1000) {
|
||||||
|
PROGRESS_LOOP_INTERVAL = 2000;
|
||||||
|
FAST_FORWARD_REWIND_AMOUNT = 60000;
|
||||||
|
} else {
|
||||||
|
PROGRESS_LOOP_INTERVAL = 2000;
|
||||||
|
FAST_FORWARD_REWIND_AMOUNT = 10000;
|
||||||
|
}
|
||||||
|
basePlayerImpl.getPlayer().setVolume(1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRepeatClicked() {
|
||||||
|
super.onRepeatClicked();
|
||||||
|
|
||||||
|
int opacity = 255;
|
||||||
|
switch (currentRepeatMode) {
|
||||||
|
case REPEAT_DISABLED:
|
||||||
|
opacity = 77;
|
||||||
|
break;
|
||||||
|
case REPEAT_ONE:
|
||||||
|
opacity = 255;
|
||||||
|
break;
|
||||||
|
case REPEAT_ALL:
|
||||||
|
// Waiting :)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (notRemoteView != null) notRemoteView.setInt(R.id.notificationRepeat, setAlphaMethodName, opacity);
|
||||||
|
if (bigNotRemoteView != null) bigNotRemoteView.setInt(R.id.notificationRepeat, setAlphaMethodName, opacity);
|
||||||
|
updateNotification(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpdateProgress(int currentProgress, int duration, int bufferPercent) {
|
||||||
|
if (bigNotRemoteView != null) bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, duration, currentProgress, false);
|
||||||
|
if (notRemoteView != null) notRemoteView.setProgressBar(R.id.notificationProgressBar, duration, currentProgress, false);
|
||||||
|
updateNotification(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFastRewind() {
|
||||||
|
super.onFastRewind();
|
||||||
|
triggerProgressUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFastForward() {
|
||||||
|
super.onFastForward();
|
||||||
|
triggerProgressUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadingChanged(boolean isLoading) {
|
||||||
|
// Disable default behavior
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
super.destroy();
|
||||||
|
if (notRemoteView != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, null);
|
||||||
|
if (bigNotRemoteView != null) bigNotRemoteView.setImageViewBitmap(R.id.notificationCover, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception exception) {
|
||||||
|
exception.printStackTrace();
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Broadcast Receiver
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setupBroadcastReceiver(IntentFilter intentFilter) {
|
||||||
|
super.setupBroadcastReceiver(intentFilter);
|
||||||
|
intentFilter.addAction(ACTION_CLOSE);
|
||||||
|
intentFilter.addAction(ACTION_PLAY_PAUSE);
|
||||||
|
intentFilter.addAction(ACTION_OPEN_DETAIL);
|
||||||
|
intentFilter.addAction(ACTION_REPEAT);
|
||||||
|
intentFilter.addAction(ACTION_FAST_FORWARD);
|
||||||
|
intentFilter.addAction(ACTION_FAST_REWIND);
|
||||||
|
|
||||||
|
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
|
||||||
|
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||||
|
|
||||||
|
intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBroadcastReceived(Intent intent) {
|
||||||
|
super.onBroadcastReceived(intent);
|
||||||
|
if (DEBUG) Log.d(TAG, "onBroadcastReceived() called with: intent = [" + intent + "]");
|
||||||
|
switch (intent.getAction()) {
|
||||||
|
case ACTION_CLOSE:
|
||||||
|
onClose();
|
||||||
|
break;
|
||||||
|
case ACTION_PLAY_PAUSE:
|
||||||
|
onVideoPlayPause();
|
||||||
|
break;
|
||||||
|
case ACTION_OPEN_DETAIL:
|
||||||
|
onOpenDetail(BackgroundPlayer.this, basePlayerImpl.getVideoUrl(), basePlayerImpl.getVideoTitle());
|
||||||
|
break;
|
||||||
|
case ACTION_REPEAT:
|
||||||
|
onRepeatClicked();
|
||||||
|
break;
|
||||||
|
case ACTION_FAST_REWIND:
|
||||||
|
onFastRewind();
|
||||||
|
break;
|
||||||
|
case ACTION_FAST_FORWARD:
|
||||||
|
onFastForward();
|
||||||
|
break;
|
||||||
|
case Intent.ACTION_SCREEN_ON:
|
||||||
|
onScreenOnOff(true);
|
||||||
|
break;
|
||||||
|
case Intent.ACTION_SCREEN_OFF:
|
||||||
|
onScreenOnOff(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// States
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoading() {
|
||||||
|
super.onLoading();
|
||||||
|
|
||||||
|
setControlsOpacity(77);
|
||||||
|
updateNotification(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlaying() {
|
||||||
|
super.onPlaying();
|
||||||
|
|
||||||
|
setControlsOpacity(255);
|
||||||
|
updateNotification(R.drawable.ic_pause_white);
|
||||||
|
|
||||||
|
lockWifiAndCpu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPaused() {
|
||||||
|
super.onPaused();
|
||||||
|
|
||||||
|
updateNotification(R.drawable.ic_play_arrow_white);
|
||||||
|
if (isProgressLoopRunning.get()) stopProgressLoop();
|
||||||
|
|
||||||
|
releaseWifiAndCpu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCompleted() {
|
||||||
|
super.onCompleted();
|
||||||
|
|
||||||
|
setControlsOpacity(255);
|
||||||
|
if (bigNotRemoteView != null) bigNotRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
|
||||||
|
if (notRemoteView != null) notRemoteView.setProgressBar(R.id.notificationProgressBar, 100, 100, false);
|
||||||
|
updateNotification(R.drawable.ic_replay_white);
|
||||||
|
|
||||||
|
releaseWifiAndCpu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,717 @@
|
||||||
|
package org.schabi.newpipe.player;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.ValueAnimator;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.media.AudioManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.DefaultLoadControl;
|
||||||
|
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||||
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
|
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||||
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||||
|
import com.google.android.exoplayer2.Timeline;
|
||||||
|
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
|
||||||
|
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
||||||
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||||
|
import com.google.android.exoplayer2.source.dash.DashMediaSource;
|
||||||
|
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
|
||||||
|
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
|
||||||
|
import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource;
|
||||||
|
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
|
||||||
|
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
|
||||||
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||||
|
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
||||||
|
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||||
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||||
|
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
|
||||||
|
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;
|
||||||
|
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
|
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Formatter;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base for the players, joining the common properties
|
||||||
|
*
|
||||||
|
* @author mauriciocolli
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||||
|
public abstract class BasePlayer implements ExoPlayer.EventListener, AudioManager.OnAudioFocusChangeListener {
|
||||||
|
public static final boolean DEBUG = false;
|
||||||
|
public static final String TAG = "BasePlayer";
|
||||||
|
|
||||||
|
protected Context context;
|
||||||
|
protected SharedPreferences sharedPreferences;
|
||||||
|
protected AudioManager audioManager;
|
||||||
|
|
||||||
|
protected BroadcastReceiver broadcastReceiver;
|
||||||
|
protected IntentFilter intentFilter;
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Intent
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
public static final String VIDEO_URL = "video_url";
|
||||||
|
public static final String VIDEO_TITLE = "video_title";
|
||||||
|
public static final String VIDEO_THUMBNAIL_URL = "video_thumbnail_url";
|
||||||
|
public static final String START_POSITION = "start_position";
|
||||||
|
public static final String CHANNEL_NAME = "channel_name";
|
||||||
|
|
||||||
|
protected Bitmap videoThumbnail = null;
|
||||||
|
protected String videoUrl = "";
|
||||||
|
protected String videoTitle = "";
|
||||||
|
protected String videoThumbnailUrl = "";
|
||||||
|
protected int videoStartPos = -1;
|
||||||
|
protected String channelName = "";
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Player
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
public int FAST_FORWARD_REWIND_AMOUNT = 10000; // 10 Seconds
|
||||||
|
public static final String CACHE_FOLDER_NAME = "exoplayer";
|
||||||
|
|
||||||
|
protected SimpleExoPlayer simpleExoPlayer;
|
||||||
|
protected boolean isPrepared = false;
|
||||||
|
|
||||||
|
protected MediaSource mediaSource;
|
||||||
|
protected CacheDataSourceFactory cacheDataSourceFactory;
|
||||||
|
protected final DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
|
||||||
|
protected final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
|
||||||
|
|
||||||
|
protected int PROGRESS_LOOP_INTERVAL = 100;
|
||||||
|
protected AtomicBoolean isProgressLoopRunning = new AtomicBoolean();
|
||||||
|
protected Handler progressLoop;
|
||||||
|
protected Runnable progressUpdate;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
public BasePlayer(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
this.progressLoop = new Handler();
|
||||||
|
this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
this.audioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE));
|
||||||
|
|
||||||
|
this.broadcastReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
onBroadcastReceived(intent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.intentFilter = new IntentFilter();
|
||||||
|
setupBroadcastReceiver(intentFilter);
|
||||||
|
context.registerReceiver(broadcastReceiver, intentFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setup() {
|
||||||
|
if (simpleExoPlayer == null) initPlayer();
|
||||||
|
initListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initExoPlayerCache() {
|
||||||
|
if (cacheDataSourceFactory == null) {
|
||||||
|
DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(context, Util.getUserAgent(context, context.getPackageName()), bandwidthMeter);
|
||||||
|
File cacheDir = new File(context.getExternalCacheDir(), CACHE_FOLDER_NAME);
|
||||||
|
if (!cacheDir.exists()) {
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
cacheDir.mkdir();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG) Log.d(TAG, "initExoPlayerCache: cacheDir = " + cacheDir.getAbsolutePath());
|
||||||
|
SimpleCache simpleCache = new SimpleCache(cacheDir, new LeastRecentlyUsedCacheEvictor(64 * 1024 * 1024L));
|
||||||
|
cacheDataSourceFactory = new CacheDataSourceFactory(simpleCache, dataSourceFactory, CacheDataSource.FLAG_BLOCK_ON_CACHE, 512 * 1024);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initPlayer() {
|
||||||
|
if (DEBUG) Log.d(TAG, "initPlayer() called with: context = [" + context + "]");
|
||||||
|
initExoPlayerCache();
|
||||||
|
|
||||||
|
AdaptiveTrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
|
||||||
|
DefaultTrackSelector defaultTrackSelector = new DefaultTrackSelector(trackSelectionFactory);
|
||||||
|
DefaultLoadControl loadControl = new DefaultLoadControl();
|
||||||
|
|
||||||
|
simpleExoPlayer = ExoPlayerFactory.newSimpleInstance(context, defaultTrackSelector, loadControl);
|
||||||
|
simpleExoPlayer.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initListeners() {
|
||||||
|
progressUpdate = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
//if(DEBUG) Log.d(TAG, "progressUpdate run() called");
|
||||||
|
onUpdateProgress((int) simpleExoPlayer.getCurrentPosition(), (int) simpleExoPlayer.getDuration(), simpleExoPlayer.getBufferedPercentage());
|
||||||
|
if (isProgressLoopRunning.get()) progressLoop.postDelayed(this, PROGRESS_LOOP_INTERVAL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleIntent(Intent intent) {
|
||||||
|
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
|
||||||
|
if (intent == null) return;
|
||||||
|
|
||||||
|
videoUrl = intent.getStringExtra(VIDEO_URL);
|
||||||
|
videoTitle = intent.getStringExtra(VIDEO_TITLE);
|
||||||
|
videoThumbnailUrl = intent.getStringExtra(VIDEO_THUMBNAIL_URL);
|
||||||
|
videoStartPos = intent.getIntExtra(START_POSITION, -1);
|
||||||
|
channelName = intent.getStringExtra(CHANNEL_NAME);
|
||||||
|
|
||||||
|
initThumbnail();
|
||||||
|
//play(getSelectedVideoStream(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initThumbnail() {
|
||||||
|
if (DEBUG) Log.d(TAG, "initThumbnail() called");
|
||||||
|
videoThumbnail = null;
|
||||||
|
if (videoThumbnailUrl == null || videoThumbnailUrl.isEmpty()) return;
|
||||||
|
ImageLoader.getInstance().resume();
|
||||||
|
ImageLoader.getInstance().loadImage(videoThumbnailUrl, new SimpleImageLoadingListener() {
|
||||||
|
@Override
|
||||||
|
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onLoadingComplete() called with: imageUri = [" + imageUri + "], view = [" + view + "], loadedImage = [" + loadedImage + "]");
|
||||||
|
videoThumbnail = loadedImage;
|
||||||
|
onThumbnailReceived(loadedImage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void playUrl(String url, String format, boolean autoPlay) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "play() called with: url = [" + url + "], autoPlay = [" + autoPlay + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url == null || simpleExoPlayer == null) {
|
||||||
|
RuntimeException runtimeException = new RuntimeException((url == null ? "Url " : "Player ") + " null");
|
||||||
|
onError(runtimeException);
|
||||||
|
throw runtimeException;
|
||||||
|
}
|
||||||
|
|
||||||
|
changeState(STATE_LOADING);
|
||||||
|
|
||||||
|
isPrepared = false;
|
||||||
|
mediaSource = buildMediaSource(url, format);
|
||||||
|
|
||||||
|
if (simpleExoPlayer.getPlaybackState() != ExoPlayer.STATE_IDLE) simpleExoPlayer.stop();
|
||||||
|
if (videoStartPos > 0) simpleExoPlayer.seekTo(videoStartPos);
|
||||||
|
simpleExoPlayer.prepare(mediaSource);
|
||||||
|
simpleExoPlayer.setPlayWhenReady(autoPlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroyPlayer() {
|
||||||
|
if (DEBUG) Log.d(TAG, "destroyPlayer() called");
|
||||||
|
if (simpleExoPlayer != null) {
|
||||||
|
simpleExoPlayer.stop();
|
||||||
|
simpleExoPlayer.release();
|
||||||
|
}
|
||||||
|
if (progressLoop != null && isProgressLoopRunning.get()) stopProgressLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
if (DEBUG) Log.d(TAG, "destroy() called");
|
||||||
|
destroyPlayer();
|
||||||
|
unregisterBroadcastReceiver();
|
||||||
|
videoThumbnail = null;
|
||||||
|
simpleExoPlayer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MediaSource buildMediaSource(String url, String overrideExtension) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "buildMediaSource() called with: url = [" + url + "], overrideExtension = [" + overrideExtension + "]");
|
||||||
|
}
|
||||||
|
Uri uri = Uri.parse(url);
|
||||||
|
int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri) : Util.inferContentType("." + overrideExtension);
|
||||||
|
MediaSource mediaSource;
|
||||||
|
switch (type) {
|
||||||
|
case C.TYPE_SS:
|
||||||
|
mediaSource = new SsMediaSource(uri, cacheDataSourceFactory, new DefaultSsChunkSource.Factory(cacheDataSourceFactory), null, null);
|
||||||
|
break;
|
||||||
|
case C.TYPE_DASH:
|
||||||
|
mediaSource = new DashMediaSource(uri, cacheDataSourceFactory, new DefaultDashChunkSource.Factory(cacheDataSourceFactory), null, null);
|
||||||
|
break;
|
||||||
|
case C.TYPE_HLS:
|
||||||
|
mediaSource = new HlsMediaSource(uri, cacheDataSourceFactory, null, null);
|
||||||
|
break;
|
||||||
|
case C.TYPE_OTHER:
|
||||||
|
mediaSource = new ExtractorMediaSource(uri, cacheDataSourceFactory, extractorsFactory, null, null);
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
throw new IllegalStateException("Unsupported type: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mediaSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Broadcast Receiver
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add your action in the intentFilter
|
||||||
|
*
|
||||||
|
* @param intentFilter intent filter that will be used for register the receiver
|
||||||
|
*/
|
||||||
|
protected void setupBroadcastReceiver(IntentFilter intentFilter) {
|
||||||
|
intentFilter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBroadcastReceived(Intent intent) {
|
||||||
|
switch (intent.getAction()) {
|
||||||
|
case AudioManager.ACTION_AUDIO_BECOMING_NOISY:
|
||||||
|
if (isPlaying()) simpleExoPlayer.setPlayWhenReady(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterBroadcastReceiver() {
|
||||||
|
if (broadcastReceiver != null && context != null) {
|
||||||
|
context.unregisterReceiver(broadcastReceiver);
|
||||||
|
broadcastReceiver = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// AudioFocus
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private static final int DUCK_DURATION = 1500;
|
||||||
|
private static final float DUCK_AUDIO_TO = .2f;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioFocusChange(int focusChange) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onAudioFocusChange() called with: focusChange = [" + focusChange + "]");
|
||||||
|
if (simpleExoPlayer == null) return;
|
||||||
|
switch (focusChange) {
|
||||||
|
case AudioManager.AUDIOFOCUS_GAIN:
|
||||||
|
onAudioFocusGain();
|
||||||
|
break;
|
||||||
|
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
|
||||||
|
onAudioFocusLossCanDuck();
|
||||||
|
break;
|
||||||
|
case AudioManager.AUDIOFOCUS_LOSS:
|
||||||
|
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
|
||||||
|
onAudioFocusLoss();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onAudioFocusGain() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onAudioFocusGain() called");
|
||||||
|
if (simpleExoPlayer != null) simpleExoPlayer.setVolume(DUCK_AUDIO_TO);
|
||||||
|
animateAudio(DUCK_AUDIO_TO, 1f, DUCK_DURATION);
|
||||||
|
simpleExoPlayer.setPlayWhenReady(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onAudioFocusLoss() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onAudioFocusLoss() called");
|
||||||
|
simpleExoPlayer.setPlayWhenReady(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onAudioFocusLossCanDuck() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onAudioFocusLossCanDuck() called");
|
||||||
|
// Set the volume to 1/10 on ducking
|
||||||
|
animateAudio(simpleExoPlayer.getVolume(), DUCK_AUDIO_TO, DUCK_DURATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// States Implementation
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
public static final int STATE_LOADING = 123;
|
||||||
|
public static final int STATE_PLAYING = 124;
|
||||||
|
public static final int STATE_BUFFERING = 125;
|
||||||
|
public static final int STATE_PAUSED = 126;
|
||||||
|
public static final int STATE_PAUSED_SEEK = 127;
|
||||||
|
public static final int STATE_COMPLETED = 128;
|
||||||
|
|
||||||
|
|
||||||
|
protected int currentState = -1;
|
||||||
|
|
||||||
|
public void changeState(int state) {
|
||||||
|
if (DEBUG) Log.d(TAG, "changeState() called with: state = [" + state + "]");
|
||||||
|
currentState = state;
|
||||||
|
switch (state) {
|
||||||
|
case STATE_LOADING:
|
||||||
|
onLoading();
|
||||||
|
break;
|
||||||
|
case STATE_PLAYING:
|
||||||
|
onPlaying();
|
||||||
|
break;
|
||||||
|
case STATE_BUFFERING:
|
||||||
|
onBuffering();
|
||||||
|
break;
|
||||||
|
case STATE_PAUSED:
|
||||||
|
onPaused();
|
||||||
|
break;
|
||||||
|
case STATE_PAUSED_SEEK:
|
||||||
|
onPausedSeek();
|
||||||
|
break;
|
||||||
|
case STATE_COMPLETED:
|
||||||
|
onCompleted();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLoading() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onLoading() called");
|
||||||
|
if (!isProgressLoopRunning.get()) startProgressLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPlaying() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onPlaying() called");
|
||||||
|
if (!isProgressLoopRunning.get()) startProgressLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBuffering() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPaused() {
|
||||||
|
if (isProgressLoopRunning.get()) stopProgressLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPausedSeek() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCompleted() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onCompleted() called");
|
||||||
|
if (isProgressLoopRunning.get()) stopProgressLoop();
|
||||||
|
|
||||||
|
if (currentRepeatMode == RepeatMode.REPEAT_ONE) {
|
||||||
|
changeState(STATE_LOADING);
|
||||||
|
simpleExoPlayer.seekTo(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Repeat
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
protected RepeatMode currentRepeatMode = RepeatMode.REPEAT_DISABLED;
|
||||||
|
|
||||||
|
public enum RepeatMode {
|
||||||
|
REPEAT_DISABLED,
|
||||||
|
REPEAT_ONE,
|
||||||
|
REPEAT_ALL
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onRepeatClicked() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onRepeatClicked() called");
|
||||||
|
// TODO: implement repeat all when playlist is implemented
|
||||||
|
|
||||||
|
// Switch the modes between DISABLED and REPEAT_ONE, till playlist is implemented
|
||||||
|
setCurrentRepeatMode(getCurrentRepeatMode() == RepeatMode.REPEAT_DISABLED ?
|
||||||
|
RepeatMode.REPEAT_ONE :
|
||||||
|
RepeatMode.REPEAT_DISABLED);
|
||||||
|
|
||||||
|
if (DEBUG) Log.d(TAG, "onRepeatClicked() currentRepeatMode = " + getCurrentRepeatMode().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// ExoPlayer Listener
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadingChanged(boolean isLoading) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onLoadingChanged() called with: isLoading = [" + isLoading + "]");
|
||||||
|
|
||||||
|
if (!isLoading && getCurrentState() == STATE_PAUSED && isProgressLoopRunning.get()) stopProgressLoop();
|
||||||
|
else if (isLoading && !isProgressLoopRunning.get()) startProgressLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onPlayerStateChanged() called with: playWhenReady = [" + playWhenReady + "], playbackState = [" + playbackState + "]");
|
||||||
|
if (getCurrentState() == STATE_PAUSED_SEEK) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onPlayerStateChanged() currently on PausedSeek");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (playbackState) {
|
||||||
|
case ExoPlayer.STATE_IDLE: // 1
|
||||||
|
isPrepared = false;
|
||||||
|
break;
|
||||||
|
case ExoPlayer.STATE_BUFFERING: // 2
|
||||||
|
if (isPrepared && getCurrentState() != STATE_LOADING) changeState(STATE_BUFFERING);
|
||||||
|
break;
|
||||||
|
case ExoPlayer.STATE_READY: //3
|
||||||
|
if (!isPrepared) {
|
||||||
|
isPrepared = true;
|
||||||
|
onPrepared(playWhenReady);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (currentState == STATE_PAUSED_SEEK) break;
|
||||||
|
changeState(playWhenReady ? STATE_PLAYING : STATE_PAUSED);
|
||||||
|
break;
|
||||||
|
case ExoPlayer.STATE_ENDED: // 4
|
||||||
|
changeState(STATE_COMPLETED);
|
||||||
|
isPrepared = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayerError(ExoPlaybackException error) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onPlayerError() called with: error = [" + error + "]");
|
||||||
|
onError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPositionDiscontinuity() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// General Player
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
public abstract void onError(Exception exception);
|
||||||
|
|
||||||
|
public void onPrepared(boolean playWhenReady) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onPrepared() called with: playWhenReady = [" + playWhenReady + "]");
|
||||||
|
if (playWhenReady) audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
|
||||||
|
changeState(playWhenReady ? STATE_PLAYING : STATE_PAUSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void onUpdateProgress(int currentProgress, int duration, int bufferPercent);
|
||||||
|
|
||||||
|
public void onVideoPlayPause() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onVideoPlayPause() called");
|
||||||
|
|
||||||
|
if (currentState == STATE_COMPLETED) {
|
||||||
|
onVideoPlayPauseRepeat();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isPlaying()) audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
|
||||||
|
else audioManager.abandonAudioFocus(null);
|
||||||
|
|
||||||
|
simpleExoPlayer.setPlayWhenReady(!isPlaying());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onVideoPlayPauseRepeat() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onVideoPlayPauseRepeat() called");
|
||||||
|
changeState(STATE_LOADING);
|
||||||
|
setVideoStartPos(0);
|
||||||
|
simpleExoPlayer.seekTo(0);
|
||||||
|
simpleExoPlayer.setPlayWhenReady(true);
|
||||||
|
audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onFastRewind() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onFastRewind() called");
|
||||||
|
seekBy(-FAST_FORWARD_REWIND_AMOUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onFastForward() {
|
||||||
|
if (DEBUG) Log.d(TAG, "onFastForward() called");
|
||||||
|
seekBy(FAST_FORWARD_REWIND_AMOUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onThumbnailReceived(Bitmap thumbnail) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onThumbnailReceived() called with: thumbnail = [" + thumbnail + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void seekBy(int milliSeconds) {
|
||||||
|
if (DEBUG) Log.d(TAG, "seekBy() called with: milliSeconds = [" + milliSeconds + "]");
|
||||||
|
if (simpleExoPlayer == null || (isCompleted() && milliSeconds > 0) || ((milliSeconds < 0 && simpleExoPlayer.getCurrentPosition() == 0))) return;
|
||||||
|
int progress = (int) (simpleExoPlayer.getCurrentPosition() + milliSeconds);
|
||||||
|
if (progress < 0) progress = 0;
|
||||||
|
simpleExoPlayer.seekTo(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPlaying() {
|
||||||
|
return simpleExoPlayer.getPlaybackState() == ExoPlayer.STATE_READY && simpleExoPlayer.getPlayWhenReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private final StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
private final Formatter formatter = new Formatter(stringBuilder, Locale.getDefault());
|
||||||
|
|
||||||
|
public String getTimeString(int milliSeconds) {
|
||||||
|
long seconds = (milliSeconds % 60000L) / 1000L;
|
||||||
|
long minutes = (milliSeconds % 3600000L) / 60000L;
|
||||||
|
long hours = (milliSeconds % 86400000L) / 3600000L;
|
||||||
|
long days = (milliSeconds % (86400000L * 7L)) / 86400000L;
|
||||||
|
|
||||||
|
stringBuilder.setLength(0);
|
||||||
|
return days > 0 ? formatter.format("%d:%02d:%02d:%02d", days, hours, minutes, seconds).toString()
|
||||||
|
: hours > 0 ? formatter.format("%d:%02d:%02d", hours, minutes, seconds).toString()
|
||||||
|
: formatter.format("%02d:%02d", minutes, seconds).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void startProgressLoop() {
|
||||||
|
progressLoop.removeCallbacksAndMessages(null);
|
||||||
|
isProgressLoopRunning.set(true);
|
||||||
|
progressLoop.post(progressUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void stopProgressLoop() {
|
||||||
|
isProgressLoopRunning.set(false);
|
||||||
|
progressLoop.removeCallbacksAndMessages(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void tryDeleteCacheFiles(Context context) {
|
||||||
|
File cacheDir = new File(context.getExternalCacheDir(), CACHE_FOLDER_NAME);
|
||||||
|
|
||||||
|
if (cacheDir.exists()) {
|
||||||
|
try {
|
||||||
|
if (cacheDir.isDirectory()) {
|
||||||
|
for (File file : cacheDir.listFiles()) {
|
||||||
|
try {
|
||||||
|
if (DEBUG) Log.d(TAG, "tryDeleteCacheFiles: " + file.getAbsolutePath() + " deleted = " + file.delete());
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void triggerProgressUpdate() {
|
||||||
|
onUpdateProgress((int) simpleExoPlayer.getCurrentPosition(), (int) simpleExoPlayer.getDuration(), simpleExoPlayer.getBufferedPercentage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void animateAudio(final float from, final float to, int duration) {
|
||||||
|
ValueAnimator valueAnimator = new ValueAnimator();
|
||||||
|
valueAnimator.setFloatValues(from, to);
|
||||||
|
valueAnimator.setDuration(duration);
|
||||||
|
valueAnimator.addListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationStart(Animator animation) {
|
||||||
|
if (simpleExoPlayer != null) simpleExoPlayer.setVolume(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationCancel(Animator animation) {
|
||||||
|
if (simpleExoPlayer != null) simpleExoPlayer.setVolume(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
if (simpleExoPlayer != null) simpleExoPlayer.setVolume(to);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationUpdate(ValueAnimator animation) {
|
||||||
|
if (simpleExoPlayer != null) simpleExoPlayer.setVolume(((float) animation.getAnimatedValue()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
valueAnimator.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Getters and Setters
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
public SimpleExoPlayer getPlayer() {
|
||||||
|
return simpleExoPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SharedPreferences getSharedPreferences() {
|
||||||
|
return sharedPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RepeatMode getCurrentRepeatMode() {
|
||||||
|
return currentRepeatMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentRepeatMode(RepeatMode mode) {
|
||||||
|
currentRepeatMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCurrentState() {
|
||||||
|
return currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVideoUrl() {
|
||||||
|
return videoUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoUrl(String videoUrl) {
|
||||||
|
this.videoUrl = videoUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVideoStartPos() {
|
||||||
|
return videoStartPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoStartPos(int videoStartPos) {
|
||||||
|
this.videoStartPos = videoStartPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVideoTitle() {
|
||||||
|
return videoTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoTitle(String videoTitle) {
|
||||||
|
this.videoTitle = videoTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChannelName() {
|
||||||
|
return channelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChannelName(String channelName) {
|
||||||
|
this.channelName = channelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompleted() {
|
||||||
|
return simpleExoPlayer != null && simpleExoPlayer.getPlaybackState() == ExoPlayer.STATE_ENDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPrepared() {
|
||||||
|
return isPrepared;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrepared(boolean prepared) {
|
||||||
|
isPrepared = prepared;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bitmap getVideoThumbnail() {
|
||||||
|
return videoThumbnail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoThumbnail(Bitmap videoThumbnail) {
|
||||||
|
this.videoThumbnail = videoThumbnail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVideoThumbnailUrl() {
|
||||||
|
return videoThumbnailUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoThumbnailUrl(String videoThumbnailUrl) {
|
||||||
|
this.videoThumbnailUrl = videoThumbnailUrl;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,8 @@
|
||||||
package org.schabi.newpipe.player;
|
package org.schabi.newpipe.player;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
|
@ -23,22 +21,20 @@ import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.PermissionHelper;
|
import org.schabi.newpipe.util.PermissionHelper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity Player implementing AbstractPlayer
|
* Activity Player implementing VideoPlayer
|
||||||
*
|
*
|
||||||
* @author mauriciocolli
|
* @author mauriciocolli
|
||||||
*/
|
*/
|
||||||
public class ExoPlayerActivity extends Activity {
|
public class MainVideoPlayer extends Activity {
|
||||||
private static final String TAG = ".ExoPlayerActivity";
|
private static final String TAG = ".MainVideoPlayer";
|
||||||
private static final boolean DEBUG = AbstractPlayer.DEBUG;
|
private static final boolean DEBUG = BasePlayer.DEBUG;
|
||||||
|
|
||||||
private AudioManager audioManager;
|
private AudioManager audioManager;
|
||||||
private BroadcastReceiver broadcastReceiver;
|
|
||||||
private GestureDetector gestureDetector;
|
private GestureDetector gestureDetector;
|
||||||
|
|
||||||
private final Runnable hideUiRunnable = new Runnable() {
|
private final Runnable hideUiRunnable = new Runnable() {
|
||||||
|
@ -49,7 +45,7 @@ public class ExoPlayerActivity extends Activity {
|
||||||
};
|
};
|
||||||
private boolean activityPaused;
|
private boolean activityPaused;
|
||||||
|
|
||||||
private AbstractPlayerImpl playerImpl;
|
private VideoPlayerImpl playerImpl;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Activity LifeCycle
|
// Activity LifeCycle
|
||||||
|
@ -72,9 +68,8 @@ public class ExoPlayerActivity extends Activity {
|
||||||
|
|
||||||
showSystemUi();
|
showSystemUi();
|
||||||
setContentView(R.layout.activity_exo_player);
|
setContentView(R.layout.activity_exo_player);
|
||||||
playerImpl = new AbstractPlayerImpl();
|
playerImpl = new VideoPlayerImpl();
|
||||||
playerImpl.setup(findViewById(android.R.id.content));
|
playerImpl.setup(findViewById(android.R.id.content));
|
||||||
initReceiver();
|
|
||||||
playerImpl.handleIntent(getIntent());
|
playerImpl.handleIntent(getIntent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,8 +92,10 @@ public class ExoPlayerActivity extends Activity {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
if (DEBUG) Log.d(TAG, "onStop() called");
|
if (DEBUG) Log.d(TAG, "onStop() called");
|
||||||
activityPaused = true;
|
activityPaused = true;
|
||||||
playerImpl.destroy();
|
if (playerImpl.getPlayer() != null) {
|
||||||
playerImpl.setVideoStartPos((int) playerImpl.getPlayer().getCurrentPosition());
|
playerImpl.setVideoStartPos((int) playerImpl.getPlayer().getCurrentPosition());
|
||||||
|
playerImpl.destroyPlayer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -106,9 +103,9 @@ public class ExoPlayerActivity extends Activity {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
if (DEBUG) Log.d(TAG, "onResume() called");
|
if (DEBUG) Log.d(TAG, "onResume() called");
|
||||||
if (activityPaused) {
|
if (activityPaused) {
|
||||||
playerImpl.getPlayPauseButton().setImageResource(R.drawable.ic_play_arrow_white);
|
|
||||||
playerImpl.initPlayer();
|
playerImpl.initPlayer();
|
||||||
playerImpl.playVideo(playerImpl.getSelectedVideoStream(), false);
|
playerImpl.getPlayPauseButton().setImageResource(R.drawable.ic_play_arrow_white);
|
||||||
|
playerImpl.play(false);
|
||||||
activityPaused = false;
|
activityPaused = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,29 +115,6 @@ public class ExoPlayerActivity extends Activity {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
if (DEBUG) Log.d(TAG, "onDestroy() called");
|
if (DEBUG) Log.d(TAG, "onDestroy() called");
|
||||||
if (playerImpl != null) playerImpl.destroy();
|
if (playerImpl != null) playerImpl.destroy();
|
||||||
if (broadcastReceiver != null) unregisterReceiver(broadcastReceiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Init
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
private void initReceiver() {
|
|
||||||
if (DEBUG) Log.d(TAG, "initReceiver() called");
|
|
||||||
broadcastReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (DEBUG) Log.d(TAG, "onReceive() called with: context = [" + context + "], intent = [" + intent + "]");
|
|
||||||
switch (intent.getAction()) {
|
|
||||||
case AbstractPlayer.ACTION_UPDATE_THUMB:
|
|
||||||
playerImpl.onUpdateThumbnail(intent);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
|
||||||
intentFilter.addAction(AbstractPlayer.ACTION_UPDATE_THUMB);
|
|
||||||
registerReceiver(broadcastReceiver, intentFilter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -182,7 +156,7 @@ public class ExoPlayerActivity extends Activity {
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
@SuppressWarnings({"unused", "WeakerAccess"})
|
||||||
private class AbstractPlayerImpl extends AbstractPlayer {
|
private class VideoPlayerImpl extends VideoPlayer {
|
||||||
private TextView titleTextView;
|
private TextView titleTextView;
|
||||||
private TextView channelTextView;
|
private TextView channelTextView;
|
||||||
private TextView volumeTextView;
|
private TextView volumeTextView;
|
||||||
|
@ -192,8 +166,8 @@ public class ExoPlayerActivity extends Activity {
|
||||||
private ImageButton screenRotationButton;
|
private ImageButton screenRotationButton;
|
||||||
private ImageButton playPauseButton;
|
private ImageButton playPauseButton;
|
||||||
|
|
||||||
AbstractPlayerImpl() {
|
VideoPlayerImpl() {
|
||||||
super("AbstractPlayerImpl" + ExoPlayerActivity.TAG, ExoPlayerActivity.this);
|
super("VideoPlayerImpl" + MainVideoPlayer.TAG, MainVideoPlayer.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -238,8 +212,8 @@ public class ExoPlayerActivity extends Activity {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void playVideo(VideoStream videoStream, boolean autoPlay) {
|
public void playUrl(String url, String format, boolean autoPlay) {
|
||||||
super.playVideo(videoStream, autoPlay);
|
super.playUrl(url, format, autoPlay);
|
||||||
playPauseButton.setImageResource(autoPlay ? R.drawable.ic_pause_white : R.drawable.ic_play_arrow_white);
|
playPauseButton.setImageResource(autoPlay ? R.drawable.ic_pause_white : R.drawable.ic_play_arrow_white);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,16 +223,16 @@ public class ExoPlayerActivity extends Activity {
|
||||||
if (playerImpl.getPlayer() == null) return;
|
if (playerImpl.getPlayer() == null) return;
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||||
&& !PermissionHelper.checkSystemAlertWindowPermission(ExoPlayerActivity.this)) {
|
&& !PermissionHelper.checkSystemAlertWindowPermission(MainVideoPlayer.this)) {
|
||||||
Toast.makeText(ExoPlayerActivity.this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
Toast.makeText(MainVideoPlayer.this, R.string.msg_popup_permission, Toast.LENGTH_LONG).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playerImpl != null) playerImpl.destroy();
|
context.startService(NavigationHelper.getOpenVideoPlayerIntent(context, PopupVideoPlayer.class, playerImpl));
|
||||||
context.startService(NavigationHelper.getOpenPlayerIntent(context, PopupVideoPlayer.class, playerImpl));
|
if (playerImpl != null) playerImpl.destroyPlayer();
|
||||||
|
|
||||||
((View) getControlAnimationView().getParent()).setVisibility(View.GONE);
|
((View) getControlAnimationView().getParent()).setVisibility(View.GONE);
|
||||||
ExoPlayerActivity.this.finish();
|
MainVideoPlayer.this.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -307,28 +281,6 @@ public class ExoPlayerActivity extends Activity {
|
||||||
toggleOrientation();
|
toggleOrientation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onVideoPlayPause() {
|
|
||||||
super.onVideoPlayPause();
|
|
||||||
if (getPlayer().getPlayWhenReady()) {
|
|
||||||
animateView(playPauseButton, false, 80, 0, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
|
||||||
animateView(playPauseButton, true, 200, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
animateView(playPauseButton, false, 80, 0, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white);
|
|
||||||
animateView(playPauseButton, true, 200, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||||
super.onStopTrackingTouch(seekBar);
|
super.onStopTrackingTouch(seekBar);
|
||||||
|
@ -344,7 +296,8 @@ public class ExoPlayerActivity extends Activity {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError() {
|
public void onError(Exception exception) {
|
||||||
|
exception.printStackTrace();
|
||||||
Toast.makeText(context, "Failed to play this video", Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, "Failed to play this video", Toast.LENGTH_SHORT).show();
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
@ -366,10 +319,37 @@ public class ExoPlayerActivity extends Activity {
|
||||||
animateView(playPauseButton, false, 100, 0);
|
animateView(playPauseButton, false, 100, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlaying() {
|
||||||
|
super.onPlaying();
|
||||||
|
//playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
||||||
|
//animateView(playPauseButton, true, 500, 0);
|
||||||
|
|
||||||
|
animateView(playPauseButton, false, 80, 0, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
||||||
|
animateView(playPauseButton, true, 200, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
showSystemUi();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPaused() {
|
public void onPaused() {
|
||||||
super.onPaused();
|
super.onPaused();
|
||||||
animateView(playPauseButton, true, 100, 0);
|
//playPauseButton.setImageResource(R.drawable.ic_play_arrow_white);
|
||||||
|
//animateView(playPauseButton, true, 100, 0);
|
||||||
|
|
||||||
|
animateView(playPauseButton, false, 80, 0, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white);
|
||||||
|
animateView(playPauseButton, true, 200, 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
showSystemUi();
|
showSystemUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,12 +359,6 @@ public class ExoPlayerActivity extends Activity {
|
||||||
animateView(playPauseButton, false, 100, 0);
|
animateView(playPauseButton, false, 100, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPlaying() {
|
|
||||||
super.onPlaying();
|
|
||||||
animateView(playPauseButton, true, 500, 0);
|
|
||||||
showSystemUi();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCompleted() {
|
public void onCompleted() {
|
||||||
|
@ -467,14 +441,14 @@ public class ExoPlayerActivity extends Activity {
|
||||||
@Override
|
@Override
|
||||||
public boolean onSingleTapConfirmed(MotionEvent e) {
|
public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||||
if (DEBUG) Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]");
|
if (DEBUG) Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]");
|
||||||
if (playerImpl.getCurrentState() != StateInterface.STATE_PLAYING) return true;
|
if (playerImpl.getCurrentState() != BasePlayer.STATE_PLAYING) return true;
|
||||||
|
|
||||||
if (playerImpl.isControlsVisible()) playerImpl.animateView(playerImpl.getControlsRoot(), false, 150, 0, true);
|
if (playerImpl.isControlsVisible()) playerImpl.animateView(playerImpl.getControlsRoot(), false, 150, 0, true);
|
||||||
else {
|
else {
|
||||||
playerImpl.animateView(playerImpl.getControlsRoot(), true, 500, 0, new Runnable() {
|
playerImpl.animateView(playerImpl.getControlsRoot(), true, 500, 0, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, AbstractPlayer.DEFAULT_CONTROLS_HIDE_TIME, true);
|
playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
showSystemUi();
|
showSystemUi();
|
||||||
|
@ -482,25 +456,25 @@ public class ExoPlayerActivity extends Activity {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final float stepsBrightness = 21, stepBrightness = (1f / stepsBrightness), minBrightness = .01f;
|
private final float stepsBrightness = 15, stepBrightness = (1f / stepsBrightness), minBrightness = .01f;
|
||||||
private float currentBrightness = .5f;
|
private float currentBrightness = .5f;
|
||||||
|
|
||||||
private int currentVolume, maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
|
private int currentVolume, maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
|
||||||
|
private final float stepsVolume = 15, stepVolume = (float) Math.ceil(maxVolume / stepsVolume), minVolume = 0;
|
||||||
|
|
||||||
private final String brightnessUnicode = new String(Character.toChars(0x2600));
|
private final String brightnessUnicode = new String(Character.toChars(0x2600));
|
||||||
// private final String volumeUnicode = new String(Character.toChars(0x1F50A));
|
|
||||||
private final String volumeUnicode = new String(Character.toChars(0x1F508));
|
private final String volumeUnicode = new String(Character.toChars(0x1F508));
|
||||||
|
|
||||||
|
|
||||||
private final int MOVEMENT_THRESHOLD = 40;
|
private final int MOVEMENT_THRESHOLD = 40;
|
||||||
private final int eventsThreshold = 3;
|
private final int eventsThreshold = 8;
|
||||||
private boolean triggered = false;
|
private boolean triggered = false;
|
||||||
private int eventsNum;
|
private int eventsNum;
|
||||||
|
|
||||||
|
// TODO: Improve video gesture controls
|
||||||
@Override
|
@Override
|
||||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||||
//noinspection PointlessBooleanExpression
|
//noinspection PointlessBooleanExpression
|
||||||
if (DEBUG && false) Log.d(TAG, "ExoPlayerActivity.onScroll = " +
|
if (DEBUG && true) Log.d(TAG, "MainVideoPlayer.onScroll = " +
|
||||||
", e1.getRaw = [" + e1.getRawX() + ", " + e1.getRawY() + "]" +
|
", e1.getRaw = [" + e1.getRawX() + ", " + e1.getRawY() + "]" +
|
||||||
", e2.getRaw = [" + e2.getRawX() + ", " + e2.getRawY() + "]" +
|
", e2.getRaw = [" + e2.getRawX() + ", " + e2.getRawY() + "]" +
|
||||||
", distanceXy = [" + distanceX + ", " + distanceY + "]");
|
", distanceXy = [" + distanceX + ", " + distanceY + "]");
|
||||||
|
@ -510,16 +484,17 @@ public class ExoPlayerActivity extends Activity {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventsNum++ % eventsThreshold != 0 || playerImpl.getCurrentState() == StateInterface.STATE_COMPLETED) return false;
|
if (eventsNum++ % eventsThreshold != 0 || playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) return false;
|
||||||
isMoving = true;
|
isMoving = true;
|
||||||
// boolean up = !((e2.getY() - e1.getY()) > 0) && distanceY > 0; // Android's origin point is on top
|
// boolean up = !((e2.getY() - e1.getY()) > 0) && distanceY > 0; // Android's origin point is on top
|
||||||
boolean up = distanceY > 0;
|
boolean up = distanceY > 0;
|
||||||
|
|
||||||
|
|
||||||
if (e1.getX() > playerImpl.getRootView().getWidth() / 2) {
|
if (e1.getX() > playerImpl.getRootView().getWidth() / 2) {
|
||||||
currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + (up ? 1 : -1);
|
double floor = Math.floor(up ? stepVolume : -stepVolume);
|
||||||
|
currentVolume = (int) (audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) + floor);
|
||||||
if (currentVolume >= maxVolume) currentVolume = maxVolume;
|
if (currentVolume >= maxVolume) currentVolume = maxVolume;
|
||||||
if (currentVolume <= 0) currentVolume = 0;
|
if (currentVolume <= minVolume) currentVolume = (int) minVolume;
|
||||||
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0);
|
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0);
|
||||||
|
|
||||||
if (DEBUG) Log.d(TAG, "onScroll().volumeControl, currentVolume = " + currentVolume);
|
if (DEBUG) Log.d(TAG, "onScroll().volumeControl, currentVolume = " + currentVolume);
|
||||||
|
@ -555,8 +530,8 @@ public class ExoPlayerActivity extends Activity {
|
||||||
if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.animateView(playerImpl.getVolumeTextView(), false, 200, 200);
|
if (playerImpl.getVolumeTextView().getVisibility() == View.VISIBLE) playerImpl.animateView(playerImpl.getVolumeTextView(), false, 200, 200);
|
||||||
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.animateView(playerImpl.getBrightnessTextView(), false, 200, 200);
|
if (playerImpl.getBrightnessTextView().getVisibility() == View.VISIBLE) playerImpl.animateView(playerImpl.getBrightnessTextView(), false, 200, 200);
|
||||||
|
|
||||||
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == StateInterface.STATE_PLAYING) {
|
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == BasePlayer.STATE_PLAYING) {
|
||||||
playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, AbstractPlayer.DEFAULT_CONTROLS_HIDE_TIME);
|
playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import android.annotation.SuppressLint;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
|
@ -31,7 +30,6 @@ import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||||
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
|
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
|
||||||
|
|
||||||
import org.schabi.newpipe.ActivityCommunicator;
|
|
||||||
import org.schabi.newpipe.BuildConfig;
|
import org.schabi.newpipe.BuildConfig;
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
@ -39,7 +37,6 @@ import org.schabi.newpipe.ReCaptchaActivity;
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
import org.schabi.newpipe.extractor.StreamingService;
|
import org.schabi.newpipe.extractor.StreamingService;
|
||||||
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
import org.schabi.newpipe.extractor.stream_info.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
|
||||||
import org.schabi.newpipe.util.Constants;
|
import org.schabi.newpipe.util.Constants;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
@ -47,13 +44,13 @@ import org.schabi.newpipe.util.Utils;
|
||||||
import org.schabi.newpipe.workers.StreamExtractorWorker;
|
import org.schabi.newpipe.workers.StreamExtractorWorker;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service Popup Player implementing AbstractPlayer
|
* Service Popup Player implementing VideoPlayer
|
||||||
*
|
*
|
||||||
* @author mauriciocolli
|
* @author mauriciocolli
|
||||||
*/
|
*/
|
||||||
public class PopupVideoPlayer extends Service {
|
public class PopupVideoPlayer extends Service {
|
||||||
private static final String TAG = ".PopupVideoPlayer";
|
private static final String TAG = ".PopupVideoPlayer";
|
||||||
private static final boolean DEBUG = AbstractPlayer.DEBUG;
|
private static final boolean DEBUG = BasePlayer.DEBUG;
|
||||||
|
|
||||||
private static final int NOTIFICATION_ID = 40028922;
|
private static final int NOTIFICATION_ID = 40028922;
|
||||||
public static final String ACTION_CLOSE = "org.schabi.newpipe.player.PopupVideoPlayer.CLOSE";
|
public static final String ACTION_CLOSE = "org.schabi.newpipe.player.PopupVideoPlayer.CLOSE";
|
||||||
|
@ -61,8 +58,6 @@ public class PopupVideoPlayer extends Service {
|
||||||
public static final String ACTION_OPEN_DETAIL = "org.schabi.newpipe.player.PopupVideoPlayer.OPEN_DETAIL";
|
public static final String ACTION_OPEN_DETAIL = "org.schabi.newpipe.player.PopupVideoPlayer.OPEN_DETAIL";
|
||||||
public static final String ACTION_REPEAT = "org.schabi.newpipe.player.PopupVideoPlayer.REPEAT";
|
public static final String ACTION_REPEAT = "org.schabi.newpipe.player.PopupVideoPlayer.REPEAT";
|
||||||
|
|
||||||
private BroadcastReceiver broadcastReceiver;
|
|
||||||
|
|
||||||
private WindowManager windowManager;
|
private WindowManager windowManager;
|
||||||
private WindowManager.LayoutParams windowLayoutParams;
|
private WindowManager.LayoutParams windowLayoutParams;
|
||||||
private GestureDetector gestureDetector;
|
private GestureDetector gestureDetector;
|
||||||
|
@ -81,7 +76,7 @@ public class PopupVideoPlayer extends Service {
|
||||||
private ImageLoader imageLoader = ImageLoader.getInstance();
|
private ImageLoader imageLoader = ImageLoader.getInstance();
|
||||||
private DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder().cacheInMemory(true).build();
|
private DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder().cacheInMemory(true).build();
|
||||||
|
|
||||||
private AbstractPlayerImpl playerImpl;
|
private VideoPlayerImpl playerImpl;
|
||||||
private StreamExtractorWorker currentExtractorWorker;
|
private StreamExtractorWorker currentExtractorWorker;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -92,9 +87,8 @@ public class PopupVideoPlayer extends Service {
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||||
notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
|
notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
|
||||||
initReceiver();
|
|
||||||
|
|
||||||
playerImpl = new AbstractPlayerImpl();
|
playerImpl = new VideoPlayerImpl();
|
||||||
ThemeHelper.setTheme(this, false);
|
ThemeHelper.setTheme(this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +126,6 @@ public class PopupVideoPlayer extends Service {
|
||||||
}
|
}
|
||||||
if (imageLoader != null) imageLoader.clearMemoryCache();
|
if (imageLoader != null) imageLoader.clearMemoryCache();
|
||||||
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
|
if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID);
|
||||||
if (broadcastReceiver != null) unregisterReceiver(broadcastReceiver);
|
|
||||||
if (currentExtractorWorker != null) {
|
if (currentExtractorWorker != null) {
|
||||||
currentExtractorWorker.cancel();
|
currentExtractorWorker.cancel();
|
||||||
currentExtractorWorker = null;
|
currentExtractorWorker = null;
|
||||||
|
@ -148,39 +141,6 @@ public class PopupVideoPlayer extends Service {
|
||||||
// Init
|
// Init
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private void initReceiver() {
|
|
||||||
broadcastReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (DEBUG) Log.d(TAG, "onReceive() called with: context = [" + context + "], intent = [" + intent + "]");
|
|
||||||
switch (intent.getAction()) {
|
|
||||||
case ACTION_CLOSE:
|
|
||||||
onVideoClose();
|
|
||||||
break;
|
|
||||||
case ACTION_PLAY_PAUSE:
|
|
||||||
playerImpl.onVideoPlayPause();
|
|
||||||
break;
|
|
||||||
case ACTION_OPEN_DETAIL:
|
|
||||||
onOpenDetail(PopupVideoPlayer.this, playerImpl.getVideoUrl(), playerImpl.getVideoTitle());
|
|
||||||
break;
|
|
||||||
case ACTION_REPEAT:
|
|
||||||
playerImpl.onRepeatClicked();
|
|
||||||
break;
|
|
||||||
case AbstractPlayer.ACTION_UPDATE_THUMB:
|
|
||||||
playerImpl.onUpdateThumbnail(intent);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
|
||||||
intentFilter.addAction(ACTION_CLOSE);
|
|
||||||
intentFilter.addAction(ACTION_PLAY_PAUSE);
|
|
||||||
intentFilter.addAction(ACTION_OPEN_DETAIL);
|
|
||||||
intentFilter.addAction(ACTION_REPEAT);
|
|
||||||
intentFilter.addAction(AbstractPlayer.ACTION_UPDATE_THUMB);
|
|
||||||
registerReceiver(broadcastReceiver, intentFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("RtlHardcoded")
|
@SuppressLint("RtlHardcoded")
|
||||||
private void initPopup() {
|
private void initPopup() {
|
||||||
if (DEBUG) Log.d(TAG, "initPopup() called");
|
if (DEBUG) Log.d(TAG, "initPopup() called");
|
||||||
|
@ -213,8 +173,9 @@ public class PopupVideoPlayer extends Service {
|
||||||
private NotificationCompat.Builder createNotification() {
|
private NotificationCompat.Builder createNotification() {
|
||||||
notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_popup_notification);
|
notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_popup_notification);
|
||||||
|
|
||||||
if (playerImpl.getVideoThumbnail() != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, playerImpl.getVideoThumbnail());
|
if (playerImpl.getVideoThumbnail() == null) notRemoteView.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail);
|
||||||
else notRemoteView.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail);
|
else notRemoteView.setImageViewBitmap(R.id.notificationCover, playerImpl.getVideoThumbnail());
|
||||||
|
|
||||||
notRemoteView.setTextViewText(R.id.notificationSongName, playerImpl.getVideoTitle());
|
notRemoteView.setTextViewText(R.id.notificationSongName, playerImpl.getVideoTitle());
|
||||||
notRemoteView.setTextViewText(R.id.notificationArtist, playerImpl.getChannelName());
|
notRemoteView.setTextViewText(R.id.notificationArtist, playerImpl.getChannelName());
|
||||||
|
|
||||||
|
@ -302,21 +263,35 @@ public class PopupVideoPlayer extends Service {
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private class AbstractPlayerImpl extends AbstractPlayer {
|
private class VideoPlayerImpl extends VideoPlayer {
|
||||||
AbstractPlayerImpl() {
|
VideoPlayerImpl() {
|
||||||
super("AbstractPlayerImpl" + PopupVideoPlayer.TAG, PopupVideoPlayer.this);
|
super("VideoPlayerImpl" + PopupVideoPlayer.TAG, PopupVideoPlayer.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void playVideo(VideoStream videoStream, boolean autoPlay) {
|
public void playUrl(String url, String format, boolean autoPlay) {
|
||||||
super.playVideo(videoStream, autoPlay);
|
super.playUrl(url, format, autoPlay);
|
||||||
|
|
||||||
windowLayoutParams.width = (int) getMinimumVideoWidth(currentPopupHeight);
|
windowLayoutParams.width = (int) getMinimumVideoWidth(currentPopupHeight);
|
||||||
windowManager.updateViewLayout(getRootView(), windowLayoutParams);
|
windowManager.updateViewLayout(getRootView(), windowLayoutParams);
|
||||||
|
|
||||||
notBuilder = createNotification();
|
notBuilder = createNotification();
|
||||||
startForeground(NOTIFICATION_ID, notBuilder.build());
|
startForeground(NOTIFICATION_ID, notBuilder.build());
|
||||||
notificationManager.notify(NOTIFICATION_ID, notBuilder.build());
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
super.destroy();
|
||||||
|
if (notRemoteView != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onThumbnailReceived(Bitmap thumbnail) {
|
||||||
|
super.onThumbnailReceived(thumbnail);
|
||||||
|
if (thumbnail != null) {
|
||||||
|
if (notRemoteView != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, thumbnail);
|
||||||
|
updateNotification(-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -324,8 +299,8 @@ public class PopupVideoPlayer extends Service {
|
||||||
if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called");
|
if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called");
|
||||||
Intent intent;
|
Intent intent;
|
||||||
if (!getSharedPreferences().getBoolean(getResources().getString(R.string.use_old_player_key), false)) {
|
if (!getSharedPreferences().getBoolean(getResources().getString(R.string.use_old_player_key), false)) {
|
||||||
intent = NavigationHelper.getOpenPlayerIntent(context, ExoPlayerActivity.class, playerImpl);
|
intent = NavigationHelper.getOpenVideoPlayerIntent(context, MainVideoPlayer.class, playerImpl);
|
||||||
if (!playerImpl.isStartedFromNewPipe()) intent.putExtra(AbstractPlayer.STARTED_FROM_NEWPIPE, false);
|
if (!playerImpl.isStartedFromNewPipe()) intent.putExtra(VideoPlayer.STARTED_FROM_NEWPIPE, false);
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
} else {
|
} else {
|
||||||
intent = new Intent(PopupVideoPlayer.this, PlayVideoActivity.class)
|
intent = new Intent(PopupVideoPlayer.this, PlayVideoActivity.class)
|
||||||
|
@ -335,9 +310,9 @@ public class PopupVideoPlayer extends Service {
|
||||||
.putExtra(PlayVideoActivity.START_POSITION, Math.round(getPlayer().getCurrentPosition() / 1000f));
|
.putExtra(PlayVideoActivity.START_POSITION, Math.round(getPlayer().getCurrentPosition() / 1000f));
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
}
|
}
|
||||||
stopSelf();
|
|
||||||
if (playerImpl != null) playerImpl.destroy();
|
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
|
if (playerImpl != null) playerImpl.destroyPlayer();
|
||||||
|
stopSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -360,13 +335,6 @@ public class PopupVideoPlayer extends Service {
|
||||||
updateNotification(-1);
|
updateNotification(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUpdateThumbnail(Intent intent) {
|
|
||||||
super.onUpdateThumbnail(intent);
|
|
||||||
if (getVideoThumbnail() != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, getVideoThumbnail());
|
|
||||||
updateNotification(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDismiss(PopupMenu menu) {
|
public void onDismiss(PopupMenu menu) {
|
||||||
super.onDismiss(menu);
|
super.onDismiss(menu);
|
||||||
|
@ -374,11 +342,45 @@ public class PopupVideoPlayer extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError() {
|
public void onError(Exception exception) {
|
||||||
|
exception.printStackTrace();
|
||||||
Toast.makeText(context, "Failed to play this video", Toast.LENGTH_SHORT).show();
|
Toast.makeText(context, "Failed to play this video", Toast.LENGTH_SHORT).show();
|
||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Broadcast Receiver
|
||||||
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setupBroadcastReceiver(IntentFilter intentFilter) {
|
||||||
|
super.setupBroadcastReceiver(intentFilter);
|
||||||
|
if (DEBUG) Log.d(TAG, "setupBroadcastReceiver() called with: intentFilter = [" + intentFilter + "]");
|
||||||
|
intentFilter.addAction(ACTION_CLOSE);
|
||||||
|
intentFilter.addAction(ACTION_PLAY_PAUSE);
|
||||||
|
intentFilter.addAction(ACTION_OPEN_DETAIL);
|
||||||
|
intentFilter.addAction(ACTION_REPEAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBroadcastReceived(Intent intent) {
|
||||||
|
super.onBroadcastReceived(intent);
|
||||||
|
if (DEBUG) Log.d(TAG, "onBroadcastReceived() called with: intent = [" + intent + "]");
|
||||||
|
switch (intent.getAction()) {
|
||||||
|
case ACTION_CLOSE:
|
||||||
|
onVideoClose();
|
||||||
|
break;
|
||||||
|
case ACTION_PLAY_PAUSE:
|
||||||
|
playerImpl.onVideoPlayPause();
|
||||||
|
break;
|
||||||
|
case ACTION_OPEN_DETAIL:
|
||||||
|
onOpenDetail(PopupVideoPlayer.this, playerImpl.getVideoUrl(), playerImpl.getVideoTitle());
|
||||||
|
break;
|
||||||
|
case ACTION_REPEAT:
|
||||||
|
playerImpl.onRepeatClicked();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// States
|
// States
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -483,8 +485,8 @@ public class PopupVideoPlayer extends Service {
|
||||||
|
|
||||||
private void onScrollEnd() {
|
private void onScrollEnd() {
|
||||||
if (DEBUG) Log.d(TAG, "onScrollEnd() called");
|
if (DEBUG) Log.d(TAG, "onScrollEnd() called");
|
||||||
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == StateInterface.STATE_PLAYING) {
|
if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == BasePlayer.STATE_PLAYING) {
|
||||||
playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, AbstractPlayer.DEFAULT_CONTROLS_HIDE_TIME);
|
playerImpl.animateView(playerImpl.getControlsRoot(), false, 300, VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,6 +518,7 @@ public class PopupVideoPlayer extends Service {
|
||||||
public void onReceive(StreamInfo info) {
|
public void onReceive(StreamInfo info) {
|
||||||
playerImpl.setVideoTitle(info.title);
|
playerImpl.setVideoTitle(info.title);
|
||||||
playerImpl.setVideoUrl(info.webpage_url);
|
playerImpl.setVideoUrl(info.webpage_url);
|
||||||
|
playerImpl.setVideoThumbnailUrl(info.thumbnail_url);
|
||||||
playerImpl.setChannelName(info.uploader);
|
playerImpl.setChannelName(info.uploader);
|
||||||
|
|
||||||
playerImpl.setVideoStreamsList(Utils.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false));
|
playerImpl.setVideoStreamsList(Utils.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false));
|
||||||
|
@ -537,7 +540,7 @@ public class PopupVideoPlayer extends Service {
|
||||||
mainHandler.post(new Runnable() {
|
mainHandler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
playerImpl.playVideo(playerImpl.getSelectedVideoStream(), true);
|
playerImpl.play(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -551,7 +554,6 @@ public class PopupVideoPlayer extends Service {
|
||||||
playerImpl.setVideoThumbnail(loadedImage);
|
playerImpl.setVideoThumbnail(loadedImage);
|
||||||
if (loadedImage != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage);
|
if (loadedImage != null) notRemoteView.setImageViewBitmap(R.id.notificationCover, loadedImage);
|
||||||
updateNotification(-1);
|
updateNotification(-1);
|
||||||
ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail = loadedImage;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -619,6 +621,7 @@ public class PopupVideoPlayer extends Service {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUnrecoverableError(Exception exception) {
|
public void onUnrecoverableError(Exception exception) {
|
||||||
|
exception.printStackTrace();
|
||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
package org.schabi.newpipe.player;
|
|
||||||
|
|
||||||
public interface StateInterface {
|
|
||||||
int STATE_LOADING = 123;
|
|
||||||
int STATE_PLAYING = 124;
|
|
||||||
int STATE_BUFFERING = 125;
|
|
||||||
int STATE_PAUSED = 126;
|
|
||||||
int STATE_PAUSED_SEEK = 127;
|
|
||||||
int STATE_COMPLETED = 128;
|
|
||||||
|
|
||||||
void changeState(int state);
|
|
||||||
|
|
||||||
void onLoading();
|
|
||||||
void onPlaying();
|
|
||||||
void onBuffering();
|
|
||||||
void onPaused();
|
|
||||||
void onPausedSeek();
|
|
||||||
void onCompleted();
|
|
||||||
}
|
|
|
@ -7,16 +7,12 @@ import android.animation.PropertyValuesHolder;
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
@ -29,84 +25,42 @@ import android.widget.ProgressBar;
|
||||||
import android.widget.SeekBar;
|
import android.widget.SeekBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.C;
|
|
||||||
import com.google.android.exoplayer2.DefaultLoadControl;
|
|
||||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
|
||||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
|
||||||
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
|
|
||||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
import com.google.android.exoplayer2.source.MergingMediaSource;
|
import com.google.android.exoplayer2.source.MergingMediaSource;
|
||||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
|
||||||
import com.google.android.exoplayer2.source.dash.DashMediaSource;
|
|
||||||
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
|
|
||||||
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
|
|
||||||
import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource;
|
|
||||||
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
|
|
||||||
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
|
|
||||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
|
||||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||||
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
|
||||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
|
||||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
|
||||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
|
|
||||||
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;
|
|
||||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
|
||||||
import com.google.android.exoplayer2.util.Util;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.ActivityCommunicator;
|
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.extractor.MediaFormat;
|
import org.schabi.newpipe.extractor.MediaFormat;
|
||||||
import org.schabi.newpipe.extractor.stream_info.AudioStream;
|
import org.schabi.newpipe.extractor.stream_info.AudioStream;
|
||||||
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
import org.schabi.newpipe.extractor.stream_info.VideoStream;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Formatter;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common properties of the players
|
* Base for <b>video</b> players
|
||||||
*
|
*
|
||||||
* @author mauriciocolli
|
* @author mauriciocolli
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unused", "WeakerAccess"})
|
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||||
public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBarChangeListener, View.OnClickListener, ExoPlayer.EventListener, PopupMenu.OnMenuItemClickListener, PopupMenu.OnDismissListener, SimpleExoPlayer.VideoListener {
|
public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.VideoListener, SeekBar.OnSeekBarChangeListener, View.OnClickListener, ExoPlayer.EventListener, PopupMenu.OnMenuItemClickListener, PopupMenu.OnDismissListener {
|
||||||
public static final boolean DEBUG = false;
|
public static final boolean DEBUG = BasePlayer.DEBUG;
|
||||||
public final String TAG;
|
public final String TAG;
|
||||||
|
|
||||||
protected Context context;
|
|
||||||
private SharedPreferences sharedPreferences;
|
|
||||||
|
|
||||||
private static int currentState = -1;
|
|
||||||
public static final String ACTION_UPDATE_THUMB = "org.schabi.newpipe.player.AbstractPlayer.UPDATE_THUMBNAIL";
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Intent
|
// Intent
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public static final String VIDEO_URL = "video_url";
|
|
||||||
public static final String VIDEO_STREAMS_LIST = "video_streams_list";
|
public static final String VIDEO_STREAMS_LIST = "video_streams_list";
|
||||||
public static final String VIDEO_ONLY_AUDIO_STREAM = "video_only_audio_stream";
|
public static final String VIDEO_ONLY_AUDIO_STREAM = "video_only_audio_stream";
|
||||||
public static final String VIDEO_TITLE = "video_title";
|
|
||||||
public static final String INDEX_SEL_VIDEO_STREAM = "index_selected_video_stream";
|
public static final String INDEX_SEL_VIDEO_STREAM = "index_selected_video_stream";
|
||||||
public static final String START_POSITION = "start_position";
|
|
||||||
public static final String CHANNEL_NAME = "channel_name";
|
|
||||||
public static final String STARTED_FROM_NEWPIPE = "started_from_newpipe";
|
public static final String STARTED_FROM_NEWPIPE = "started_from_newpipe";
|
||||||
|
|
||||||
private String videoUrl = "";
|
|
||||||
private int videoStartPos = -1;
|
|
||||||
private String videoTitle = "";
|
|
||||||
private Bitmap videoThumbnail;
|
|
||||||
private String channelName = "";
|
|
||||||
private int selectedIndexStream;
|
private int selectedIndexStream;
|
||||||
private ArrayList<VideoStream> videoStreamsList = new ArrayList<>();
|
private ArrayList<VideoStream> videoStreamsList = new ArrayList<>();
|
||||||
private AudioStream videoOnlyAudioStream;
|
private AudioStream videoOnlyAudioStream;
|
||||||
|
@ -115,36 +69,10 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
// Player
|
// Player
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public static final int FAST_FORWARD_REWIND_AMOUNT = 10000; // 10 Seconds
|
|
||||||
public static final int DEFAULT_CONTROLS_HIDE_TIME = 3000; // 3 Seconds
|
public static final int DEFAULT_CONTROLS_HIDE_TIME = 3000; // 3 Seconds
|
||||||
public static final String CACHE_FOLDER_NAME = "exoplayer";
|
|
||||||
|
|
||||||
private boolean startedFromNewPipe = true;
|
private boolean startedFromNewPipe = true;
|
||||||
private boolean isPrepared = false;
|
|
||||||
private boolean wasPlaying = false;
|
private boolean wasPlaying = false;
|
||||||
private SimpleExoPlayer simpleExoPlayer;
|
|
||||||
|
|
||||||
@SuppressWarnings("FieldCanBeLocal")
|
|
||||||
private MediaSource videoSource;
|
|
||||||
private static CacheDataSourceFactory cacheDataSourceFactory;
|
|
||||||
private static final DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
|
|
||||||
private static final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
|
|
||||||
|
|
||||||
private AtomicBoolean isProgressLoopRunning = new AtomicBoolean();
|
|
||||||
private Handler progressLoop;
|
|
||||||
private Runnable progressUpdate;
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
|
||||||
// Repeat
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
private RepeatMode currentRepeatMode = RepeatMode.REPEAT_DISABLED;
|
|
||||||
|
|
||||||
public enum RepeatMode {
|
|
||||||
REPEAT_DISABLED,
|
|
||||||
REPEAT_ONE,
|
|
||||||
REPEAT_ALL
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Views
|
// Views
|
||||||
|
@ -181,35 +109,15 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public AbstractPlayer(String debugTag, Context context) {
|
public VideoPlayer(String debugTag, Context context) {
|
||||||
|
super(context);
|
||||||
this.TAG = debugTag;
|
this.TAG = debugTag;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.progressLoop = new Handler();
|
|
||||||
this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
|
|
||||||
if (cacheDataSourceFactory == null) {
|
|
||||||
DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(context, Util.getUserAgent(context, context.getPackageName()), bandwidthMeter);
|
|
||||||
File cacheDir = new File(context.getExternalCacheDir(), CACHE_FOLDER_NAME);
|
|
||||||
if (!cacheDir.exists()) {
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
cacheDir.mkdir();
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(TAG, "buildMediaSource: cacheDir = " + cacheDir.getAbsolutePath());
|
|
||||||
SimpleCache simpleCache = new SimpleCache(cacheDir, new LeastRecentlyUsedCacheEvictor(64 * 1024 * 1024L));
|
|
||||||
cacheDataSourceFactory = new CacheDataSourceFactory(simpleCache, dataSourceFactory, CacheDataSource.FLAG_BLOCK_ON_CACHE, 512 * 1024);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setup(View rootView) {
|
public void setup(View rootView) {
|
||||||
initViews(rootView);
|
initViews(rootView);
|
||||||
initListeners();
|
setup();
|
||||||
if (simpleExoPlayer == null) initPlayer();
|
|
||||||
else {
|
|
||||||
simpleExoPlayer.addListener(this);
|
|
||||||
simpleExoPlayer.setVideoListener(this);
|
|
||||||
simpleExoPlayer.setVideoSurfaceView(surfaceView);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initViews(View rootView) {
|
public void initViews(View rootView) {
|
||||||
|
@ -241,36 +149,24 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void initListeners() {
|
public void initListeners() {
|
||||||
progressUpdate = new Runnable() {
|
super.initListeners();
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
//if(DEBUG) Log.d(TAG, "progressUpdate run() called");
|
|
||||||
onUpdateProgress((int) simpleExoPlayer.getCurrentPosition(), (int) simpleExoPlayer.getDuration(), simpleExoPlayer.getBufferedPercentage());
|
|
||||||
if (isProgressLoopRunning.get()) progressLoop.postDelayed(this, 100);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
playbackSeekBar.setOnSeekBarChangeListener(this);
|
playbackSeekBar.setOnSeekBarChangeListener(this);
|
||||||
fullScreenButton.setOnClickListener(this);
|
fullScreenButton.setOnClickListener(this);
|
||||||
qualityTextView.setOnClickListener(this);
|
qualityTextView.setOnClickListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void initPlayer() {
|
public void initPlayer() {
|
||||||
if (DEBUG) Log.d(TAG, "initPlayer() called with: context = [" + context + "]");
|
super.initPlayer();
|
||||||
|
|
||||||
AdaptiveTrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
|
|
||||||
DefaultTrackSelector defaultTrackSelector = new DefaultTrackSelector(trackSelectionFactory);
|
|
||||||
DefaultLoadControl loadControl = new DefaultLoadControl();
|
|
||||||
|
|
||||||
simpleExoPlayer = ExoPlayerFactory.newSimpleInstance(context, defaultTrackSelector, loadControl);
|
|
||||||
simpleExoPlayer.addListener(this);
|
|
||||||
simpleExoPlayer.setVideoListener(this);
|
|
||||||
simpleExoPlayer.setVideoSurfaceView(surfaceView);
|
simpleExoPlayer.setVideoSurfaceView(surfaceView);
|
||||||
|
simpleExoPlayer.setVideoListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void handleIntent(Intent intent) {
|
public void handleIntent(Intent intent) {
|
||||||
|
super.handleIntent(intent);
|
||||||
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
|
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
|
||||||
if (intent == null) return;
|
if (intent == null) return;
|
||||||
|
|
||||||
|
@ -284,79 +180,36 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
Serializable audioStream = intent.getSerializableExtra(VIDEO_ONLY_AUDIO_STREAM);
|
Serializable audioStream = intent.getSerializableExtra(VIDEO_ONLY_AUDIO_STREAM);
|
||||||
if (audioStream != null) videoOnlyAudioStream = (AudioStream) audioStream;
|
if (audioStream != null) videoOnlyAudioStream = (AudioStream) audioStream;
|
||||||
|
|
||||||
videoUrl = intent.getStringExtra(VIDEO_URL);
|
|
||||||
videoTitle = intent.getStringExtra(VIDEO_TITLE);
|
|
||||||
videoStartPos = intent.getIntExtra(START_POSITION, -1);
|
|
||||||
channelName = intent.getStringExtra(CHANNEL_NAME);
|
|
||||||
startedFromNewPipe = intent.getBooleanExtra(STARTED_FROM_NEWPIPE, true);
|
startedFromNewPipe = intent.getBooleanExtra(STARTED_FROM_NEWPIPE, true);
|
||||||
try {
|
play(true);
|
||||||
videoThumbnail = ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail;
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
playVideo(getSelectedVideoStream(), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playVideo(VideoStream videoStream, boolean autoPlay) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "playVideo() called with: videoStream = [" + videoStream + ", " + videoStream.url + ", isVideoOnly = " + videoStream.isVideoOnly + "], autoPlay = [" + autoPlay + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (videoStream == null || videoStream.url == null || simpleExoPlayer == null) {
|
public void play(boolean autoPlay) {
|
||||||
onError();
|
playUrl(getSelectedVideoStream().url, MediaFormat.getSuffixById(getSelectedVideoStream().format), autoPlay);
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
isPrepared = false;
|
@Override
|
||||||
|
public void playUrl(String url, String format, boolean autoPlay) {
|
||||||
|
if (DEBUG) Log.d(TAG, "play() called with: url = [" + url + "], autoPlay = [" + autoPlay + "]");
|
||||||
qualityChanged = false;
|
qualityChanged = false;
|
||||||
|
|
||||||
|
if (url == null || simpleExoPlayer == null) {
|
||||||
|
RuntimeException runtimeException = new RuntimeException((url == null ? "Url " : "Player ") + " null");
|
||||||
|
onError(runtimeException);
|
||||||
|
throw runtimeException;
|
||||||
|
}
|
||||||
|
|
||||||
qualityPopupMenu.getMenu().removeGroup(qualityPopupMenuGroupId);
|
qualityPopupMenu.getMenu().removeGroup(qualityPopupMenuGroupId);
|
||||||
buildQualityMenu(qualityPopupMenu);
|
buildQualityMenu(qualityPopupMenu);
|
||||||
|
|
||||||
videoSource = buildMediaSource(videoStream, MediaFormat.getSuffixById(getSelectedVideoStream().format));
|
super.playUrl(url, format, autoPlay);
|
||||||
|
|
||||||
if (simpleExoPlayer.getPlaybackState() != ExoPlayer.STATE_IDLE) simpleExoPlayer.stop();
|
|
||||||
if (videoStartPos > 0) simpleExoPlayer.seekTo(videoStartPos);
|
|
||||||
simpleExoPlayer.prepare(videoSource);
|
|
||||||
simpleExoPlayer.setPlayWhenReady(autoPlay);
|
|
||||||
changeState(STATE_LOADING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() {
|
@Override
|
||||||
if (DEBUG) Log.d(TAG, "destroy() called");
|
public MediaSource buildMediaSource(String url, String overrideExtension) {
|
||||||
if (simpleExoPlayer != null) {
|
MediaSource mediaSource = super.buildMediaSource(url, overrideExtension);
|
||||||
simpleExoPlayer.stop();
|
if (!getSelectedVideoStream().isVideoOnly) return mediaSource;
|
||||||
simpleExoPlayer.release();
|
|
||||||
}
|
|
||||||
if (progressLoop != null) stopProgressLoop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private MediaSource buildMediaSource(VideoStream videoStream, String overrideExtension) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "buildMediaSource() called with: videoStream = [" + videoStream + ", " + videoStream.url + "isVideoOnly = " + videoStream.isVideoOnly + "], overrideExtension = [" + overrideExtension + "]");
|
|
||||||
}
|
|
||||||
Uri uri = Uri.parse(videoStream.url);
|
|
||||||
int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri) : Util.inferContentType("." + overrideExtension);
|
|
||||||
MediaSource mediaSource;
|
|
||||||
switch (type) {
|
|
||||||
case C.TYPE_SS:
|
|
||||||
mediaSource = new SsMediaSource(uri, cacheDataSourceFactory, new DefaultSsChunkSource.Factory(cacheDataSourceFactory), null, null);
|
|
||||||
break;
|
|
||||||
case C.TYPE_DASH:
|
|
||||||
mediaSource = new DashMediaSource(uri, cacheDataSourceFactory, new DefaultDashChunkSource.Factory(cacheDataSourceFactory), null, null);
|
|
||||||
break;
|
|
||||||
case C.TYPE_HLS:
|
|
||||||
mediaSource = new HlsMediaSource(uri, cacheDataSourceFactory, null, null);
|
|
||||||
break;
|
|
||||||
case C.TYPE_OTHER:
|
|
||||||
mediaSource = new ExtractorMediaSource(uri, cacheDataSourceFactory, extractorsFactory, null, null);
|
|
||||||
break;
|
|
||||||
default: {
|
|
||||||
throw new IllegalStateException("Unsupported type: " + type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!videoStream.isVideoOnly) return mediaSource;
|
|
||||||
|
|
||||||
Uri audioUri = Uri.parse(videoOnlyAudioStream.url);
|
Uri audioUri = Uri.parse(videoOnlyAudioStream.url);
|
||||||
return new MergingMediaSource(mediaSource, new ExtractorMediaSource(audioUri, cacheDataSourceFactory, extractorsFactory, null, null));
|
return new MergingMediaSource(mediaSource, new ExtractorMediaSource(audioUri, cacheDataSourceFactory, extractorsFactory, null, null));
|
||||||
|
@ -370,39 +223,12 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
qualityTextView.setText(getSelectedVideoStream().resolution);
|
qualityTextView.setText(getSelectedVideoStream().resolution);
|
||||||
popupMenu.setOnMenuItemClickListener(this);
|
popupMenu.setOnMenuItemClickListener(this);
|
||||||
popupMenu.setOnDismissListener(this);
|
popupMenu.setOnDismissListener(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// States Implementation
|
// States Implementation
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
|
||||||
public void changeState(int state) {
|
|
||||||
if (DEBUG) Log.d(TAG, "changeState() called with: state = [" + state + "]");
|
|
||||||
currentState = state;
|
|
||||||
switch (state) {
|
|
||||||
case STATE_LOADING:
|
|
||||||
onLoading();
|
|
||||||
break;
|
|
||||||
case STATE_PLAYING:
|
|
||||||
onPlaying();
|
|
||||||
break;
|
|
||||||
case STATE_BUFFERING:
|
|
||||||
onBuffering();
|
|
||||||
break;
|
|
||||||
case STATE_PAUSED:
|
|
||||||
onPaused();
|
|
||||||
break;
|
|
||||||
case STATE_PAUSED_SEEK:
|
|
||||||
onPausedSeek();
|
|
||||||
break;
|
|
||||||
case STATE_COMPLETED:
|
|
||||||
onCompleted();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoading() {
|
public void onLoading() {
|
||||||
if (DEBUG) Log.d(TAG, "onLoading() called");
|
if (DEBUG) Log.d(TAG, "onLoading() called");
|
||||||
|
@ -463,7 +289,6 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
|
|
||||||
if (isProgressLoopRunning.get()) stopProgressLoop();
|
if (isProgressLoopRunning.get()) stopProgressLoop();
|
||||||
|
|
||||||
if (videoThumbnail != null) endScreen.setImageBitmap(videoThumbnail);
|
|
||||||
animateView(controlsRoot, true, 500, 0);
|
animateView(controlsRoot, true, 500, 0);
|
||||||
animateView(endScreen, true, 800, 0);
|
animateView(endScreen, true, 800, 0);
|
||||||
animateView(currentDisplaySeek, false, 200, 0);
|
animateView(currentDisplaySeek, false, 200, 0);
|
||||||
|
@ -481,74 +306,10 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
|
|
||||||
if (currentRepeatMode == RepeatMode.REPEAT_ONE) {
|
if (currentRepeatMode == RepeatMode.REPEAT_ONE) {
|
||||||
changeState(STATE_LOADING);
|
changeState(STATE_LOADING);
|
||||||
getPlayer().seekTo(0);
|
simpleExoPlayer.seekTo(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
|
||||||
// ExoPlayer Listener
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadingChanged(boolean isLoading) {
|
|
||||||
if (DEBUG) Log.d(TAG, "onLoadingChanged() called with: isLoading = [" + isLoading + "]");
|
|
||||||
|
|
||||||
if (!isLoading && getCurrentState() == STATE_PAUSED && isProgressLoopRunning.get()) stopProgressLoop();
|
|
||||||
else if (isLoading && !isProgressLoopRunning.get()) startProgressLoop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
|
||||||
if (DEBUG) Log.d(TAG, "onPlayerStateChanged() called with: playWhenReady = [" + playWhenReady + "], playbackState = [" + playbackState + "]");
|
|
||||||
if (getCurrentState() == STATE_PAUSED_SEEK) {
|
|
||||||
if (DEBUG) Log.d(TAG, "onPlayerStateChanged() currently on PausedSeek");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (playbackState) {
|
|
||||||
case ExoPlayer.STATE_IDLE: // 1
|
|
||||||
isPrepared = false;
|
|
||||||
break;
|
|
||||||
case ExoPlayer.STATE_BUFFERING: // 2
|
|
||||||
if (isPrepared && getCurrentState() != STATE_LOADING) changeState(STATE_BUFFERING);
|
|
||||||
break;
|
|
||||||
case ExoPlayer.STATE_READY: //3
|
|
||||||
if (!isPrepared) {
|
|
||||||
isPrepared = true;
|
|
||||||
onPrepared(playWhenReady);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (currentState == STATE_PAUSED_SEEK) break;
|
|
||||||
changeState(playWhenReady ? STATE_PLAYING : STATE_PAUSED);
|
|
||||||
break;
|
|
||||||
case ExoPlayer.STATE_ENDED: // 4
|
|
||||||
changeState(STATE_COMPLETED);
|
|
||||||
isPrepared = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPlayerError(ExoPlaybackException error) {
|
|
||||||
if (DEBUG) Log.d(TAG, "onPlayerError() called with: error = [" + error + "]");
|
|
||||||
onError();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPositionDiscontinuity() {
|
|
||||||
if (DEBUG) Log.d(TAG, "onPositionDiscontinuity() called");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// ExoPlayer Video Listener
|
// ExoPlayer Video Listener
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -570,8 +331,7 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
// General Player
|
// General Player
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public abstract void onError();
|
@Override
|
||||||
|
|
||||||
public void onPrepared(boolean playWhenReady) {
|
public void onPrepared(boolean playWhenReady) {
|
||||||
if (DEBUG) Log.d(TAG, "onPrepared() called with: playWhenReady = [" + playWhenReady + "]");
|
if (DEBUG) Log.d(TAG, "onPrepared() called with: playWhenReady = [" + playWhenReady + "]");
|
||||||
|
|
||||||
|
@ -584,11 +344,19 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
playbackSeekBar.setMax((int) simpleExoPlayer.getDuration());
|
playbackSeekBar.setMax((int) simpleExoPlayer.getDuration());
|
||||||
playbackEndTime.setText(getTimeString((int) simpleExoPlayer.getDuration()));
|
playbackEndTime.setText(getTimeString((int) simpleExoPlayer.getDuration()));
|
||||||
|
|
||||||
changeState(playWhenReady ? STATE_PLAYING : STATE_PAUSED);
|
super.onPrepared(playWhenReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
super.destroy();
|
||||||
|
if (endScreen != null) endScreen.setImageBitmap(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onUpdateProgress(int currentProgress, int duration, int bufferPercent) {
|
public void onUpdateProgress(int currentProgress, int duration, int bufferPercent) {
|
||||||
if (!isPrepared) return;
|
if (!isPrepared) return;
|
||||||
|
|
||||||
if (currentState != STATE_PAUSED) {
|
if (currentState != STATE_PAUSED) {
|
||||||
if (currentState != STATE_PAUSED_SEEK) playbackSeekBar.setProgress(currentProgress);
|
if (currentState != STATE_PAUSED_SEEK) playbackSeekBar.setProgress(currentProgress);
|
||||||
playbackCurrentTime.setText(getTimeString(currentProgress));
|
playbackCurrentTime.setText(getTimeString(currentProgress));
|
||||||
|
@ -601,32 +369,32 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onUpdateThumbnail(Intent intent) {
|
@Override
|
||||||
if (DEBUG) Log.d(TAG, "onUpdateThumbnail() called with: intent = [" + intent + "]");
|
public void onVideoPlayPauseRepeat() {
|
||||||
if (!intent.getStringExtra(VIDEO_URL).equals(videoUrl)) return;
|
if (DEBUG) Log.d(TAG, "onVideoPlayPauseRepeat() called");
|
||||||
videoThumbnail = ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail;
|
if (qualityChanged) {
|
||||||
|
setVideoStartPos(0);
|
||||||
|
play(true);
|
||||||
|
} else super.onVideoPlayPauseRepeat();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onVideoPlayPause() {
|
@Override
|
||||||
if (DEBUG) Log.d(TAG, "onVideoPlayPause() called");
|
public void onThumbnailReceived(Bitmap thumbnail) {
|
||||||
if (currentState == STATE_COMPLETED) {
|
super.onThumbnailReceived(thumbnail);
|
||||||
changeState(STATE_LOADING);
|
if (thumbnail != null) endScreen.setImageBitmap(thumbnail);
|
||||||
if (qualityChanged) playVideo(getSelectedVideoStream(), true);
|
|
||||||
simpleExoPlayer.seekTo(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
simpleExoPlayer.setPlayWhenReady(!isPlaying());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract void onFullScreenButtonClicked();
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onFastRewind() {
|
public void onFastRewind() {
|
||||||
if (DEBUG) Log.d(TAG, "onFastRewind() called");
|
super.onFastRewind();
|
||||||
seekBy(-FAST_FORWARD_REWIND_AMOUNT);
|
|
||||||
showAndAnimateControl(R.drawable.ic_action_av_fast_rewind, true);
|
showAndAnimateControl(R.drawable.ic_action_av_fast_rewind, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onFastForward() {
|
public void onFastForward() {
|
||||||
if (DEBUG) Log.d(TAG, "onFastForward() called");
|
super.onFastForward();
|
||||||
seekBy(FAST_FORWARD_REWIND_AMOUNT);
|
|
||||||
showAndAnimateControl(R.drawable.ic_action_av_fast_forward, true);
|
showAndAnimateControl(R.drawable.ic_action_av_fast_forward, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -651,10 +419,10 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||||
if (DEBUG) Log.d(TAG, "onMenuItemClick() called with: menuItem = [" + menuItem + "], menuItem.getItemId = [" + menuItem.getItemId() + "]");
|
if (DEBUG) Log.d(TAG, "onMenuItemClick() called with: menuItem = [" + menuItem + "], menuItem.getItemId = [" + menuItem.getItemId() + "]");
|
||||||
if (selectedIndexStream == menuItem.getItemId()) return true;
|
if (selectedIndexStream == menuItem.getItemId()) return true;
|
||||||
setVideoStartPos((int) getPlayer().getCurrentPosition());
|
setVideoStartPos((int) simpleExoPlayer.getCurrentPosition());
|
||||||
|
|
||||||
selectedIndexStream = menuItem.getItemId();
|
selectedIndexStream = menuItem.getItemId();
|
||||||
if (!(getCurrentState() == STATE_COMPLETED)) playVideo(getSelectedVideoStream(), wasPlaying);
|
if (!(getCurrentState() == STATE_COMPLETED)) play(wasPlaying);
|
||||||
else qualityChanged = true;
|
else qualityChanged = true;
|
||||||
|
|
||||||
qualityTextView.setText(menuItem.getTitle());
|
qualityTextView.setText(menuItem.getTitle());
|
||||||
|
@ -671,8 +439,6 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
qualityTextView.setText(getSelectedVideoStream().resolution);
|
qualityTextView.setText(getSelectedVideoStream().resolution);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void onFullScreenButtonClicked();
|
|
||||||
|
|
||||||
public void onQualitySelectorClicked() {
|
public void onQualitySelectorClicked() {
|
||||||
if (DEBUG) Log.d(TAG, "onQualitySelectorClicked() called");
|
if (DEBUG) Log.d(TAG, "onQualitySelectorClicked() called");
|
||||||
qualityPopupMenu.show();
|
qualityPopupMenu.show();
|
||||||
|
@ -684,18 +450,6 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
wasPlaying = isPlaying();
|
wasPlaying = isPlaying();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRepeatClicked() {
|
|
||||||
if (DEBUG) Log.d(TAG, "onRepeatClicked() called");
|
|
||||||
// TODO: implement repeat all when playlist is implemented
|
|
||||||
|
|
||||||
// Switch the modes between DISABLED and REPEAT_ONE, till playlist is implemented
|
|
||||||
setCurrentRepeatMode(getCurrentRepeatMode() == RepeatMode.REPEAT_DISABLED ?
|
|
||||||
RepeatMode.REPEAT_ONE :
|
|
||||||
RepeatMode.REPEAT_DISABLED);
|
|
||||||
|
|
||||||
if (DEBUG) Log.d(TAG, "onRepeatClicked() currentRepeatMode = " + getCurrentRepeatMode().name());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// SeekBar Listener
|
// SeekBar Listener
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -737,21 +491,6 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
// Utils
|
// Utils
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private static final StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
private static final Formatter formatter = new Formatter(stringBuilder, Locale.getDefault());
|
|
||||||
|
|
||||||
public String getTimeString(int milliSeconds) {
|
|
||||||
long seconds = (milliSeconds % 60000L) / 1000L;
|
|
||||||
long minutes = (milliSeconds % 3600000L) / 60000L;
|
|
||||||
long hours = (milliSeconds % 86400000L) / 3600000L;
|
|
||||||
long days = (milliSeconds % (86400000L * 7L)) / 86400000L;
|
|
||||||
|
|
||||||
stringBuilder.setLength(0);
|
|
||||||
return days > 0 ? formatter.format("%d:%02d:%02d:%02d", days, hours, minutes, seconds).toString()
|
|
||||||
: hours > 0 ? formatter.format("%d:%02d:%02d", hours, minutes, seconds).toString()
|
|
||||||
: formatter.format("%02d:%02d", minutes, seconds).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isControlsVisible() {
|
public boolean isControlsVisible() {
|
||||||
return controlsRoot != null && controlsRoot.getVisibility() == View.VISIBLE;
|
return controlsRoot != null && controlsRoot.getVisibility() == View.VISIBLE;
|
||||||
}
|
}
|
||||||
|
@ -908,62 +647,14 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void seekBy(int milliSeconds) {
|
|
||||||
if (DEBUG) Log.d(TAG, "seekBy() called with: milliSeconds = [" + milliSeconds + "]");
|
|
||||||
if (simpleExoPlayer == null) return;
|
|
||||||
int progress = (int) (simpleExoPlayer.getCurrentPosition() + milliSeconds);
|
|
||||||
simpleExoPlayer.seekTo(progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isPlaying() {
|
|
||||||
return simpleExoPlayer.getPlaybackState() == ExoPlayer.STATE_READY && simpleExoPlayer.getPlayWhenReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isQualityMenuVisible() {
|
public boolean isQualityMenuVisible() {
|
||||||
return isQualityPopupMenuVisible;
|
return isQualityPopupMenuVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startProgressLoop() {
|
|
||||||
progressLoop.removeCallbacksAndMessages(null);
|
|
||||||
isProgressLoopRunning.set(true);
|
|
||||||
progressLoop.post(progressUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopProgressLoop() {
|
|
||||||
isProgressLoopRunning.set(false);
|
|
||||||
progressLoop.removeCallbacksAndMessages(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tryDeleteCacheFiles(Context context) {
|
|
||||||
File cacheDir = new File(context.getExternalCacheDir(), CACHE_FOLDER_NAME);
|
|
||||||
|
|
||||||
if (cacheDir.exists()) {
|
|
||||||
try {
|
|
||||||
if (cacheDir.isDirectory()) {
|
|
||||||
for (File file : cacheDir.listFiles()) {
|
|
||||||
try {
|
|
||||||
if (DEBUG) Log.d(TAG, "tryDeleteCacheFiles: " + file.getAbsolutePath() + " deleted = " + file.delete());
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Getters and Setters
|
// Getters and Setters
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public SimpleExoPlayer getPlayer() {
|
|
||||||
return simpleExoPlayer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SharedPreferences getSharedPreferences() {
|
|
||||||
return sharedPreferences;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AspectRatioFrameLayout getAspectRatioFrameLayout() {
|
public AspectRatioFrameLayout getAspectRatioFrameLayout() {
|
||||||
return aspectRatioFrameLayout;
|
return aspectRatioFrameLayout;
|
||||||
}
|
}
|
||||||
|
@ -972,22 +663,10 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
return surfaceView;
|
return surfaceView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RepeatMode getCurrentRepeatMode() {
|
|
||||||
return currentRepeatMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCurrentRepeatMode(RepeatMode mode) {
|
|
||||||
currentRepeatMode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean wasPlaying() {
|
public boolean wasPlaying() {
|
||||||
return wasPlaying;
|
return wasPlaying;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCurrentState() {
|
|
||||||
return currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VideoStream getSelectedVideoStream() {
|
public VideoStream getSelectedVideoStream() {
|
||||||
return videoStreamsList.get(selectedIndexStream);
|
return videoStreamsList.get(selectedIndexStream);
|
||||||
}
|
}
|
||||||
|
@ -1000,46 +679,6 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
return qualityPopupMenuGroupId;
|
return qualityPopupMenuGroupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getVideoUrl() {
|
|
||||||
return videoUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVideoUrl(String videoUrl) {
|
|
||||||
this.videoUrl = videoUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getVideoStartPos() {
|
|
||||||
return videoStartPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVideoStartPos(int videoStartPos) {
|
|
||||||
this.videoStartPos = videoStartPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getVideoTitle() {
|
|
||||||
return videoTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVideoTitle(String videoTitle) {
|
|
||||||
this.videoTitle = videoTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bitmap getVideoThumbnail() {
|
|
||||||
return videoThumbnail;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVideoThumbnail(Bitmap videoThumbnail) {
|
|
||||||
this.videoThumbnail = videoThumbnail;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getChannelName() {
|
|
||||||
return channelName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChannelName(String channelName) {
|
|
||||||
this.channelName = channelName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSelectedStreamIndex() {
|
public int getSelectedStreamIndex() {
|
||||||
return selectedIndexStream;
|
return selectedIndexStream;
|
||||||
}
|
}
|
||||||
|
@ -1135,4 +774,5 @@ public abstract class AbstractPlayer implements StateInterface, SeekBar.OnSeekBa
|
||||||
public TextView getCurrentDisplaySeek() {
|
public TextView getCurrentDisplaySeek() {
|
||||||
return currentDisplaySeek;
|
return currentDisplaySeek;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue