From ce36f3ae3be1ea9a54d349144bebaaebc20a9966 Mon Sep 17 00:00:00 2001 From: John Zhen Mo Date: Sat, 11 Nov 2017 14:47:34 -0800 Subject: [PATCH] -Modified play queue action buttons on playlist and channel fragments. -Added info item menu with custom title banner on long click. -Enabled starting playlists and channels from middle. -Enabled caching for play queue item thumbnails. -Modified play queue to revert to first item on deleting last item. --- .../fragments/detail/VideoDetailFragment.java | 32 +++++ .../fragments/list/BaseListFragment.java | 40 +++++- .../list/channel/ChannelFragment.java | 62 ++++++++- .../list/playlist/PlaylistFragment.java | 60 +++++++- .../subscription/SubscriptionFragment.java | 3 + .../newpipe/info_list/InfoItemBuilder.java | 1 + .../newpipe/info_list/InfoItemDialog.java | 39 ++++++ .../holder/StreamMiniInfoItemHolder.java | 32 +++++ .../newpipe/player/ServicePlayerActivity.java | 12 +- .../schabi/newpipe/playlist/PlayQueue.java | 2 +- .../playlist/PlayQueueItemBuilder.java | 2 + app/src/main/res/layout/channel_header.xml | 126 ++++++++++------- app/src/main/res/layout/dialog_title.xml | 36 +++++ app/src/main/res/layout/play_queue_item.xml | 6 +- app/src/main/res/layout/playlist_header.xml | 131 +++++++++++------- app/src/main/res/values/strings.xml | 5 + 16 files changed, 464 insertions(+), 125 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java create mode 100644 app/src/main/res/layout/dialog_title.xml 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 9522446af..a4ee01ccc 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 @@ -63,6 +63,7 @@ import org.schabi.newpipe.fragments.BackPressable; import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.history.HistoryListener; import org.schabi.newpipe.info_list.InfoItemBuilder; +import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.player.MainVideoPlayer; import org.schabi.newpipe.player.PopupVideoPlayer; import org.schabi.newpipe.player.helper.PlayerHelper; @@ -460,6 +461,11 @@ public class VideoDetailFragment extends BaseStateFragment implement public void selected(StreamInfoItem selectedItem) { selectAndLoadVideo(selectedItem.service_id, selectedItem.url, selectedItem.name); } + + @Override + public void held(StreamInfoItem selectedItem) { + showStreamDialog(selectedItem); + } }); videoTitleRoot.setOnClickListener(this); @@ -477,6 +483,32 @@ public class VideoDetailFragment extends BaseStateFragment implement detailControlsPopup.setOnTouchListener(getOnControlsTouchListener()); } + private void showStreamDialog(final StreamInfoItem item) { + final Context context = getContext(); + final String[] commands = new String[]{ + context.getResources().getString(R.string.enqueue_on_background), + context.getResources().getString(R.string.enqueue_on_popup) + }; + + final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + switch (i) { + case 0: + NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item)); + break; + case 1: + NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item)); + break; + default: + break; + } + } + }; + + new InfoItemDialog(getActivity(), item, commands, actions).show(); + } + private View.OnTouchListener getOnControlsTouchListener() { return new View.OnTouchListener() { @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index a780c5160..fd1e9ea63 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -1,6 +1,7 @@ package org.schabi.newpipe.fragments.list; import android.content.Context; +import android.content.DialogInterface; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v7.app.ActionBar; @@ -10,7 +11,6 @@ import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.View; -import android.widget.PopupMenu; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.InfoItem; @@ -20,7 +20,9 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.info_list.InfoItemBuilder; +import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoListAdapter; +import org.schabi.newpipe.playlist.SinglePlayQueue; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.StateSaver; @@ -140,6 +142,11 @@ public abstract class BaseListFragment extends BaseStateFragment implem useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(), selectedItem.service_id, selectedItem.url, selectedItem.name); } + + @Override + public void held(StreamInfoItem selectedItem) { + showStreamDialog(selectedItem); + } }); infoListAdapter.setOnChannelSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { @@ -150,6 +157,9 @@ public abstract class BaseListFragment extends BaseStateFragment implem useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(), selectedItem.service_id, selectedItem.url, selectedItem.name); } + + @Override + public void held(ChannelInfoItem selectedItem) {} }); infoListAdapter.setOnPlaylistSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { @@ -160,6 +170,9 @@ public abstract class BaseListFragment extends BaseStateFragment implem useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(), selectedItem.service_id, selectedItem.url, selectedItem.name); } + + @Override + public void held(PlaylistInfoItem selectedItem) {} }); itemsList.clearOnScrollListeners(); @@ -177,6 +190,31 @@ public abstract class BaseListFragment extends BaseStateFragment implem } } + protected void showStreamDialog(final StreamInfoItem item) { + final Context context = getContext(); + final String[] commands = new String[]{ + context.getResources().getString(R.string.enqueue_on_background), + context.getResources().getString(R.string.enqueue_on_popup) + }; + + final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + switch (i) { + case 0: + NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item)); + break; + case 1: + NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item)); + break; + default: + break; + } + } + }; + + new InfoItemDialog(getActivity(), item, commands, actions).show(); + } /*////////////////////////////////////////////////////////////////////////// // Menu //////////////////////////////////////////////////////////////////////////*/ diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index 908997a11..8c7cd676f 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -1,6 +1,7 @@ package org.schabi.newpipe.fragments.list.channel; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Build; @@ -20,6 +21,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; @@ -31,10 +33,13 @@ import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.channel.ChannelInfo; import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; import org.schabi.newpipe.fragments.subscription.SubscriptionService; +import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.playlist.ChannelPlayQueue; import org.schabi.newpipe.playlist.PlayQueue; +import org.schabi.newpipe.playlist.SinglePlayQueue; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.AnimationUtils; import org.schabi.newpipe.util.ExtractorHelper; @@ -76,9 +81,9 @@ public class ChannelFragment extends BaseListInfoFragment { private TextView headerSubscribersTextView; private Button headerSubscribeButton; - private Button headerPlayAllButton; - private Button headerPopupButton; - private Button headerBackgroundButton; + private LinearLayout headerPlayAllButton; + private LinearLayout headerPopupButton; + private LinearLayout headerBackgroundButton; private MenuItem menuRssButton; @@ -136,13 +141,52 @@ public class ChannelFragment extends BaseListInfoFragment { headerSubscribersTextView = headerRootLayout.findViewById(R.id.channel_subscriber_view); headerSubscribeButton = headerRootLayout.findViewById(R.id.channel_subscribe_button); - headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_play_all_button); - headerPopupButton = headerRootLayout.findViewById(R.id.playlist_play_popup_button); - headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_play_bg_button); + headerPlayAllButton = headerRootLayout.findViewById(R.id.channel_play_all_button); + headerPopupButton = headerRootLayout.findViewById(R.id.channel_play_popup_button); + headerBackgroundButton = headerRootLayout.findViewById(R.id.channel_play_bg_button); return headerRootLayout; } + @Override + protected void showStreamDialog(final StreamInfoItem item) { + final Context context = getContext(); + final String[] commands = new String[]{ + context.getResources().getString(R.string.enqueue_on_background), + context.getResources().getString(R.string.enqueue_on_popup), + context.getResources().getString(R.string.start_here_on_main), + context.getResources().getString(R.string.start_here_on_background), + context.getResources().getString(R.string.start_here_on_popup), + }; + + final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0); + switch (i) { + case 0: + NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item)); + break; + case 1: + NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item)); + break; + case 2: + NavigationHelper.playOnMainPlayer(context, getPlayQueue(index)); + break; + case 3: + NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index)); + break; + case 4: + NavigationHelper.playOnPopupPlayer(context, getPlayQueue(index)); + break; + default: + break; + } + } + }; + + new InfoItemDialog(getActivity(), item, commands, actions).show(); + } /*////////////////////////////////////////////////////////////////////////// // Menu //////////////////////////////////////////////////////////////////////////*/ @@ -435,12 +479,16 @@ public class ChannelFragment extends BaseListInfoFragment { } private PlayQueue getPlayQueue() { + return getPlayQueue(0); + } + + private PlayQueue getPlayQueue(final int index) { return new ChannelPlayQueue( currentInfo.service_id, currentInfo.url, currentInfo.next_streams_url, infoListAdapter.getItemsList(), - 0 + index ); } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 0c5b002e3..4e6fb14ec 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -1,5 +1,7 @@ package org.schabi.newpipe.fragments.list.playlist; +import android.content.Context; +import android.content.DialogInterface; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; @@ -12,8 +14,8 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; @@ -22,9 +24,12 @@ import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; +import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; -import org.schabi.newpipe.playlist.PlaylistPlayQueue; +import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.playlist.PlayQueue; +import org.schabi.newpipe.playlist.PlaylistPlayQueue; +import org.schabi.newpipe.playlist.SinglePlayQueue; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.NavigationHelper; @@ -47,9 +52,9 @@ public class PlaylistFragment extends BaseListInfoFragment { private ImageView headerUploaderAvatar; private TextView headerStreamCount; - private Button headerPlayAllButton; - private Button headerPopupButton; - private Button headerBackgroundButton; + private LinearLayout headerPlayAllButton; + private LinearLayout headerPopupButton; + private LinearLayout headerBackgroundButton; public static PlaylistFragment getInstance(int serviceId, String url, String name) { PlaylistFragment instance = new PlaylistFragment(); @@ -99,6 +104,45 @@ public class PlaylistFragment extends BaseListInfoFragment { inflater.inflate(R.menu.menu_playlist, menu); } + @Override + protected void showStreamDialog(final StreamInfoItem item) { + final Context context = getContext(); + final String[] commands = new String[]{ + context.getResources().getString(R.string.enqueue_on_background), + context.getResources().getString(R.string.enqueue_on_popup), + context.getResources().getString(R.string.start_here_on_main), + context.getResources().getString(R.string.start_here_on_background), + context.getResources().getString(R.string.start_here_on_popup), + }; + + final DialogInterface.OnClickListener actions = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + final int index = Math.max(infoListAdapter.getItemsList().indexOf(item), 0); + switch (i) { + case 0: + NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(item)); + break; + case 1: + NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(item)); + break; + case 2: + NavigationHelper.playOnMainPlayer(context, getPlayQueue(index)); + break; + case 3: + NavigationHelper.playOnBackgroundPlayer(context, getPlayQueue(index)); + break; + case 4: + NavigationHelper.playOnPopupPlayer(context, getPlayQueue(index)); + break; + default: + break; + } + } + }; + + new InfoItemDialog(getActivity(), item, commands, actions).show(); + } /*////////////////////////////////////////////////////////////////////////// // Load and handle //////////////////////////////////////////////////////////////////////////*/ @@ -181,12 +225,16 @@ public class PlaylistFragment extends BaseListInfoFragment { } private PlayQueue getPlayQueue() { + return getPlayQueue(0); + } + + private PlayQueue getPlayQueue(final int index) { return new PlaylistPlayQueue( currentInfo.service_id, currentInfo.url, currentInfo.next_streams_url, infoListAdapter.getItemsList(), - 0 + index ); } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java index 4e60ef0d0..302adecf8 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java @@ -132,6 +132,9 @@ public class SubscriptionFragment extends BaseStateFragment { void selected(T selectedItem); + void held(T selectedItem); } private final Context context; diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java new file mode 100644 index 000000000..72152500f --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java @@ -0,0 +1,39 @@ +package org.schabi.newpipe.info_list; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.support.annotation.NonNull; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.InfoItem; + +public class InfoItemDialog { + private final AlertDialog dialog; + + public InfoItemDialog(@NonNull final Activity activity, + @NonNull final InfoItem item, + @NonNull final String[] commands, + @NonNull final DialogInterface.OnClickListener actions) { + + final LayoutInflater inflater = activity.getLayoutInflater(); + final View bannerView = inflater.inflate(R.layout.dialog_title, null); + bannerView.setSelected(true); + TextView titleView = bannerView.findViewById(R.id.itemTitleView); + titleView.setText(item.name); + TextView typeView = bannerView.findViewById(R.id.itemTypeView); + typeView.setText(item.info_type.name()); + + dialog = new AlertDialog.Builder(activity) + .setCustomTitle(bannerView) + .setItems(commands, actions) + .create(); + } + + public void show() { + dialog.show(); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java index 138503d39..48f775070 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java @@ -67,6 +67,38 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder { } } }); + + switch (item.stream_type) { + case AUDIO_STREAM: + case VIDEO_STREAM: + case FILE: + enableLongClick(item); + break; + case LIVE_STREAM: + case AUDIO_LIVE_STREAM: + case NONE: + default: + disableLongClick(); + break; + } + } + + private void enableLongClick(final StreamInfoItem item) { + itemView.setLongClickable(true); + itemView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + if (itemBuilder.getOnStreamSelectedListener() != null) { + itemBuilder.getOnStreamSelectedListener().held(item); + } + return true; + } + }); + } + + private void disableLongClick() { + itemView.setLongClickable(false); + itemView.setOnLongClickListener(null); } /** diff --git a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java index 98595b358..0f8da3711 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java @@ -444,20 +444,22 @@ public abstract class ServicePlayerActivity extends AppCompatActivity @Override public void onClick(View view) { + if (player == null) return; + if (view.getId() == repeatButton.getId()) { - if (player != null) player.onRepeatClicked(); + player.onRepeatClicked(); } else if (view.getId() == backwardButton.getId()) { - if (player != null) player.onPlayPrevious(); + player.onPlayPrevious(); } else if (view.getId() == playPauseButton.getId()) { - if (player != null) player.onVideoPlayPause(); + player.onVideoPlayPause(); } else if (view.getId() == forwardButton.getId()) { - if (player != null) player.onPlayNext(); + player.onPlayNext(); } else if (view.getId() == shuffleButton.getId()) { - if (player != null) player.onShuffleClicked(); + player.onShuffleClicked(); } else if (view.getId() == playbackSpeedButton.getId()) { playbackSpeedPopupMenu.show(); diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java index b86450b10..3daa58bb7 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java @@ -279,7 +279,7 @@ public abstract class PlayQueue implements Serializable { queueIndex.set(currentIndex % (size - 1)); } else if (currentIndex == removeIndex && currentIndex == size - 1){ - queueIndex.set(removeIndex - 1); + queueIndex.set(0); } if (backup != null) { diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItemBuilder.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItemBuilder.java index c3649ce09..82277a4e7 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItemBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItemBuilder.java @@ -107,6 +107,8 @@ public class PlayQueueItemBuilder { .bitmapConfig(Bitmap.Config.RGB_565) // Users won't be able to see much anyways .preProcessor(bitmapProcessor) .imageScaleType(ImageScaleType.EXACTLY) + .cacheInMemory(true) + .cacheOnDisk(true) .build(); } } diff --git a/app/src/main/res/layout/channel_header.xml b/app/src/main/res/layout/channel_header.xml index 7f08e5932..a328bccac 100644 --- a/app/src/main/res/layout/channel_header.xml +++ b/app/src/main/res/layout/channel_header.xml @@ -83,60 +83,88 @@ tools:visibility="visible"/> - -