-Changed quality resolution to persist across player.

-Updated ExoPlayer to 2.5.4.
-Expanded button size in main video player play queue.
-Removed Quality event.
-Extracted player error strings to xml.
This commit is contained in:
John Zhen Mo 2017-10-24 21:47:14 -07:00
parent d0e626c6ee
commit 0806344ffb
19 changed files with 210 additions and 142 deletions

View File

@ -66,7 +66,7 @@ dependencies {
compile 'de.hdodenhof:circleimageview:2.1.0'
compile 'com.github.nirhart:parallaxscroll:1.0'
compile 'com.nononsenseapps:filepicker:3.0.1'
compile 'com.google.android.exoplayer:exoplayer:r2.5.3'
compile 'com.google.android.exoplayer:exoplayer:r2.5.4'
debugCompile 'com.facebook.stetho:stetho:1.5.0'
debugCompile 'com.facebook.stetho:stetho-urlconnection:1.5.0'

View File

@ -788,14 +788,14 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
((HistoryListener) activity).onVideoPlayed(currentInfo, getSelectedVideoStream());
}
final PlayQueue playQueue = new SinglePlayQueue(currentInfo, actionBarHandler.getSelectedVideoStream());
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
final Intent intent;
if (append) {
Toast.makeText(activity, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
intent = NavigationHelper.getPlayerEnqueueIntent(activity, PopupVideoPlayer.class, playQueue);
} else {
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
intent = NavigationHelper.getPlayerIntent(activity, PopupVideoPlayer.class, playQueue);
intent = NavigationHelper.getPlayerIntent(activity, PopupVideoPlayer.class, playQueue, getSelectedVideoStream().resolution);
}
activity.startService(intent);
}
@ -866,8 +866,8 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|| (Build.VERSION.SDK_INT < 16);
if (!useOldPlayer) {
// ExoPlayer
final PlayQueue playQueue = new SinglePlayQueue(currentInfo, actionBarHandler.getSelectedVideoStream());
mIntent = NavigationHelper.getPlayerIntent(activity, MainVideoPlayer.class, playQueue);
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
mIntent = NavigationHelper.getPlayerIntent(activity, MainVideoPlayer.class, playQueue, getSelectedVideoStream().resolution);
} else {
// Internal Player
mIntent = new Intent(activity, PlayVideoActivity.class)

View File

@ -388,13 +388,23 @@ public final class BackgroundPlayer extends Service {
@Override
public void onRecoverableError(Exception exception) {
exception.printStackTrace();
Toast.makeText(context, "Failed to play this audio", Toast.LENGTH_SHORT).show();
if (errorToast == null) {
errorToast = Toast.makeText(context, R.string.player_audio_failure, Toast.LENGTH_SHORT);
errorToast.show();
}
}
@Override
public void onUnrecoverableError(Exception exception) {
exception.printStackTrace();
Toast.makeText(context, "Unexpected error occurred", Toast.LENGTH_SHORT).show();
if (errorToast != null) {
errorToast.cancel();
}
errorToast = Toast.makeText(context, R.string.player_unexpected_failure, Toast.LENGTH_SHORT);
errorToast.show();
shutdown();
}

View File

@ -36,6 +36,7 @@ import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl;
@ -125,6 +126,7 @@ public abstract class BasePlayer implements Player.EventListener,
public static final String REPEAT_MODE = "repeat_mode";
public static final String PLAYBACK_PITCH = "playback_pitch";
public static final String PLAYBACK_SPEED = "playback_speed";
public static final String PLAYBACK_QUALITY = "playback_quality";
public static final String PLAY_QUEUE = "play_queue";
public static final String APPEND_ONLY = "append_only";
@ -141,6 +143,7 @@ public abstract class BasePlayer implements Player.EventListener,
protected StreamInfo currentInfo;
protected PlayQueueItem currentItem;
protected Toast errorToast;
/*//////////////////////////////////////////////////////////////////////////
// Player
//////////////////////////////////////////////////////////////////////////*/
@ -659,8 +662,8 @@ public abstract class BasePlayer implements Player.EventListener,
* {@link ExoPlaybackException#TYPE_SOURCE TYPE_SOURCE}: <br><br>
* If the current {@link com.google.android.exoplayer2.Timeline.Window window} has
* duration and position greater than 0, then we know the current window is working correctly
* and the error is produced by transitioning into a bad window, therefore we simply increment
* the current index. Otherwise, we report an error to the play queue.
* and the error is produced by transitioning into a bad window, therefore we report an error
* to the play queue based on if the current error can be skipped.
*
* This is done because ExoPlayer reports the source exceptions before window is
* transitioned on seamless playback.
@ -668,27 +671,33 @@ public abstract class BasePlayer implements Player.EventListener,
* Because player error causes ExoPlayer to go back to {@link Player#STATE_IDLE STATE_IDLE},
* we reset and prepare the media source again to resume playback.<br><br>
*
* {@link ExoPlaybackException#TYPE_RENDERER TYPE_RENDERER} and
* {@link ExoPlaybackException#TYPE_UNEXPECTED TYPE_UNEXPECTED}: <br><br>
* If renderer failed or unexpected exceptions occurred, treat the error as unrecoverable.
* If a runtime error occurred, then we can try to recover it by restarting the playback
* after setting the timestamp recovery.
*
* {@link ExoPlaybackException#TYPE_RENDERER TYPE_RENDERER}: <br><br>
* If the renderer failed, treat the error as unrecoverable.
*
* @see Player.EventListener#onPlayerError(ExoPlaybackException)
* */
@Override
public void onPlayerError(ExoPlaybackException error) {
if (DEBUG) Log.d(TAG, "onPlayerError() called with: error = [" + error + "]");
if (errorToast != null) {
errorToast.cancel();
errorToast = null;
}
switch (error.type) {
case ExoPlaybackException.TYPE_SOURCE:
if (simpleExoPlayer.getDuration() < 0 || simpleExoPlayer.getCurrentPosition() < 0) {
playQueue.error();
onRecoverableError(error);
} else {
playQueue.offsetIndex(+1);
}
playbackManager.reset();
playbackManager.load();
final boolean skippable = simpleExoPlayer.getDuration() >= 0 && simpleExoPlayer.getCurrentPosition() >= 0;
playQueue.error(skippable);
onRecoverableError(error);
break;
case ExoPlaybackException.TYPE_UNEXPECTED:
onRecoverableError(error);
setRecovery();
reload();
break;
default:
onUnrecoverableError(error);
@ -883,6 +892,13 @@ public abstract class BasePlayer implements Player.EventListener,
return pitchFormatter.format(pitch);
}
protected void reload() {
if (playbackManager != null) {
playbackManager.reset();
playbackManager.load();
}
}
protected void startProgressLoop() {
if (progressUpdateReactor != null) progressUpdateReactor.dispose();
progressUpdateReactor = getProgressReactor();

View File

@ -344,7 +344,8 @@ public final class MainVideoPlayer extends Activity {
this.getPlayQueue(),
this.simpleExoPlayer.getRepeatMode(),
this.getPlaybackSpeed(),
this.getPlaybackPitch()
this.getPlaybackPitch(),
this.getPlaybackQuality()
);
context.startService(intent);
destroyPlayer();
@ -432,13 +433,23 @@ public final class MainVideoPlayer extends Activity {
@Override
public void onRecoverableError(Exception exception) {
exception.printStackTrace();
Toast.makeText(context, "Failed to play this video", Toast.LENGTH_SHORT).show();
if (errorToast == null) {
errorToast = Toast.makeText(context, R.string.player_video_failure, Toast.LENGTH_SHORT);
errorToast.show();
}
}
@Override
public void onUnrecoverableError(Exception exception) {
exception.printStackTrace();
Toast.makeText(context, "Unexpected error occurred", Toast.LENGTH_SHORT).show();
if (errorToast != null) {
errorToast.cancel();
}
errorToast = Toast.makeText(context, R.string.player_unexpected_failure, Toast.LENGTH_SHORT);
errorToast.show();
shutdown();
}
@ -447,6 +458,12 @@ public final class MainVideoPlayer extends Activity {
return ListHelper.getDefaultResolutionIndex(context, sortedVideos);
}
@Override
protected int getOverrideResolutionIndex(final List<VideoStream> sortedVideos,
final String playbackQuality) {
return ListHelper.getDefaultResolutionIndex(context, sortedVideos, playbackQuality);
}
/*//////////////////////////////////////////////////////////////////////////
// States
//////////////////////////////////////////////////////////////////////////*/

View File

@ -441,7 +441,8 @@ public final class PopupVideoPlayer extends Service {
this.getPlayQueue(),
this.simpleExoPlayer.getRepeatMode(),
this.getPlaybackSpeed(),
this.getPlaybackPitch()
this.getPlaybackPitch(),
this.getPlaybackQuality()
);
if (!isStartedFromNewPipe()) intent.putExtra(VideoPlayer.STARTED_FROM_NEWPIPE, false);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@ -468,13 +469,23 @@ public final class PopupVideoPlayer extends Service {
@Override
public void onRecoverableError(Exception exception) {
exception.printStackTrace();
Toast.makeText(context, "Failed to play this video", Toast.LENGTH_SHORT).show();
if (errorToast == null) {
errorToast = Toast.makeText(context, R.string.player_video_failure, Toast.LENGTH_SHORT);
errorToast.show();
}
}
@Override
public void onUnrecoverableError(Exception exception) {
exception.printStackTrace();
Toast.makeText(context, "Unexpected error occurred", Toast.LENGTH_SHORT).show();
if (errorToast != null) {
errorToast.cancel();
}
errorToast = Toast.makeText(context, R.string.player_unexpected_failure, Toast.LENGTH_SHORT);
errorToast.show();
shutdown();
}
@ -503,6 +514,12 @@ public final class PopupVideoPlayer extends Service {
return ListHelper.getPopupDefaultResolutionIndex(context, sortedVideos);
}
@Override
protected int getOverrideResolutionIndex(final List<VideoStream> sortedVideos,
final String playbackQuality) {
return ListHelper.getPopupDefaultResolutionIndex(context, sortedVideos, playbackQuality);
}
/*//////////////////////////////////////////////////////////////////////////
// Activity Event Listener
//////////////////////////////////////////////////////////////////////////*/

View File

@ -25,6 +25,7 @@ import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
@ -48,6 +49,7 @@ import android.widget.TextView;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
@ -82,15 +84,17 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
public static final String STARTED_FROM_NEWPIPE = "started_from_newpipe";
private ArrayList<VideoStream> availableStreams;
private int selectedStreamIndex;
/*//////////////////////////////////////////////////////////////////////////
// Player
//////////////////////////////////////////////////////////////////////////*/
public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000; // 2 Seconds
private ArrayList<VideoStream> availableStreams;
private int selectedStreamIndex;
protected String playbackQuality;
private boolean startedFromNewPipe = true;
protected boolean wasPlaying = false;
@ -125,7 +129,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
private Handler controlsVisibilityHandler = new Handler();
private boolean isSomePopupMenuVisible = false;
private boolean qualityChanged = false;
private int qualityPopupMenuGroupId = 69;
private PopupMenu qualityPopupMenu;
@ -197,6 +200,17 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
}
}
@Override
public void handleIntent(final Intent intent) {
if (intent == null) return;
if (intent.hasExtra(PLAYBACK_QUALITY)) {
setPlaybackQuality(intent.getStringExtra(PLAYBACK_QUALITY));
}
super.handleIntent(intent);
}
/*//////////////////////////////////////////////////////////////////////////
// UI Builders
//////////////////////////////////////////////////////////////////////////*/
@ -232,6 +246,8 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
protected abstract int getDefaultResolutionIndex(final List<VideoStream> sortedVideos);
protected abstract int getOverrideResolutionIndex(final List<VideoStream> sortedVideos, final String playbackQuality);
@Override
public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) {
super.sync(item, info);
@ -241,11 +257,10 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
if (info != null) {
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false);
availableStreams = new ArrayList<>(videos);
final int qualityIndex = item.getQualityIndex();
if (qualityIndex == PlayQueueItem.DEFAULT_QUALITY) {
if (playbackQuality == null) {
selectedStreamIndex = getDefaultResolutionIndex(videos);
} else {
selectedStreamIndex = qualityIndex;
selectedStreamIndex = getOverrideResolutionIndex(videos, getPlaybackQuality());
}
buildQualityMenu();
@ -255,17 +270,17 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
}
}
@Override
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false);
final int sortedStreamsIndex = item.getQualityIndex();
if (videos.isEmpty() || sortedStreamsIndex >= videos.size()) return null;
final VideoStream video;
if (sortedStreamsIndex == PlayQueueItem.DEFAULT_QUALITY) {
if (playbackQuality == null) {
final int index = getDefaultResolutionIndex(videos);
video = videos.get(index);
} else {
video = videos.get(sortedStreamsIndex);
final int index = getOverrideResolutionIndex(videos, getPlaybackQuality());
video = videos.get(index);
}
final MediaSource streamSource = buildMediaSource(video.url, MediaFormat.getSuffixById(video.format));
@ -455,10 +470,15 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
Log.d(TAG, "onMenuItemClick() called with: menuItem = [" + menuItem + "], menuItem.getItemId = [" + menuItem.getItemId() + "]");
if (qualityPopupMenuGroupId == menuItem.getGroupId()) {
if (selectedStreamIndex == menuItem.getItemId()) return true;
final int menuItemIndex = menuItem.getItemId();
if (selectedStreamIndex == menuItemIndex ||
availableStreams == null || availableStreams.size() <= menuItemIndex) return true;
final String oldResolution = getPlaybackQuality();
final String newResolution = availableStreams.get(menuItemIndex).resolution;
setRecovery();
playQueue.setQuality(playQueue.getIndex(), menuItem.getItemId());
setPlaybackQuality(newResolution);
reload();
qualityTextView.setText(menuItem.getTitle());
return true;
@ -489,8 +509,9 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
isSomePopupMenuVisible = true;
showControls(300);
VideoStream videoStream = getSelectedVideoStream();
qualityTextView.setText(MediaFormat.getNameById(videoStream.format) + " " + videoStream.resolution);
final VideoStream videoStream = getSelectedVideoStream();
final String qualityText = MediaFormat.getNameById(videoStream.format) + " " + videoStream.resolution;
qualityTextView.setText(qualityText);
wasPlaying = simpleExoPlayer.getPlayWhenReady();
}
@ -648,6 +669,14 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
// Getters and Setters
//////////////////////////////////////////////////////////////////////////*/
public void setPlaybackQuality(final String quality) {
this.playbackQuality = quality;
}
public String getPlaybackQuality() {
return playbackQuality;
}
public AspectRatioFrameLayout getAspectRatioFrameLayout() {
return aspectRatioFrameLayout;
}

View File

@ -158,8 +158,8 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
// why no pattern matching in Java =(
switch (event.type()) {
case INIT:
case QUALITY:
case REORDER:
case ERROR:
reset();
break;
case APPEND:
@ -177,7 +177,6 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
final MoveEvent moveEvent = (MoveEvent) event;
move(moveEvent.getFromIndex(), moveEvent.getToIndex());
break;
case ERROR:
case RECOVERY:
default:
break;

View File

@ -14,7 +14,6 @@ import org.schabi.newpipe.playlist.events.RecoveryEvent;
import org.schabi.newpipe.playlist.events.RemoveEvent;
import org.schabi.newpipe.playlist.events.ReorderEvent;
import org.schabi.newpipe.playlist.events.SelectEvent;
import org.schabi.newpipe.playlist.events.QualityEvent;
import java.io.Serializable;
import java.util.ArrayList;
@ -247,15 +246,22 @@ public abstract class PlayQueue implements Serializable {
}
/**
* Report an exception for the item at the current index in order to remove it.
* Report an exception for the item at the current index in order and the course of action:
* if the error can be skipped or the current item should be removed.
*
* This is done as a separate event as the underlying manager may have
* different implementation regarding exceptions.
* */
public synchronized void error() {
public synchronized void error(final boolean skippable) {
final int index = getIndex();
removeInternal(index);
broadcast(new ErrorEvent(index));
if (skippable) {
queueIndex.incrementAndGet();
} else {
removeInternal(index);
}
broadcast(new ErrorEvent(index, skippable));
}
private synchronized void removeInternal(final int index) {
@ -301,21 +307,6 @@ public abstract class PlayQueue implements Serializable {
broadcast(new MoveEvent(source, target));
}
/**
* Updates the quality index at the given item index.
*
* Broadcasts an update event, signalling to all recipients that they should reset.
* */
public synchronized void setQuality(final int queueIndex, final int qualityIndex) {
if (queueIndex < 0 || queueIndex >= streams.size()) return;
final PlayQueueItem item = streams.get(queueIndex);
final int oldQualityIndex = item.getQualityIndex();
item.setQualityIndex(qualityIndex);
broadcast(new QualityEvent(queueIndex, oldQualityIndex, qualityIndex));
}
/**
* Sets the recovery record of the item at the index.
*

View File

@ -102,7 +102,6 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
private void onPlayQueueChanged(final PlayQueueEvent message) {
switch (message.type()) {
case RECOVERY:
case QUALITY:
// Do nothing.
break;
case SELECT:
@ -116,12 +115,14 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
break;
case ERROR:
final ErrorEvent errorEvent = (ErrorEvent) message;
notifyItemRangeRemoved(errorEvent.index(), 1);
if (!errorEvent.isSkippable()) {
notifyItemRemoved(errorEvent.index());
}
notifyItemChanged(errorEvent.index());
break;
case REMOVE:
final RemoveEvent removeEvent = (RemoveEvent) message;
notifyItemRangeRemoved(removeEvent.index(), 1);
notifyItemRemoved(removeEvent.index());
notifyItemChanged(removeEvent.index());
break;
case MOVE:

View File

@ -15,7 +15,6 @@ import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
public class PlayQueueItem implements Serializable {
final public static int DEFAULT_QUALITY = Integer.MIN_VALUE;
final public static long RECOVERY_UNSET = Long.MIN_VALUE;
final private String title;
@ -25,7 +24,6 @@ public class PlayQueueItem implements Serializable {
final private String thumbnailUrl;
final private String uploader;
private int qualityIndex;
private long recoveryPosition;
private Throwable error;
@ -36,11 +34,6 @@ public class PlayQueueItem implements Serializable {
this.stream = Single.just(info);
}
PlayQueueItem(@NonNull final StreamInfo info, final int qualityIndex) {
this(info);
this.qualityIndex = qualityIndex;
}
PlayQueueItem(@NonNull final StreamInfoItem item) {
this(item.name, item.url, item.service_id, item.duration, item.thumbnail_url, item.uploader_name);
}
@ -54,7 +47,6 @@ public class PlayQueueItem implements Serializable {
this.thumbnailUrl = thumbnailUrl;
this.uploader = uploader;
this.qualityIndex = DEFAULT_QUALITY;
this.recoveryPosition = RECOVERY_UNSET;
}
@ -86,10 +78,6 @@ public class PlayQueueItem implements Serializable {
return uploader;
}
public int getQualityIndex() {
return qualityIndex;
}
public long getRecoveryPosition() {
return recoveryPosition;
}
@ -123,10 +111,6 @@ public class PlayQueueItem implements Serializable {
// Item States, keep external access out
////////////////////////////////////////////////////////////////////////////
/*package-private*/ void setQualityIndex(final int qualityIndex) {
this.qualityIndex = qualityIndex;
}
/*package-private*/ void setRecoveryPosition(final long recoveryPosition) {
this.recoveryPosition = recoveryPosition;
}

View File

@ -9,10 +9,6 @@ public final class SinglePlayQueue extends PlayQueue {
super(0, Collections.singletonList(new PlayQueueItem(info)));
}
public SinglePlayQueue(final StreamInfo info, final int qualityIndex) {
super(0, Collections.singletonList(new PlayQueueItem(info, qualityIndex)));
}
@Override
public boolean isComplete() {
return true;

View File

@ -3,17 +3,23 @@ package org.schabi.newpipe.playlist.events;
public class ErrorEvent implements PlayQueueEvent {
final private int index;
final private boolean skippable;
@Override
public PlayQueueEventType type() {
return PlayQueueEventType.ERROR;
}
public ErrorEvent(final int index) {
public ErrorEvent(final int index, final boolean skippable) {
this.index = index;
this.skippable = skippable;
}
public int index() {
return index;
}
public boolean isSkippable() {
return skippable;
}
}

View File

@ -18,9 +18,6 @@ public enum PlayQueueEventType {
// sent when queue is shuffled
REORDER,
// sent when quality index is set on a stream
QUALITY,
// sent when recovery record is set on a stream
RECOVERY,

View File

@ -1,31 +0,0 @@
package org.schabi.newpipe.playlist.events;
public class QualityEvent implements PlayQueueEvent {
final private int streamIndex;
final private int oldQualityIndex;
final private int newQualityIndex;
@Override
public PlayQueueEventType type() {
return PlayQueueEventType.QUALITY;
}
public QualityEvent(final int streamIndex, final int oldQualityIndex, final int newQualityIndex) {
this.streamIndex = streamIndex;
this.oldQualityIndex = oldQualityIndex;
this.newQualityIndex = newQualityIndex;
}
public int getStreamIndex() {
return streamIndex;
}
public int getOldQualityIndex() {
return oldQualityIndex;
}
public int getNewQualityIndex() {
return newQualityIndex;
}
}

View File

@ -56,6 +56,13 @@ public final class ListHelper {
if (defaultPreferences == null) return 0;
String defaultResolution = defaultPreferences.getString(context.getString(R.string.default_resolution_key), context.getString(R.string.default_resolution_value));
return getDefaultResolutionIndex(context, videoStreams, defaultResolution);
}
/**
* @see #getDefaultResolutionIndex(String, String, MediaFormat, List)
*/
public static int getDefaultResolutionIndex(Context context, List<VideoStream> videoStreams, String defaultResolution) {
return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams);
}
@ -67,6 +74,13 @@ public final class ListHelper {
if (defaultPreferences == null) return 0;
String defaultResolution = defaultPreferences.getString(context.getString(R.string.default_popup_resolution_key), context.getString(R.string.default_popup_resolution_value));
return getPopupDefaultResolutionIndex(context, videoStreams, defaultResolution);
}
/**
* @see #getDefaultResolutionIndex(String, String, MediaFormat, List)
*/
public static int getPopupDefaultResolutionIndex(Context context, List<VideoStream> videoStreams, String defaultResolution) {
return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams);
}

View File

@ -39,21 +39,19 @@ public class NavigationHelper {
//////////////////////////////////////////////////////////////////////////*/
public static Intent getPlayerIntent(final Context context,
final Class targetClazz,
final PlayQueue playQueue) {
return new Intent(context, targetClazz)
final PlayQueue playQueue,
final String quality) {
Intent intent = new Intent(context, targetClazz)
.putExtra(VideoPlayer.PLAY_QUEUE, playQueue);
if (quality != null) intent.putExtra(VideoPlayer.PLAYBACK_QUALITY, quality);
return intent;
}
public static Intent getPlayerIntent(final Context context,
final Class targetClazz,
final PlayQueue playQueue,
final int repeatMode,
final float playbackSpeed,
final float playbackPitch) {
return getPlayerIntent(context, targetClazz, playQueue)
.putExtra(BasePlayer.REPEAT_MODE, repeatMode)
.putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed)
.putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch);
final PlayQueue playQueue) {
return getPlayerIntent(context, targetClazz, playQueue, null);
}
public static Intent getPlayerEnqueueIntent(final Context context,
@ -63,6 +61,19 @@ public class NavigationHelper {
.putExtra(BasePlayer.APPEND_ONLY, true);
}
public static Intent getPlayerIntent(final Context context,
final Class targetClazz,
final PlayQueue playQueue,
final int repeatMode,
final float playbackSpeed,
final float playbackPitch,
final String playbackQuality) {
return getPlayerIntent(context, targetClazz, playQueue, playbackQuality)
.putExtra(BasePlayer.REPEAT_MODE, repeatMode)
.putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed)
.putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch);
}
/*//////////////////////////////////////////////////////////////////////////
// Through FragmentManager
//////////////////////////////////////////////////////////////////////////*/

View File

@ -57,13 +57,14 @@
<ImageButton
android:id="@+id/playQueueClose"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="40dp"
android:layout_marginEnd="40dp"
android:padding="10dp"
android:clickable="true"
android:focusable="true"
android:scaleType="fitXY"
@ -73,13 +74,14 @@
<ImageButton
android:id="@+id/repeatButton"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginLeft="40dp"
android:layout_marginStart="40dp"
android:padding="10dp"
android:clickable="true"
android:focusable="true"
android:scaleType="fitXY"
@ -89,12 +91,11 @@
<ImageButton
android:id="@+id/shuffleButton"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/repeatButton"
android:layout_marginLeft="15dp"
android:layout_marginStart="15dp"
android:padding="10dp"
android:clickable="true"
android:focusable="true"
android:scaleType="fitXY"
@ -301,7 +302,9 @@
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
android:background="#00000000"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="fitXY"
android:src="@drawable/ic_pause_white"
tools:ignore="ContentDescription"/>
@ -315,7 +318,9 @@
android:layout_centerInParent="true"
android:layout_toLeftOf="@id/playPauseButton"
android:layout_toStartOf="@id/playPauseButton"
android:background="#00000000"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="fitXY"
android:src="@drawable/exo_controls_previous"
tools:ignore="ContentDescription"/>
@ -329,7 +334,9 @@
android:layout_centerInParent="true"
android:layout_toRightOf="@id/playPauseButton"
android:layout_toEndOf="@id/playPauseButton"
android:background="#00000000"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="fitXY"
android:src="@drawable/exo_controls_next"
tools:ignore="ContentDescription"/>

View File

@ -136,6 +136,10 @@
<string name="could_not_get_stream">Could not get any stream</string>
<string name="could_not_load_image">Could not load image</string>
<string name="app_ui_crash">App/UI crashed</string>
<string name="player_video_failure">Failed to play this video</string>
<string name="player_audio_failure">Failed to play this audio</string>
<string name="player_unexpected_failure">Unexpected player error occurred</string>
<!-- error activity -->
<string name="sorry_string">Sorry, that should not have happened.</string>
<string name="guru_meditation" translatable="false">Guru Meditation.</string>