From b597774bb94bb927e8fa89f18f79c579aec3c06c Mon Sep 17 00:00:00 2001 From: John Zhen M Date: Thu, 12 Oct 2017 20:47:12 -0700 Subject: [PATCH] -Enabled play queue control in main video player. -Fixed video players does not resolve to preferred quality on playlists. -Refactored resolution conversion to Localization. -Fixed video player quality menu building exception when stream info is not yet available. --- .../fragments/detail/VideoDetailFragment.java | 13 +- .../newpipe/player/MainVideoPlayer.java | 150 +++++++++++++++++- .../newpipe/player/PopupVideoPlayer.java | 12 ++ .../schabi/newpipe/player/VideoPlayer.java | 7 +- .../org/schabi/newpipe/util/Localization.java | 10 ++ .../main/res/layout/activity_main_player.xml | 48 +++++- 6 files changed, 223 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 1872383e7..024874d14 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -754,15 +754,6 @@ public class VideoDetailFragment extends BaseStateFragment implement } } - private static int resolutionOf(final String resolution) { - final String[] candidates = TextUtils.split(resolution, "p"); - if (candidates.length > 0 && TextUtils.isDigitsOnly(candidates[0])) { - return Integer.parseInt(candidates[0]); - } else { - return Integer.MAX_VALUE; - } - } - private void openPopupPlayer(final boolean append) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !PermissionHelper.checkSystemAlertWindowPermission(activity)) { Toast toast = Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG); @@ -785,7 +776,7 @@ public class VideoDetailFragment extends BaseStateFragment implement if (append) { intent = NavigationHelper.getPlayerIntent(activity, PopupVideoPlayer.class, playQueue, true); } else { - intent = NavigationHelper.getPlayerIntent(activity, PopupVideoPlayer.class, playQueue, resolutionOf(candidate.resolution)); + intent = NavigationHelper.getPlayerIntent(activity, PopupVideoPlayer.class, playQueue, Localization.resolutionOf(candidate.resolution)); } activity.startService(intent); } @@ -853,7 +844,7 @@ public class VideoDetailFragment extends BaseStateFragment implement // ExoPlayer final PlayQueue playQueue = new SinglePlayQueue(currentInfo); final VideoStream candidate = sortedStreamVideosList.get(actionBarHandler.getSelectedVideoStream()); - mIntent = NavigationHelper.getPlayerIntent(activity, MainVideoPlayer.class, playQueue, resolutionOf(candidate.resolution)); + mIntent = NavigationHelper.getPlayerIntent(activity, MainVideoPlayer.class, playQueue, Localization.resolutionOf(candidate.resolution)); } else { // Internal Player mIntent = new Intent(activity, PlayVideoActivity.class) diff --git a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java index 454eeed28..00b14d661 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java @@ -28,6 +28,9 @@ import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; @@ -35,6 +38,7 @@ import android.view.View; import android.view.WindowManager; import android.widget.ImageButton; import android.widget.PopupMenu; +import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; @@ -45,7 +49,10 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.playlist.PlayQueueItem; +import org.schabi.newpipe.playlist.PlayQueueItemBuilder; +import org.schabi.newpipe.playlist.PlayQueueItemHolder; import org.schabi.newpipe.util.AnimationUtils; +import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.ThemeHelper; @@ -159,6 +166,7 @@ public final class MainVideoPlayer extends Activity { private void showSystemUi() { if (DEBUG) Log.d(TAG, "showSystemUi() called"); + if (playerImpl != null && playerImpl.queueVisible) return; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE @@ -212,10 +220,18 @@ public final class MainVideoPlayer extends Activity { private TextView volumeTextView; private TextView brightnessTextView; private ImageButton repeatButton; + private ImageButton queueButton; private ImageButton screenRotationButton; private ImageButton playPauseButton; + private RelativeLayout queueLayout; + private ImageButton itemsListCloseButton; + private RecyclerView itemsList; + private ItemTouchHelper itemTouchHelper; + + private boolean queueVisible; + VideoPlayerImpl() { super("VideoPlayerImpl" + MainVideoPlayer.TAG, MainVideoPlayer.this); } @@ -228,6 +244,7 @@ public final class MainVideoPlayer extends Activity { this.volumeTextView = rootView.findViewById(R.id.volumeTextView); this.brightnessTextView = rootView.findViewById(R.id.brightnessTextView); this.repeatButton = rootView.findViewById(R.id.repeatButton); + this.queueButton = rootView.findViewById(R.id.queueButton); this.screenRotationButton = rootView.findViewById(R.id.screenRotationButton); this.playPauseButton = rootView.findViewById(R.id.playPauseButton); @@ -244,11 +261,22 @@ public final class MainVideoPlayer extends Activity { gestureDetector.setIsLongpressEnabled(false); getRootView().setOnTouchListener(listener); + queueButton.setOnClickListener(this); repeatButton.setOnClickListener(this); playPauseButton.setOnClickListener(this); screenRotationButton.setOnClickListener(this); } + @Override + public int getPreferredResolution() { + if (sharedPreferences == null || context == null) return Integer.MAX_VALUE; + + return Localization.resolutionOf(sharedPreferences.getString( + context.getString(R.string.default_resolution_key), + context.getString(R.string.default_resolution_value) + )); + } + /*////////////////////////////////////////////////////////////////////////// // ExoPlayer Video Listener //////////////////////////////////////////////////////////////////////////*/ @@ -310,9 +338,19 @@ public final class MainVideoPlayer extends Activity { @Override public void onClick(View v) { super.onClick(v); - if (v.getId() == repeatButton.getId()) onRepeatClicked(); - else if (v.getId() == playPauseButton.getId()) onVideoPlayPause(); - else if (v.getId() == screenRotationButton.getId()) onScreenRotationClicked(); + if (v.getId() == repeatButton.getId()) { + onRepeatClicked(); + + } else if (v.getId() == playPauseButton.getId()) { + onVideoPlayPause(); + + } else if (v.getId() == screenRotationButton.getId()) { + onScreenRotationClicked(); + + } else if (v.getId() == queueButton.getId()) { + onQueueClicked(); + return; + } if (getCurrentState() != STATE_COMPLETED) { getControlsVisibilityHandler().removeCallbacksAndMessages(null); @@ -327,6 +365,19 @@ public final class MainVideoPlayer extends Activity { } } + private void onQueueClicked() { + queueVisible = true; + buildQueue(); + hideSystemUi(); + getControlsRoot().setVisibility(View.INVISIBLE); + queueLayout.setVisibility(View.VISIBLE); + } + + private void onQueueClosed() { + queueLayout.setVisibility(View.GONE); + queueVisible = false; + } + private void onScreenRotationClicked() { if (DEBUG) Log.d(TAG, "onScreenRotationClicked() called"); toggleOrientation(); @@ -434,6 +485,20 @@ public final class MainVideoPlayer extends Activity { // Utils //////////////////////////////////////////////////////////////////////////*/ + @Override + public void showControlsThenHide() { + if (queueVisible) return; + + super.showControlsThenHide(); + } + + @Override + public void showControls(long duration) { + if (queueVisible) return; + + super.showControls(duration); + } + @Override public void hideControls(final long duration, long delay) { if (DEBUG) Log.d(TAG, "hideControls() called with: delay = [" + delay + "]"); @@ -451,6 +516,85 @@ public final class MainVideoPlayer extends Activity { }, delay); } + private void buildQueue() { + queueLayout = findViewById(R.id.play_queue_control); + + itemsListCloseButton = findViewById(R.id.play_queue_close_area); + + itemsList = findViewById(R.id.play_queue); + itemsList.setAdapter(playQueueAdapter); + itemsList.setClickable(true); + itemsList.setLongClickable(true); + + itemTouchHelper = new ItemTouchHelper(getItemTouchCallback()); + itemTouchHelper.attachToRecyclerView(itemsList); + + playQueueAdapter.setSelectedListener(getOnSelectedListener()); + + itemsListCloseButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + onQueueClosed(); + } + }); + } + + private ItemTouchHelper.SimpleCallback getItemTouchCallback() { + return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) { + @Override + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) { + if (source.getItemViewType() != target.getItemViewType()) { + return false; + } + + final int sourceIndex = source.getLayoutPosition(); + final int targetIndex = target.getLayoutPosition(); + playQueue.move(sourceIndex, targetIndex); + return true; + } + + @Override + public boolean isLongPressDragEnabled() { + return false; + } + + @Override + public boolean isItemViewSwipeEnabled() { + return false; + } + + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {} + }; + } + + private PlayQueueItemBuilder.OnSelectedListener getOnSelectedListener() { + return new PlayQueueItemBuilder.OnSelectedListener() { + @Override + public void selected(PlayQueueItem item, View view) { + final int index = playQueue.indexOf(item); + if (index == -1) return; + + if (playQueue.getIndex() == index) { + onRestart(); + } else { + playQueue.setIndex(index); + } + } + + @Override + public void held(PlayQueueItem item, View view) { + final int index = playQueue.indexOf(item); + if (index != -1) playQueue.remove(index); + } + + @Override + public void onStartDrag(PlayQueueItemHolder viewHolder) { + if (itemTouchHelper != null) itemTouchHelper.startDrag(viewHolder); + } + }; + } + /////////////////////////////////////////////////////////////////////////// // Getters /////////////////////////////////////////////////////////////////////////// diff --git a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java index 6adf0f8e3..f8f2f0486 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java @@ -53,6 +53,7 @@ import android.widget.Toast; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.MainActivity; @@ -73,6 +74,7 @@ import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.ExtractorHelper; +import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.ThemeHelper; @@ -405,6 +407,15 @@ public final class PopupVideoPlayer extends Service { resizingIndicator = rootView.findViewById(R.id.resizing_indicator); } + @Override + public int getPreferredResolution() { + if (sharedPreferences == null || context == null) return Integer.MAX_VALUE; + return Localization.resolutionOf(sharedPreferences.getString( + context.getString(R.string.default_popup_resolution_key), + context.getString(R.string.default_popup_resolution_value) + )); + } + @Override public void destroy() { super.destroy(); @@ -451,6 +462,7 @@ public final class PopupVideoPlayer extends Service { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } context.startActivity(intent); + playerImpl.stopActivityBinding(); destroyPlayer(); stopSelf(); } diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java index 422567d00..4c0982d72 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java @@ -222,13 +222,15 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. super.handleIntent(intent); if (intent == null) return; - final int resolutionTarget = intent.getIntExtra(MAX_RESOLUTION, Integer.MAX_VALUE); + final int resolutionTarget = intent.getIntExtra(MAX_RESOLUTION, getPreferredResolution()); trackSelector.setParameters( // Assume video is horizontal new DefaultTrackSelector.Parameters().withMaxVideoSize(Integer.MAX_VALUE, resolutionTarget) ); } + public abstract int getPreferredResolution(); + /*////////////////////////////////////////////////////////////////////////// // UI Builders //////////////////////////////////////////////////////////////////////////*/ @@ -246,7 +248,8 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. } private void buildQualityMenu() { - if (qualityPopupMenu == null || videoTrackGroups == null || selectedVideoTrackGroup == null || videoTrackGroups.length != availableStreams.size()) return; + if (qualityPopupMenu == null || videoTrackGroups == null || selectedVideoTrackGroup == null + || availableStreams == null || videoTrackGroups.length != availableStreams.size()) return; qualityPopupMenu.getMenu().removeGroup(qualityPopupMenuGroupId); trackGroupInfos = new ArrayList<>(); diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index b6ec3cd3a..1e864d5fd 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -6,6 +6,7 @@ import android.content.res.Resources; import android.preference.PreferenceManager; import android.support.annotation.PluralsRes; import android.support.annotation.StringRes; +import android.text.TextUtils; import org.schabi.newpipe.R; @@ -151,4 +152,13 @@ public class Localization { } return output; } + + public static int resolutionOf(final String resolution) { + final String[] candidates = TextUtils.split(resolution, "p"); + if (candidates.length > 0 && TextUtils.isDigitsOnly(candidates[0])) { + return Integer.parseInt(candidates[0]); + } else { + return Integer.MAX_VALUE; + } + } } diff --git a/app/src/main/res/layout/activity_main_player.xml b/app/src/main/res/layout/activity_main_player.xml index ba6520048..24a1fafc4 100644 --- a/app/src/main/res/layout/activity_main_player.xml +++ b/app/src/main/res/layout/activity_main_player.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + xmlns:app="http://schemas.android.com/apk/res-auto" android:background="@android:color/black" android:gravity="center"> @@ -41,6 +42,33 @@ tools:ignore="ContentDescription" tools:visibility="visible"/> + + + + + + + + + +