From 80157fc1be921beda9051879dfbe1b9ef95e6075 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Mon, 27 Dec 2021 16:16:40 +0100 Subject: [PATCH 01/11] Refactor generating InfoItemDialog's This commit refactors the way `InfoItemDialog`s are generated. This is necessary because the old way used the `StreamDialogEntry` enum for most of the dialogs' content generation process. This required static variables and methods to store the entries which are used for the dialog to be build (See e.g.`enabledEntries` and methods like `generateCommands()`). In other words, `StreamDialogEntry` wasn't an enumeration anymore. To address this issue, a `Builder` is introduced for the `InfoItemDialog`'s genration. The builder also comes with some default entries and and a specific order. Both can be used, but are not enforced. A second problem that introduced a structure which was atypical for an enumeration was the usage of non-final attributes within `StreamDialogEntry` instances. These were needed, because the default actions needed to overriden in some cases. To address this problem, the `StreamDialogEntry` enumeration was renamed to `StreamDialogDefaultEntry` and a new `StreamDialogEntry` class is used instead. --- .../fragments/list/BaseListFragment.java | 67 ++--- .../list/playlist/PlaylistFragment.java | 66 ++--- .../newpipe/info_list/InfoItemDialog.java | 146 +++++++++-- .../schabi/newpipe/local/feed/FeedFragment.kt | 57 +---- .../history/StatisticsPlaylistFragment.java | 69 ++---- .../local/playlist/LocalPlaylistFragment.java | 85 ++----- .../util/StreamDialogDefaultEntry.java | 151 ++++++++++++ .../newpipe/util/StreamDialogEntry.java | 230 ++---------------- 8 files changed, 384 insertions(+), 487 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java 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 6ea0a8a0d..ec9e34964 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,5 +1,8 @@ package org.schabi.newpipe.fragments.list; +import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; + import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; @@ -25,29 +28,20 @@ import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.extractor.comments.CommentsInfoItem; import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem; -import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.info_list.InfoListAdapter; -import org.schabi.newpipe.player.helper.PlayerHolder; -import org.schabi.newpipe.util.external_communication.KoreUtils; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; import org.schabi.newpipe.util.StateSaver; -import org.schabi.newpipe.util.StreamDialogEntry; +import org.schabi.newpipe.util.StreamDialogDefaultEntry; import org.schabi.newpipe.views.SuperScrollLayoutManager; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Queue; import java.util.function.Supplier; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; -import static org.schabi.newpipe.ktx.ViewUtils.animate; -import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; - public abstract class BaseListFragment extends BaseStateFragment implements ListViewContract, StateSaver.WriteRead, SharedPreferences.OnSharedPreferenceChangeListener { @@ -415,49 +409,22 @@ public abstract class BaseListFragment extends BaseStateFragment if (context == null || context.getResources() == null || activity == null) { return; } - final List entries = new ArrayList<>(); - if (PlayerHolder.getInstance().isPlayQueueReady()) { - entries.add(StreamDialogEntry.enqueue); + final InfoItemDialog.Builder dialogBuilder = new InfoItemDialog.Builder( + activity, this, item); - if (PlayerHolder.getInstance().getQueueSize() > 1) { - entries.add(StreamDialogEntry.enqueue_next); - } - } + dialogBuilder.addEnqueueEntriesIfNeeded(); + dialogBuilder.addStartHereEntries(); + dialogBuilder.addAllEntries( + StreamDialogDefaultEntry.APPEND_PLAYLIST, + StreamDialogDefaultEntry.SHARE, + StreamDialogDefaultEntry.OPEN_IN_BROWSER + ); + dialogBuilder.addPlayWithKodiEntryIfNeeded(); + dialogBuilder.addMarkAsWatchedEntryIfNeeded(item.getStreamType()); + dialogBuilder.addChannelDetailsEntryIfPossible(); - if (item.getStreamType() == StreamType.AUDIO_STREAM) { - entries.addAll(Arrays.asList( - StreamDialogEntry.start_here_on_background, - StreamDialogEntry.append_playlist, - StreamDialogEntry.share - )); - } else { - entries.addAll(Arrays.asList( - StreamDialogEntry.start_here_on_background, - StreamDialogEntry.start_here_on_popup, - StreamDialogEntry.append_playlist, - StreamDialogEntry.share - )); - } - entries.add(StreamDialogEntry.open_in_browser); - if (KoreUtils.shouldShowPlayWithKodi(context, item.getServiceId())) { - entries.add(StreamDialogEntry.play_with_kodi); - } - - // show "mark as watched" only when watch history is enabled - if (StreamDialogEntry.shouldAddMarkAsWatched(item.getStreamType(), context)) { - entries.add( - StreamDialogEntry.mark_as_watched - ); - } - if (!isNullOrEmpty(item.getUploaderUrl())) { - entries.add(StreamDialogEntry.show_channel_details); - } - - StreamDialogEntry.setEnabledEntries(entries); - - new InfoItemDialog(activity, item, StreamDialogEntry.getCommands(context), - (dialog, which) -> StreamDialogEntry.clickOn(which, this, item)).show(); + dialogBuilder.create().show(); } /*////////////////////////////////////////////////////////////////////////// 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 84dcb4fd9..d90ea4e8c 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,6 +1,5 @@ package org.schabi.newpipe.fragments.list.playlist; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; @@ -36,24 +35,20 @@ import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper; import org.schabi.newpipe.extractor.stream.StreamInfoItem; -import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.local.playlist.RemotePlaylistManager; import org.schabi.newpipe.player.MainPlayer.PlayerType; -import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.PicassoHelper; -import org.schabi.newpipe.util.StreamDialogEntry; -import org.schabi.newpipe.util.external_communication.KoreUtils; +import org.schabi.newpipe.util.StreamDialogDefaultEntry; import org.schabi.newpipe.util.external_communication.ShareUtils; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; @@ -147,53 +142,26 @@ public class PlaylistFragment extends BaseListInfoFragment { return; } - final ArrayList entries = new ArrayList<>(); + final InfoItemDialog.Builder dialogBuilder = new InfoItemDialog.Builder( + activity, this, item); - if (PlayerHolder.getInstance().isPlayQueueReady()) { - entries.add(StreamDialogEntry.enqueue); + dialogBuilder.addEnqueueEntriesIfNeeded(); + dialogBuilder.addStartHereEntries(); + dialogBuilder.addAllEntries( + StreamDialogDefaultEntry.APPEND_PLAYLIST, + StreamDialogDefaultEntry.SHARE, + StreamDialogDefaultEntry.OPEN_IN_BROWSER + ); + dialogBuilder.addPlayWithKodiEntryIfNeeded(); + dialogBuilder.addMarkAsWatchedEntryIfNeeded(item.getStreamType()); + dialogBuilder.addChannelDetailsEntryIfPossible(); - if (PlayerHolder.getInstance().getQueueSize() > 1) { - entries.add(StreamDialogEntry.enqueue_next); - } - } + dialogBuilder.setAction(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND, + (fragment, infoItem) -> NavigationHelper.playOnBackgroundPlayer( + context, getPlayQueueStartingAt(infoItem), true)); - if (item.getStreamType() == StreamType.AUDIO_STREAM) { - entries.addAll(Arrays.asList( - StreamDialogEntry.start_here_on_background, - StreamDialogEntry.append_playlist, - StreamDialogEntry.share - )); - } else { - entries.addAll(Arrays.asList( - StreamDialogEntry.start_here_on_background, - StreamDialogEntry.start_here_on_popup, - StreamDialogEntry.append_playlist, - StreamDialogEntry.share - )); - } - entries.add(StreamDialogEntry.open_in_browser); - if (KoreUtils.shouldShowPlayWithKodi(context, item.getServiceId())) { - entries.add(StreamDialogEntry.play_with_kodi); - } + dialogBuilder.create().show(); - // show "mark as watched" only when watch history is enabled - if (StreamDialogEntry.shouldAddMarkAsWatched(item.getStreamType(), context)) { - entries.add( - StreamDialogEntry.mark_as_watched - ); - } - if (!isNullOrEmpty(item.getUploaderUrl())) { - entries.add(StreamDialogEntry.show_channel_details); - } - - StreamDialogEntry.setEnabledEntries(entries); - - StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItem) -> - NavigationHelper.playOnBackgroundPlayer(context, - getPlayQueueStartingAt(infoItem), true)); - - new InfoItemDialog(activity, item, StreamDialogEntry.getCommands(context), - (dialog, which) -> StreamDialogEntry.clickOn(which, this, item)).show(); } @Override 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 index c485337f0..b5b17ab3e 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java @@ -1,54 +1,164 @@ package org.schabi.newpipe.info_list; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; + import android.app.Activity; import android.content.DialogInterface; import android.view.View; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.Fragment; +import androidx.preference.PreferenceManager; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.stream.StreamInfoItem; +import org.schabi.newpipe.extractor.stream.StreamType; +import org.schabi.newpipe.player.helper.PlayerHolder; +import org.schabi.newpipe.util.StreamDialogDefaultEntry; +import org.schabi.newpipe.util.StreamDialogEntry; +import org.schabi.newpipe.util.external_communication.KoreUtils; -public class InfoItemDialog { +import java.util.ArrayList; +import java.util.List; + +/** + * Dialog with actions for a {@link StreamInfoItem}. + * This dialog is mostly used for longpress context menus. + */ +public final class InfoItemDialog { private final AlertDialog dialog; - public InfoItemDialog(@NonNull final Activity activity, - @NonNull final StreamInfoItem info, - @NonNull final String[] commands, - @NonNull final DialogInterface.OnClickListener actions) { - this(activity, commands, actions, info.getName(), info.getUploaderName()); - } - - public InfoItemDialog(@NonNull final Activity activity, - @NonNull final String[] commands, - @NonNull final DialogInterface.OnClickListener actions, - @NonNull final String title, - @Nullable final String additionalDetail) { + private InfoItemDialog(@NonNull final Activity activity, + @NonNull final Fragment fragment, + @NonNull final StreamInfoItem info, + @NonNull final List entries) { final View bannerView = View.inflate(activity, R.layout.dialog_title, null); bannerView.setSelected(true); final TextView titleView = bannerView.findViewById(R.id.itemTitleView); - titleView.setText(title); + titleView.setText(info.getName()); final TextView detailsView = bannerView.findViewById(R.id.itemAdditionalDetails); - if (additionalDetail != null) { - detailsView.setText(additionalDetail); + if (info.getUploaderName() != null) { + detailsView.setText(info.getUploaderName()); detailsView.setVisibility(View.VISIBLE); } else { detailsView.setVisibility(View.GONE); } + final String[] items = entries.stream() + .map(entry -> entry.getString(activity)).toArray(String[]::new); + + final DialogInterface.OnClickListener action = (d, index) -> + entries.get(index).action.onClick(fragment, info); + dialog = new AlertDialog.Builder(activity) .setCustomTitle(bannerView) - .setItems(commands, actions) + .setItems(items, action) .create(); + } public void show() { dialog.show(); } + + /** + *

Builder to generate a {@link InfoItemDialog}.

+ * Use {@link #addEntry(StreamDialogDefaultEntry)} + * and {@link #addAllEntries(StreamDialogDefaultEntry...)} to add options to the dialog. + *
+ * Custom actions for entries can be set using + * {@link #setAction(StreamDialogDefaultEntry, StreamDialogEntry.StreamDialogEntryAction)}. + */ + public static class Builder { + @NonNull private final Activity activity; + @NonNull private final StreamInfoItem info; + @NonNull private final Fragment fragment; + @NonNull private final List entries = new ArrayList<>(); + + public Builder(@NonNull final Activity activity, + @NonNull final Fragment fragment, + @NonNull final StreamInfoItem info) { + this.activity = activity; + this.fragment = fragment; + this.info = info; + } + + public void addEntry(@NonNull final StreamDialogDefaultEntry entry) { + entries.add(entry.toStreamDialogEntry()); + } + + public void addAllEntries(@NonNull final StreamDialogDefaultEntry... newEntries) { + for (final StreamDialogDefaultEntry entry: newEntries) { + this.entries.add(entry.toStreamDialogEntry()); + } + } + + public void setAction(@NonNull final StreamDialogDefaultEntry entry, + @NonNull final StreamDialogEntry.StreamDialogEntryAction action) { + for (int i = 0; i < entries.size(); i++) { + if (entries.get(i).resource == entry.resource) { + entries.set(i, new StreamDialogEntry(entry.resource, action)); + } + } + } + + public void addChannelDetailsEntryIfPossible() { + if (!isNullOrEmpty(info.getUploaderUrl())) { + addEntry(StreamDialogDefaultEntry.SHOW_CHANNEL_DETAILS); + } + } + + public void addEnqueueEntriesIfNeeded() { + if (PlayerHolder.getInstance().isPlayerOpen()) { + addEntry(StreamDialogDefaultEntry.ENQUEUE); + + if (PlayerHolder.getInstance().getQueueSize() > 1) { + addEntry(StreamDialogDefaultEntry.ENQUEUE_NEXT); + } + } + } + + public void addStartHereEntries() { + addEntry(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND); + if (info.getStreamType() != StreamType.AUDIO_STREAM + && info.getStreamType() != StreamType.AUDIO_LIVE_STREAM) { + addEntry(StreamDialogDefaultEntry.START_HERE_ON_POPUP); + } + } + + /** + * Adds {@link StreamDialogDefaultEntry.MARK_AS_WATCHED} if the watch history is enabled + * and the stream is not a livestream. + * @param streamType the item's stream type + */ + public void addMarkAsWatchedEntryIfNeeded(final StreamType streamType) { + final boolean isWatchHistoryEnabled = PreferenceManager + .getDefaultSharedPreferences(activity) + .getBoolean(activity.getString(R.string.enable_watch_history_key), false); + if (streamType != StreamType.AUDIO_LIVE_STREAM + && streamType != StreamType.LIVE_STREAM + && isWatchHistoryEnabled) { + addEntry(StreamDialogDefaultEntry.MARK_AS_WATCHED); + } + } + + public void addPlayWithKodiEntryIfNeeded() { + if (KoreUtils.shouldShowPlayWithKodi(activity, info.getServiceId())) { + addEntry(StreamDialogDefaultEntry.PLAY_WITH_KODI); + } + } + + /** + * Creates the {@link InfoItemDialog}. + * @return a new instance of {@link InfoItemDialog} + */ + public InfoItemDialog create() { + return new InfoItemDialog(this.activity, this.fragment, this.info, this.entries); + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt index e6da0d545..78e765609 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt @@ -68,7 +68,6 @@ import org.schabi.newpipe.error.UserAction import org.schabi.newpipe.extractor.exceptions.AccountTerminatedException import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException import org.schabi.newpipe.extractor.stream.StreamInfoItem -import org.schabi.newpipe.extractor.stream.StreamType import org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty import org.schabi.newpipe.fragments.BaseStateFragment import org.schabi.newpipe.info_list.InfoItemDialog @@ -78,15 +77,13 @@ import org.schabi.newpipe.ktx.slideUp import org.schabi.newpipe.local.feed.item.StreamItem import org.schabi.newpipe.local.feed.service.FeedLoadService import org.schabi.newpipe.local.subscription.SubscriptionManager -import org.schabi.newpipe.player.helper.PlayerHolder import org.schabi.newpipe.util.DeviceUtils import org.schabi.newpipe.util.Localization import org.schabi.newpipe.util.NavigationHelper -import org.schabi.newpipe.util.StreamDialogEntry +import org.schabi.newpipe.util.StreamDialogDefaultEntry import org.schabi.newpipe.util.ThemeHelper.getGridSpanCountStreams import org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout import java.time.OffsetDateTime -import java.util.ArrayList import java.util.function.Consumer class FeedFragment : BaseStateFragment() { @@ -361,48 +358,20 @@ class FeedFragment : BaseStateFragment() { val activity: Activity? = getActivity() if (context == null || context.resources == null || activity == null) return - val entries = ArrayList() - if (PlayerHolder.getInstance().isPlayQueueReady) { - entries.add(StreamDialogEntry.enqueue) + val dialogBuilder = InfoItemDialog.Builder(activity, this, item) - if (PlayerHolder.getInstance().queueSize > 1) { - entries.add(StreamDialogEntry.enqueue_next) - } - } + dialogBuilder.addEnqueueEntriesIfNeeded() + dialogBuilder.addStartHereEntries() + dialogBuilder.addAllEntries( + StreamDialogDefaultEntry.APPEND_PLAYLIST, + StreamDialogDefaultEntry.SHARE, + StreamDialogDefaultEntry.OPEN_IN_BROWSER + ) + dialogBuilder.addPlayWithKodiEntryIfNeeded() + dialogBuilder.addMarkAsWatchedEntryIfNeeded(item.streamType) + dialogBuilder.addChannelDetailsEntryIfPossible() - if (item.streamType == StreamType.AUDIO_STREAM) { - entries.addAll( - listOf( - StreamDialogEntry.start_here_on_background, - StreamDialogEntry.append_playlist, - StreamDialogEntry.share, - StreamDialogEntry.open_in_browser - ) - ) - } else { - entries.addAll( - listOf( - StreamDialogEntry.start_here_on_background, - StreamDialogEntry.start_here_on_popup, - StreamDialogEntry.append_playlist, - StreamDialogEntry.share, - StreamDialogEntry.open_in_browser - ) - ) - } - - // show "mark as watched" only when watch history is enabled - if (StreamDialogEntry.shouldAddMarkAsWatched(item.streamType, context)) { - entries.add( - StreamDialogEntry.mark_as_watched - ) - } - entries.add(StreamDialogEntry.show_channel_details) - - StreamDialogEntry.setEnabledEntries(entries) - InfoItemDialog(activity, item, StreamDialogEntry.getCommands(context)) { _, which -> - StreamDialogEntry.clickOn(which, this, item) - }.show() + dialogBuilder.create().show() } private val listenerStreamItem = object : OnItemClickListener, OnItemLongClickListener { diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java index 73682d5d5..a11f1a589 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java @@ -29,20 +29,16 @@ import org.schabi.newpipe.databinding.StatisticPlaylistControlBinding; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.extractor.stream.StreamInfoItem; -import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.local.BaseLocalListFragment; -import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.settings.HistorySettingsFragment; -import org.schabi.newpipe.util.external_communication.KoreUtils; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; -import org.schabi.newpipe.util.StreamDialogEntry; +import org.schabi.newpipe.util.StreamDialogDefaultEntry; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -336,58 +332,29 @@ public class StatisticsPlaylistFragment } final StreamInfoItem infoItem = item.toStreamInfoItem(); - final ArrayList entries = new ArrayList<>(); + final InfoItemDialog.Builder dialogBuilder = new InfoItemDialog.Builder( + activity, this, infoItem); - if (PlayerHolder.getInstance().isPlayQueueReady()) { - entries.add(StreamDialogEntry.enqueue); + dialogBuilder.addEnqueueEntriesIfNeeded(); + dialogBuilder.addStartHereEntries(); + dialogBuilder.addAllEntries( + StreamDialogDefaultEntry.DELETE, + StreamDialogDefaultEntry.APPEND_PLAYLIST, + StreamDialogDefaultEntry.SHARE, + StreamDialogDefaultEntry.OPEN_IN_BROWSER + ); + dialogBuilder.addPlayWithKodiEntryIfNeeded(); + dialogBuilder.addMarkAsWatchedEntryIfNeeded(infoItem.getStreamType()); + dialogBuilder.addChannelDetailsEntryIfPossible(); - if (PlayerHolder.getInstance().getQueueSize() > 1) { - entries.add(StreamDialogEntry.enqueue_next); - } - } - if (infoItem.getStreamType() == StreamType.AUDIO_STREAM) { - entries.addAll(Arrays.asList( - StreamDialogEntry.start_here_on_background, - StreamDialogEntry.delete, - StreamDialogEntry.append_playlist, - StreamDialogEntry.share - )); - } else { - entries.addAll(Arrays.asList( - StreamDialogEntry.start_here_on_background, - StreamDialogEntry.start_here_on_popup, - StreamDialogEntry.delete, - StreamDialogEntry.append_playlist, - StreamDialogEntry.share - )); - } - entries.add(StreamDialogEntry.open_in_browser); - if (KoreUtils.shouldShowPlayWithKodi(context, infoItem.getServiceId())) { - entries.add(StreamDialogEntry.play_with_kodi); - } - - // show "mark as watched" only when watch history is enabled - if (StreamDialogEntry.shouldAddMarkAsWatched( - item.getStreamEntity().getStreamType(), - context - )) { - entries.add( - StreamDialogEntry.mark_as_watched - ); - } - entries.add(StreamDialogEntry.show_channel_details); - - StreamDialogEntry.setEnabledEntries(entries); - - StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItemDuplicate) -> - NavigationHelper + dialogBuilder.setAction(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND, + (fragment, infoItemDuplicate) -> NavigationHelper .playOnBackgroundPlayer(context, getPlayQueueStartingAt(item), true)); - StreamDialogEntry.delete.setCustomAction((fragment, infoItemDuplicate) -> + dialogBuilder.setAction(StreamDialogDefaultEntry.DELETE, (fragment, infoItemDuplicate) -> deleteEntry(Math.max(itemListAdapter.getItemsList().indexOf(item), 0))); - new InfoItemDialog(activity, infoItem, StreamDialogEntry.getCommands(context), - (dialog, which) -> StreamDialogEntry.clickOn(which, this, infoItem)).show(); + dialogBuilder.create().show(); } private void deleteEntry(final int index) { diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index feb5b2f96..088577b25 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -1,5 +1,8 @@ package org.schabi.newpipe.local.playlist; +import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout; + import android.app.Activity; import android.content.Context; import android.content.DialogInterface; @@ -38,22 +41,18 @@ import org.schabi.newpipe.databinding.PlaylistControlBinding; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.extractor.stream.StreamInfoItem; -import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.info_list.InfoItemDialog; import org.schabi.newpipe.local.BaseLocalListFragment; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.player.MainPlayer.PlayerType; -import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; -import org.schabi.newpipe.util.external_communication.KoreUtils; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; -import org.schabi.newpipe.util.StreamDialogEntry; +import org.schabi.newpipe.util.StreamDialogDefaultEntry; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -68,9 +67,6 @@ import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.subjects.PublishSubject; -import static org.schabi.newpipe.ktx.ViewUtils.animate; -import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout; - public class LocalPlaylistFragment extends BaseLocalListFragment, Void> { // Save the list 10 seconds after the last change occurred private static final long SAVE_DEBOUNCE_MILLIS = 10000; @@ -751,62 +747,33 @@ public class LocalPlaylistFragment extends BaseLocalListFragment entries = new ArrayList<>(); + final InfoItemDialog.Builder dialogBuilder = new InfoItemDialog.Builder( + activity, this, infoItem); - if (PlayerHolder.getInstance().isPlayQueueReady()) { - entries.add(StreamDialogEntry.enqueue); + dialogBuilder.addEnqueueEntriesIfNeeded(); + dialogBuilder.addStartHereEntries(); + dialogBuilder.addAllEntries( + StreamDialogDefaultEntry.SET_AS_PLAYLIST_THUMBNAIL, + StreamDialogDefaultEntry.DELETE, + StreamDialogDefaultEntry.APPEND_PLAYLIST, + StreamDialogDefaultEntry.SHARE, + StreamDialogDefaultEntry.OPEN_IN_BROWSER + ); + dialogBuilder.addPlayWithKodiEntryIfNeeded(); + dialogBuilder.addMarkAsWatchedEntryIfNeeded(infoItem.getStreamType()); + dialogBuilder.addChannelDetailsEntryIfPossible(); - if (PlayerHolder.getInstance().getQueueSize() > 1) { - entries.add(StreamDialogEntry.enqueue_next); - } - } - if (infoItem.getStreamType() == StreamType.AUDIO_STREAM) { - entries.addAll(Arrays.asList( - StreamDialogEntry.start_here_on_background, - StreamDialogEntry.set_as_playlist_thumbnail, - StreamDialogEntry.delete, - StreamDialogEntry.append_playlist, - StreamDialogEntry.share - )); - } else { - entries.addAll(Arrays.asList( - StreamDialogEntry.start_here_on_background, - StreamDialogEntry.start_here_on_popup, - StreamDialogEntry.set_as_playlist_thumbnail, - StreamDialogEntry.delete, - StreamDialogEntry.append_playlist, - StreamDialogEntry.share - )); - } - entries.add(StreamDialogEntry.open_in_browser); - if (KoreUtils.shouldShowPlayWithKodi(context, infoItem.getServiceId())) { - entries.add(StreamDialogEntry.play_with_kodi); - } - - // show "mark as watched" only when watch history is enabled - if (StreamDialogEntry.shouldAddMarkAsWatched( - item.getStreamEntity().getStreamType(), - context - )) { - entries.add( - StreamDialogEntry.mark_as_watched - ); - } - entries.add(StreamDialogEntry.show_channel_details); - - StreamDialogEntry.setEnabledEntries(entries); - - StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItemDuplicate) -> - NavigationHelper.playOnBackgroundPlayer(context, - getPlayQueueStartingAt(item), true)); - StreamDialogEntry.set_as_playlist_thumbnail.setCustomAction( + // set custom actions + dialogBuilder.setAction(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND, + (fragment, infoItemDuplicate) -> NavigationHelper.playOnBackgroundPlayer( + context, getPlayQueueStartingAt(item), true)); + dialogBuilder.setAction(StreamDialogDefaultEntry.SET_AS_PLAYLIST_THUMBNAIL, (fragment, infoItemDuplicate) -> changeThumbnailUrl(item.getStreamEntity().getThumbnailUrl())); - StreamDialogEntry.delete.setCustomAction((fragment, infoItemDuplicate) -> - deleteItem(item)); + dialogBuilder.setAction(StreamDialogDefaultEntry.DELETE, + (fragment, infoItemDuplicate) -> deleteItem(item)); - new InfoItemDialog(activity, infoItem, StreamDialogEntry.getCommands(context), - (dialog, which) -> StreamDialogEntry.clickOn(which, this, infoItem)).show(); + dialogBuilder.create().show(); } private void setInitialData(final long pid, final String title) { diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java b/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java new file mode 100644 index 000000000..435a32219 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java @@ -0,0 +1,151 @@ +package org.schabi.newpipe.util; + +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; + +import android.net.Uri; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; +import androidx.fragment.app.Fragment; + +import org.schabi.newpipe.NewPipeDatabase; +import org.schabi.newpipe.R; +import org.schabi.newpipe.database.stream.model.StreamEntity; +import org.schabi.newpipe.extractor.stream.StreamInfoItem; +import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; +import org.schabi.newpipe.local.dialog.PlaylistDialog; +import org.schabi.newpipe.local.history.HistoryRecordManager; +import org.schabi.newpipe.player.playqueue.SinglePlayQueue; +import org.schabi.newpipe.util.external_communication.KoreUtils; +import org.schabi.newpipe.util.external_communication.ShareUtils; + +import java.util.Collections; + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.schedulers.Schedulers; + +public enum StreamDialogDefaultEntry { + ////////////////////////////////////// + // enum values with DEFAULT actions // + ////////////////////////////////////// + + SHOW_CHANNEL_DETAILS(R.string.show_channel_details, (fragment, item) -> { + if (isNullOrEmpty(item.getUploaderUrl())) { + final int serviceId = item.getServiceId(); + final String url = item.getUrl(); + Toast.makeText(fragment.getContext(), R.string.loading_channel_details, + Toast.LENGTH_SHORT).show(); + ExtractorHelper.getStreamInfo(serviceId, url, false) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + NewPipeDatabase.getInstance(fragment.requireContext()).streamDAO() + .setUploaderUrl(serviceId, url, result.getUploaderUrl()) + .subscribeOn(Schedulers.io()).subscribe(); + openChannelFragment(fragment, item, result.getUploaderUrl()); + }, throwable -> Toast.makeText( + // TODO: Open the Error Activity + fragment.getContext(), + R.string.error_show_channel_details, + Toast.LENGTH_SHORT + ).show()); + } else { + openChannelFragment(fragment, item, item.getUploaderUrl()); + } + }), + + /** + * Enqueues the stream automatically to the current PlayerType.
+ *
+ * Info: Add this entry within showStreamDialog. + */ + ENQUEUE(R.string.enqueue_stream, (fragment, item) -> + NavigationHelper.enqueueOnPlayer(fragment.getContext(), new SinglePlayQueue(item)) + ), + + ENQUEUE_NEXT(R.string.enqueue_next_stream, (fragment, item) -> + NavigationHelper.enqueueNextOnPlayer(fragment.getContext(), new SinglePlayQueue(item)) + ), + + START_HERE_ON_BACKGROUND(R.string.start_here_on_background, (fragment, item) -> + NavigationHelper.playOnBackgroundPlayer(fragment.getContext(), + new SinglePlayQueue(item), true)), + + START_HERE_ON_POPUP(R.string.start_here_on_popup, (fragment, item) -> + NavigationHelper.playOnPopupPlayer(fragment.getContext(), + new SinglePlayQueue(item), true)), + + SET_AS_PLAYLIST_THUMBNAIL(R.string.set_as_playlist_thumbnail, (fragment, item) -> { + }), // has to be set manually + + DELETE(R.string.delete, (fragment, item) -> { + }), // has to be set manually + + APPEND_PLAYLIST(R.string.add_to_playlist, (fragment, item) -> + PlaylistDialog.createCorrespondingDialog( + fragment.getContext(), + Collections.singletonList(new StreamEntity(item)), + dialog -> dialog.show( + fragment.getParentFragmentManager(), + "StreamDialogEntry@" + + (dialog instanceof PlaylistAppendDialog ? "append" : "create") + + "_playlist" + ) + ) + ), + + PLAY_WITH_KODI(R.string.play_with_kodi_title, (fragment, item) -> { + final Uri videoUrl = Uri.parse(item.getUrl()); + try { + NavigationHelper.playWithKore(fragment.requireContext(), videoUrl); + } catch (final Exception e) { + KoreUtils.showInstallKoreDialog(fragment.requireActivity()); + } + }), + + SHARE(R.string.share, (fragment, item) -> + ShareUtils.shareText(fragment.requireContext(), item.getName(), item.getUrl(), + item.getThumbnailUrl())), + + OPEN_IN_BROWSER(R.string.open_in_browser, (fragment, item) -> + ShareUtils.openUrlInBrowser(fragment.requireContext(), item.getUrl())), + + + MARK_AS_WATCHED(R.string.mark_as_watched, (fragment, item) -> + new HistoryRecordManager(fragment.getContext()) + .markAsWatched(item) + .onErrorComplete() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe() + ); + + + @StringRes + public final int resource; + @NonNull + public final StreamDialogEntry.StreamDialogEntryAction action; + StreamDialogDefaultEntry(@StringRes final int resource, + @NonNull final StreamDialogEntry.StreamDialogEntryAction action) { + this.resource = resource; + this.action = action; + } + + @NonNull + public StreamDialogEntry toStreamDialogEntry() { + return new StreamDialogEntry(resource, action); + } + + ///////////////////////////////////////////// + // private method to open channel fragment // + ///////////////////////////////////////////// + + private static void openChannelFragment(@NonNull final Fragment fragment, + @NonNull final StreamInfoItem item, + final String uploaderUrl) { + // For some reason `getParentFragmentManager()` doesn't work, but this does. + NavigationHelper.openChannelFragment( + fragment.requireActivity().getSupportFragmentManager(), + item.getServiceId(), uploaderUrl, item.getUploaderName()); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java b/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java index 1b4c8046c..bb59d0f29 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java +++ b/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java @@ -1,238 +1,36 @@ package org.schabi.newpipe.util; import android.content.Context; -import android.net.Uri; -import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; import androidx.fragment.app.Fragment; -import androidx.preference.PreferenceManager; -import org.schabi.newpipe.R; -import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.extractor.stream.StreamInfoItem; -import org.schabi.newpipe.extractor.stream.StreamType; -import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; -import org.schabi.newpipe.local.dialog.PlaylistDialog; -import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.player.playqueue.SinglePlayQueue; -import org.schabi.newpipe.util.external_communication.KoreUtils; -import org.schabi.newpipe.util.external_communication.ShareUtils; -import java.util.Collections; -import java.util.List; import java.util.function.Consumer; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.schedulers.Schedulers; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; -public enum StreamDialogEntry { - ////////////////////////////////////// - // enum values with DEFAULT actions // - ////////////////////////////////////// +public class StreamDialogEntry { - show_channel_details(R.string.show_channel_details, (fragment, item) -> { - SaveUploaderUrlHelper.saveUploaderUrlIfNeeded(fragment, item, - uploaderUrl -> openChannelFragment(fragment, item, uploaderUrl)); - }), + @StringRes + public final int resource; + @NonNull + public final StreamDialogEntryAction action; - /** - * Enqueues the stream automatically to the current PlayerType.
- *
- * Info: Add this entry within showStreamDialog. - */ - enqueue(R.string.enqueue_stream, (fragment, item) -> { - fetchItemInfoIfSparse(fragment, item, fullItem -> - NavigationHelper.enqueueOnPlayer(fragment.getContext(), fullItem)); - }), - - enqueue_next(R.string.enqueue_next_stream, (fragment, item) -> { - fetchItemInfoIfSparse(fragment, item, fullItem -> - NavigationHelper.enqueueNextOnPlayer(fragment.getContext(), fullItem)); - }), - - start_here_on_background(R.string.start_here_on_background, (fragment, item) -> { - fetchItemInfoIfSparse(fragment, item, fullItem -> - NavigationHelper.playOnBackgroundPlayer(fragment.getContext(), fullItem, true)); - }), - - start_here_on_popup(R.string.start_here_on_popup, (fragment, item) -> { - fetchItemInfoIfSparse(fragment, item, fullItem -> - NavigationHelper.playOnPopupPlayer(fragment.getContext(), fullItem, true)); - }), - - set_as_playlist_thumbnail(R.string.set_as_playlist_thumbnail, (fragment, item) -> { - }), // has to be set manually - - delete(R.string.delete, (fragment, item) -> { - }), // has to be set manually - - append_playlist(R.string.add_to_playlist, (fragment, item) -> { - PlaylistDialog.createCorrespondingDialog( - fragment.getContext(), - Collections.singletonList(new StreamEntity(item)), - dialog -> dialog.show( - fragment.getParentFragmentManager(), - "StreamDialogEntry@" - + (dialog instanceof PlaylistAppendDialog ? "append" : "create") - + "_playlist" - ) - ); - }), - - play_with_kodi(R.string.play_with_kodi_title, (fragment, item) -> { - final Uri videoUrl = Uri.parse(item.getUrl()); - try { - NavigationHelper.playWithKore(fragment.requireContext(), videoUrl); - } catch (final Exception e) { - KoreUtils.showInstallKoreDialog(fragment.requireActivity()); - } - }), - - share(R.string.share, (fragment, item) -> - ShareUtils.shareText(fragment.requireContext(), item.getName(), item.getUrl(), - item.getThumbnailUrl())), - - open_in_browser(R.string.open_in_browser, (fragment, item) -> - ShareUtils.openUrlInBrowser(fragment.requireContext(), item.getUrl())), - - - mark_as_watched(R.string.mark_as_watched, (fragment, item) -> { - new HistoryRecordManager(fragment.getContext()) - .markAsWatched(item) - .onErrorComplete() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(); - }); - - /////////////// - // variables // - /////////////// - - private static StreamDialogEntry[] enabledEntries; - private final int resource; - private final StreamDialogEntryAction defaultAction; - private StreamDialogEntryAction customAction; - - StreamDialogEntry(final int resource, final StreamDialogEntryAction defaultAction) { + public StreamDialogEntry(@StringRes final int resource, + @NonNull final StreamDialogEntryAction action) { this.resource = resource; - this.defaultAction = defaultAction; - this.customAction = null; + this.action = action; } - - /////////////////////////////////////////////////////// - // non-static methods to initialize and edit entries // - /////////////////////////////////////////////////////// - - public static void setEnabledEntries(final List entries) { - setEnabledEntries(entries.toArray(new StreamDialogEntry[0])); - } - - /** - * To be called before using {@link #setCustomAction(StreamDialogEntryAction)}. - * - * @param entries the entries to be enabled - */ - public static void setEnabledEntries(final StreamDialogEntry... entries) { - // cleanup from last time StreamDialogEntry was used - for (final StreamDialogEntry streamDialogEntry : values()) { - streamDialogEntry.customAction = null; - } - - enabledEntries = entries; - } - - public static String[] getCommands(final Context context) { - final String[] commands = new String[enabledEntries.length]; - for (int i = 0; i != enabledEntries.length; ++i) { - commands[i] = context.getResources().getString(enabledEntries[i].resource); - } - - return commands; - } - - - //////////////////////////////////////////////// - // static methods that act on enabled entries // - //////////////////////////////////////////////// - - public static void clickOn(final int which, final Fragment fragment, - final StreamInfoItem infoItem) { - if (enabledEntries[which].customAction == null) { - enabledEntries[which].defaultAction.onClick(fragment, infoItem); - } else { - enabledEntries[which].customAction.onClick(fragment, infoItem); - } - } - - /** - * Can be used after {@link #setEnabledEntries(StreamDialogEntry...)} has been called. - * - * @param action the action to be set - */ - public void setCustomAction(final StreamDialogEntryAction action) { - this.customAction = action; + public String getString(@NonNull final Context context) { + return context.getString(resource); } public interface StreamDialogEntryAction { void onClick(Fragment fragment, StreamInfoItem infoItem); } - - public static boolean shouldAddMarkAsWatched(final StreamType streamType, - final Context context) { - final boolean isWatchHistoryEnabled = PreferenceManager - .getDefaultSharedPreferences(context) - .getBoolean(context.getString(R.string.enable_watch_history_key), false); - return streamType != StreamType.AUDIO_LIVE_STREAM - && streamType != StreamType.LIVE_STREAM - && isWatchHistoryEnabled; - } - - ///////////////////////////////////////////// - // private method to open channel fragment // - ///////////////////////////////////////////// - - private static void openChannelFragment(final Fragment fragment, - final StreamInfoItem item, - final String uploaderUrl) { - // For some reason `getParentFragmentManager()` doesn't work, but this does. - NavigationHelper.openChannelFragment( - fragment.requireActivity().getSupportFragmentManager(), - item.getServiceId(), uploaderUrl, item.getUploaderName()); - } - - ///////////////////////////////////////////// - // helper functions // - ///////////////////////////////////////////// - - private static void fetchItemInfoIfSparse(final Fragment fragment, - final StreamInfoItem item, - final Consumer callback) { - if (!(item.getStreamType() == StreamType.LIVE_STREAM - || item.getStreamType() == StreamType.AUDIO_LIVE_STREAM) - && item.getDuration() < 0) { - // Sparse item: fetched by fast fetch - ExtractorHelper.getStreamInfo( - item.getServiceId(), - item.getUrl(), - false - ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> { - final HistoryRecordManager recordManager = - new HistoryRecordManager(fragment.getContext()); - recordManager.saveStreamState(result, 0) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .doOnError(throwable -> Log.e("StreamDialogEntry", - throwable.toString())) - .subscribe(); - - callback.accept(new SinglePlayQueue(result)); - }, throwable -> Log.e("StreamDialogEntry", throwable.toString())); - } else { - callback.accept(new SinglePlayQueue(item)); - } - } } From 1cd3ef5dba37a7b7a3358d5c47a80237cf3f85dc Mon Sep 17 00:00:00 2001 From: TobiGr Date: Mon, 27 Dec 2021 16:16:36 +0100 Subject: [PATCH 02/11] Extract entries into beginning and end category --- .../fragments/list/BaseListFragment.java | 13 ++------ .../list/playlist/PlaylistFragment.java | 12 ++----- .../newpipe/info_list/InfoItemDialog.java | 32 ++++++++++++++----- .../schabi/newpipe/local/feed/FeedFragment.kt | 13 ++------ .../history/StatisticsPlaylistFragment.java | 17 +++------- .../local/playlist/LocalPlaylistFragment.java | 13 +++----- 6 files changed, 39 insertions(+), 61 deletions(-) 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 ec9e34964..1129baaec 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 @@ -35,7 +35,6 @@ import org.schabi.newpipe.info_list.InfoListAdapter; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; import org.schabi.newpipe.util.StateSaver; -import org.schabi.newpipe.util.StreamDialogDefaultEntry; import org.schabi.newpipe.views.SuperScrollLayoutManager; import java.util.List; @@ -413,16 +412,8 @@ public abstract class BaseListFragment extends BaseStateFragment final InfoItemDialog.Builder dialogBuilder = new InfoItemDialog.Builder( activity, this, item); - dialogBuilder.addEnqueueEntriesIfNeeded(); - dialogBuilder.addStartHereEntries(); - dialogBuilder.addAllEntries( - StreamDialogDefaultEntry.APPEND_PLAYLIST, - StreamDialogDefaultEntry.SHARE, - StreamDialogDefaultEntry.OPEN_IN_BROWSER - ); - dialogBuilder.addPlayWithKodiEntryIfNeeded(); - dialogBuilder.addMarkAsWatchedEntryIfNeeded(item.getStreamType()); - dialogBuilder.addChannelDetailsEntryIfPossible(); + dialogBuilder.addDefaultEntriesAtBeginning(); + dialogBuilder.addDefaultEntriesAtEnd(); dialogBuilder.create().show(); } 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 d90ea4e8c..26498f61a 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 @@ -145,16 +145,8 @@ public class PlaylistFragment extends BaseListInfoFragment { final InfoItemDialog.Builder dialogBuilder = new InfoItemDialog.Builder( activity, this, item); - dialogBuilder.addEnqueueEntriesIfNeeded(); - dialogBuilder.addStartHereEntries(); - dialogBuilder.addAllEntries( - StreamDialogDefaultEntry.APPEND_PLAYLIST, - StreamDialogDefaultEntry.SHARE, - StreamDialogDefaultEntry.OPEN_IN_BROWSER - ); - dialogBuilder.addPlayWithKodiEntryIfNeeded(); - dialogBuilder.addMarkAsWatchedEntryIfNeeded(item.getStreamType()); - dialogBuilder.addChannelDetailsEntryIfPossible(); + dialogBuilder.addDefaultEntriesAtBeginning(); + dialogBuilder.addDefaultEntriesAtEnd(); dialogBuilder.setAction(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND, (fragment, infoItem) -> NavigationHelper.playOnBackgroundPlayer( 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 index b5b17ab3e..fb7d40a3a 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java @@ -76,16 +76,16 @@ public final class InfoItemDialog { */ public static class Builder { @NonNull private final Activity activity; - @NonNull private final StreamInfoItem info; + @NonNull private final StreamInfoItem item; @NonNull private final Fragment fragment; @NonNull private final List entries = new ArrayList<>(); public Builder(@NonNull final Activity activity, @NonNull final Fragment fragment, - @NonNull final StreamInfoItem info) { + @NonNull final StreamInfoItem item) { this.activity = activity; this.fragment = fragment; - this.info = info; + this.item = item; } public void addEntry(@NonNull final StreamDialogDefaultEntry entry) { @@ -108,7 +108,7 @@ public final class InfoItemDialog { } public void addChannelDetailsEntryIfPossible() { - if (!isNullOrEmpty(info.getUploaderUrl())) { + if (!isNullOrEmpty(item.getUploaderUrl())) { addEntry(StreamDialogDefaultEntry.SHOW_CHANNEL_DETAILS); } } @@ -125,8 +125,8 @@ public final class InfoItemDialog { public void addStartHereEntries() { addEntry(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND); - if (info.getStreamType() != StreamType.AUDIO_STREAM - && info.getStreamType() != StreamType.AUDIO_LIVE_STREAM) { + if (item.getStreamType() != StreamType.AUDIO_STREAM + && item.getStreamType() != StreamType.AUDIO_LIVE_STREAM) { addEntry(StreamDialogDefaultEntry.START_HERE_ON_POPUP); } } @@ -148,17 +148,33 @@ public final class InfoItemDialog { } public void addPlayWithKodiEntryIfNeeded() { - if (KoreUtils.shouldShowPlayWithKodi(activity, info.getServiceId())) { + if (KoreUtils.shouldShowPlayWithKodi(activity, item.getServiceId())) { addEntry(StreamDialogDefaultEntry.PLAY_WITH_KODI); } } + public void addDefaultEntriesAtBeginning() { + addEnqueueEntriesIfNeeded(); + addStartHereEntries(); + } + + public void addDefaultEntriesAtEnd() { + addAllEntries( + StreamDialogDefaultEntry.APPEND_PLAYLIST, + StreamDialogDefaultEntry.SHARE, + StreamDialogDefaultEntry.OPEN_IN_BROWSER + ); + addPlayWithKodiEntryIfNeeded(); + addMarkAsWatchedEntryIfNeeded(item.getStreamType()); + addChannelDetailsEntryIfPossible(); + } + /** * Creates the {@link InfoItemDialog}. * @return a new instance of {@link InfoItemDialog} */ public InfoItemDialog create() { - return new InfoItemDialog(this.activity, this.fragment, this.info, this.entries); + return new InfoItemDialog(this.activity, this.fragment, this.item, this.entries); } } } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt index 78e765609..bda829907 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt @@ -80,7 +80,6 @@ import org.schabi.newpipe.local.subscription.SubscriptionManager import org.schabi.newpipe.util.DeviceUtils import org.schabi.newpipe.util.Localization import org.schabi.newpipe.util.NavigationHelper -import org.schabi.newpipe.util.StreamDialogDefaultEntry import org.schabi.newpipe.util.ThemeHelper.getGridSpanCountStreams import org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout import java.time.OffsetDateTime @@ -360,16 +359,8 @@ class FeedFragment : BaseStateFragment() { val dialogBuilder = InfoItemDialog.Builder(activity, this, item) - dialogBuilder.addEnqueueEntriesIfNeeded() - dialogBuilder.addStartHereEntries() - dialogBuilder.addAllEntries( - StreamDialogDefaultEntry.APPEND_PLAYLIST, - StreamDialogDefaultEntry.SHARE, - StreamDialogDefaultEntry.OPEN_IN_BROWSER - ) - dialogBuilder.addPlayWithKodiEntryIfNeeded() - dialogBuilder.addMarkAsWatchedEntryIfNeeded(item.streamType) - dialogBuilder.addChannelDetailsEntryIfPossible() + dialogBuilder.addDefaultEntriesAtBeginning() + dialogBuilder.addDefaultEntriesAtEnd() dialogBuilder.create().show() } diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java index a11f1a589..542f7c631 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java @@ -335,19 +335,12 @@ public class StatisticsPlaylistFragment final InfoItemDialog.Builder dialogBuilder = new InfoItemDialog.Builder( activity, this, infoItem); - dialogBuilder.addEnqueueEntriesIfNeeded(); - dialogBuilder.addStartHereEntries(); - dialogBuilder.addAllEntries( - StreamDialogDefaultEntry.DELETE, - StreamDialogDefaultEntry.APPEND_PLAYLIST, - StreamDialogDefaultEntry.SHARE, - StreamDialogDefaultEntry.OPEN_IN_BROWSER - ); - dialogBuilder.addPlayWithKodiEntryIfNeeded(); - dialogBuilder.addMarkAsWatchedEntryIfNeeded(infoItem.getStreamType()); - dialogBuilder.addChannelDetailsEntryIfPossible(); - + // set entries + dialogBuilder.addDefaultEntriesAtBeginning(); + dialogBuilder.addEntry(StreamDialogDefaultEntry.DELETE); + dialogBuilder.addDefaultEntriesAtEnd(); + // set custom actions dialogBuilder.setAction(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND, (fragment, infoItemDuplicate) -> NavigationHelper .playOnBackgroundPlayer(context, getPlayQueueStartingAt(item), true)); diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index 088577b25..f1f94eaac 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -750,18 +750,13 @@ public class LocalPlaylistFragment extends BaseLocalListFragment Date: Thu, 23 Dec 2021 00:33:34 +0100 Subject: [PATCH 03/11] Add default entries automatically --- .../fragments/list/BaseListFragment.java | 8 +-- .../list/playlist/PlaylistFragment.java | 3 - .../newpipe/info_list/InfoItemDialog.java | 62 ++++++++++++++----- .../schabi/newpipe/local/feed/FeedFragment.kt | 7 +-- .../history/StatisticsPlaylistFragment.java | 4 +- .../local/playlist/LocalPlaylistFragment.java | 6 +- 6 files changed, 53 insertions(+), 37 deletions(-) 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 1129baaec..4b5c260e0 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 @@ -409,13 +409,7 @@ public abstract class BaseListFragment extends BaseStateFragment return; } - final InfoItemDialog.Builder dialogBuilder = new InfoItemDialog.Builder( - activity, this, item); - - dialogBuilder.addDefaultEntriesAtBeginning(); - dialogBuilder.addDefaultEntriesAtEnd(); - - dialogBuilder.create().show(); + new InfoItemDialog.Builder(activity, this, item).create().show(); } /*////////////////////////////////////////////////////////////////////////// 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 26498f61a..14d77b31d 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 @@ -145,9 +145,6 @@ public class PlaylistFragment extends BaseListInfoFragment { final InfoItemDialog.Builder dialogBuilder = new InfoItemDialog.Builder( activity, this, item); - dialogBuilder.addDefaultEntriesAtBeginning(); - dialogBuilder.addDefaultEntriesAtEnd(); - dialogBuilder.setAction(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND, (fragment, infoItem) -> NavigationHelper.playOnBackgroundPlayer( context, getPlayQueueStartingAt(infoItem), true)); 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 index fb7d40a3a..11489621d 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java @@ -76,16 +76,39 @@ public final class InfoItemDialog { */ public static class Builder { @NonNull private final Activity activity; - @NonNull private final StreamInfoItem item; + @NonNull private final StreamInfoItem infoItem; @NonNull private final Fragment fragment; @NonNull private final List entries = new ArrayList<>(); + private final boolean addDefaultEntriesAutomatically; public Builder(@NonNull final Activity activity, @NonNull final Fragment fragment, - @NonNull final StreamInfoItem item) { + @NonNull final StreamInfoItem infoItem) { + this(activity, fragment, infoItem, true); + } + + /** + *

Create an instance of this Builder

+ * @param activity + * @param fragment + * @param infoItem + * @param addDefaultEntriesAutomatically whether default entries added with + * {@link #addDefaultEntriesAtBeginning()} and + * {@link #addDefaultEntriesAtEnd()} + * are added automatically when generating + * the {@link InfoItemDialog}. + */ + public Builder(@NonNull final Activity activity, + @NonNull final Fragment fragment, + @NonNull final StreamInfoItem infoItem, + final boolean addDefaultEntriesAutomatically) { this.activity = activity; this.fragment = fragment; - this.item = item; + this.infoItem = infoItem; + this.addDefaultEntriesAutomatically = addDefaultEntriesAutomatically; + if (addDefaultEntriesAutomatically) { + addDefaultEntriesAtBeginning(); + } } public void addEntry(@NonNull final StreamDialogDefaultEntry entry) { @@ -98,17 +121,26 @@ public final class InfoItemDialog { } } + /** + *

Change an entries' action that is called when the entry is selected.

+ *

Warning: Only use this method when the entry has been already added. + * Changing the action of an entry which has not been added to the Builder yet + * does not have an effect.

+ * @param entry the entry to change + * @param action the action to perform when the entry is selected + */ public void setAction(@NonNull final StreamDialogDefaultEntry entry, @NonNull final StreamDialogEntry.StreamDialogEntryAction action) { for (int i = 0; i < entries.size(); i++) { if (entries.get(i).resource == entry.resource) { entries.set(i, new StreamDialogEntry(entry.resource, action)); + return; } } } public void addChannelDetailsEntryIfPossible() { - if (!isNullOrEmpty(item.getUploaderUrl())) { + if (!isNullOrEmpty(infoItem.getUploaderUrl())) { addEntry(StreamDialogDefaultEntry.SHOW_CHANNEL_DETAILS); } } @@ -125,8 +157,8 @@ public final class InfoItemDialog { public void addStartHereEntries() { addEntry(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND); - if (item.getStreamType() != StreamType.AUDIO_STREAM - && item.getStreamType() != StreamType.AUDIO_LIVE_STREAM) { + if (infoItem.getStreamType() != StreamType.AUDIO_STREAM + && infoItem.getStreamType() != StreamType.AUDIO_LIVE_STREAM) { addEntry(StreamDialogDefaultEntry.START_HERE_ON_POPUP); } } @@ -134,21 +166,20 @@ public final class InfoItemDialog { /** * Adds {@link StreamDialogDefaultEntry.MARK_AS_WATCHED} if the watch history is enabled * and the stream is not a livestream. - * @param streamType the item's stream type */ - public void addMarkAsWatchedEntryIfNeeded(final StreamType streamType) { + public void addMarkAsWatchedEntryIfNeeded() { final boolean isWatchHistoryEnabled = PreferenceManager .getDefaultSharedPreferences(activity) .getBoolean(activity.getString(R.string.enable_watch_history_key), false); - if (streamType != StreamType.AUDIO_LIVE_STREAM - && streamType != StreamType.LIVE_STREAM - && isWatchHistoryEnabled) { + if (isWatchHistoryEnabled + && infoItem.getStreamType() != StreamType.LIVE_STREAM + && infoItem.getStreamType() != StreamType.AUDIO_LIVE_STREAM) { addEntry(StreamDialogDefaultEntry.MARK_AS_WATCHED); } } public void addPlayWithKodiEntryIfNeeded() { - if (KoreUtils.shouldShowPlayWithKodi(activity, item.getServiceId())) { + if (KoreUtils.shouldShowPlayWithKodi(activity, infoItem.getServiceId())) { addEntry(StreamDialogDefaultEntry.PLAY_WITH_KODI); } } @@ -165,7 +196,7 @@ public final class InfoItemDialog { StreamDialogDefaultEntry.OPEN_IN_BROWSER ); addPlayWithKodiEntryIfNeeded(); - addMarkAsWatchedEntryIfNeeded(item.getStreamType()); + addMarkAsWatchedEntryIfNeeded(); addChannelDetailsEntryIfPossible(); } @@ -174,7 +205,10 @@ public final class InfoItemDialog { * @return a new instance of {@link InfoItemDialog} */ public InfoItemDialog create() { - return new InfoItemDialog(this.activity, this.fragment, this.item, this.entries); + if (addDefaultEntriesAutomatically) { + addDefaultEntriesAtEnd(); + } + return new InfoItemDialog(this.activity, this.fragment, this.infoItem, this.entries); } } } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt index bda829907..63e918739 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt @@ -357,12 +357,7 @@ class FeedFragment : BaseStateFragment() { val activity: Activity? = getActivity() if (context == null || context.resources == null || activity == null) return - val dialogBuilder = InfoItemDialog.Builder(activity, this, item) - - dialogBuilder.addDefaultEntriesAtBeginning() - dialogBuilder.addDefaultEntriesAtEnd() - - dialogBuilder.create().show() + InfoItemDialog.Builder(activity, this, item).create().show() } private val listenerStreamItem = object : OnItemClickListener, OnItemLongClickListener { diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java index 542f7c631..8567f3804 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java @@ -335,10 +335,8 @@ public class StatisticsPlaylistFragment final InfoItemDialog.Builder dialogBuilder = new InfoItemDialog.Builder( activity, this, infoItem); - // set entries - dialogBuilder.addDefaultEntriesAtBeginning(); + // set entries in the middle; the others are added automatically dialogBuilder.addEntry(StreamDialogDefaultEntry.DELETE); - dialogBuilder.addDefaultEntriesAtEnd(); // set custom actions dialogBuilder.setAction(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND, diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index f1f94eaac..bd6957cd0 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -750,15 +750,13 @@ public class LocalPlaylistFragment extends BaseLocalListFragment NavigationHelper.playOnBackgroundPlayer( context, getPlayQueueStartingAt(item), true)); From 962fe9c36d6ee83235da757533d6119ed610790c Mon Sep 17 00:00:00 2001 From: TobiGr Date: Sun, 26 Dec 2021 15:34:36 +0100 Subject: [PATCH 04/11] Use Context instead of Activity Improve docs --- .../fragments/list/BaseListFragment.java | 2 +- .../list/playlist/PlaylistFragment.java | 4 +- .../newpipe/info_list/InfoItemDialog.java | 130 ++++++++++++++---- .../schabi/newpipe/local/feed/FeedFragment.kt | 2 +- .../history/StatisticsPlaylistFragment.java | 4 +- .../local/playlist/LocalPlaylistFragment.java | 4 +- .../util/StreamDialogDefaultEntry.java | 28 ++-- .../external_communication/KoreUtils.java | 4 + 8 files changed, 138 insertions(+), 40 deletions(-) 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 4b5c260e0..199b8b2da 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 @@ -409,7 +409,7 @@ public abstract class BaseListFragment extends BaseStateFragment return; } - new InfoItemDialog.Builder(activity, this, item).create().show(); + new InfoItemDialog.Builder(activity, context, this, item).create().show(); } /*////////////////////////////////////////////////////////////////////////// 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 14d77b31d..3485678d6 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 @@ -142,8 +142,8 @@ public class PlaylistFragment extends BaseListInfoFragment { return; } - final InfoItemDialog.Builder dialogBuilder = new InfoItemDialog.Builder( - activity, this, item); + final InfoItemDialog.Builder dialogBuilder = + new InfoItemDialog.Builder(activity, context, this, item); dialogBuilder.setAction(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND, (fragment, infoItem) -> NavigationHelper.playOnBackgroundPlayer( 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 index 11489621d..6db66ef14 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java @@ -1,8 +1,7 @@ package org.schabi.newpipe.info_list; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; - import android.app.Activity; +import android.content.Context; import android.content.DialogInterface; import android.view.View; import android.widget.TextView; @@ -24,10 +23,18 @@ import java.util.ArrayList; import java.util.List; /** - * Dialog with actions for a {@link StreamInfoItem}. + * Dialog for a {@link StreamInfoItem}. + * The dialog'S content are actions that can be performed on the {@link StreamInfoItem}. * This dialog is mostly used for longpress context menus. */ public final class InfoItemDialog { + /** + * Ideally, {@link InfoItemDialog} would extend {@link AlertDialog}. + * However, extending {@link AlertDialog} requires many additional lines + * and brings more complexity to this class, especially the constructor. + * To circumvent this, an {@link AlertDialog.Builder} is used in the constructor. + * Its result is stored in this class variable to allow access via the {@link #show()} method. + */ private final AlertDialog dialog; private InfoItemDialog(@NonNull final Activity activity, @@ -76,38 +83,100 @@ public final class InfoItemDialog { */ public static class Builder { @NonNull private final Activity activity; + @NonNull private final Context context; @NonNull private final StreamInfoItem infoItem; @NonNull private final Fragment fragment; @NonNull private final List entries = new ArrayList<>(); private final boolean addDefaultEntriesAutomatically; + /** + *

Create a Builder instance that automatically adds the some default entries + * at the top and bottom of the dialog.

+ * The dialog has the following structure: + *
+         *     + - - - - - - - - - - - - - - - - - - - - - -+
+         *     | ENQUEUE                                    |
+         *     | ENQUEUE_HERE                               |
+         *     | START_ON_BACKGROUND                        |
+         *     | START_ON_POPUP                             |
+         *     + - - - - - - - - - - - - - - - - - - - - - -+
+         *     | entries added manually with                |
+         *     | addEntry() and addAllEntries()             |
+         *     + - - - - - - - - - - - - - - - - - - - - - -+
+         *     | APPEND_PLAYLIST                            |
+         *     | SHARE                                      |
+         *     | OPEN_IN_BROWSER                            |
+         *     | PLAY_WITH_KODI                             |
+         *     | MARK_AS_WATCHED                            |
+         *     | SHOW_CHANNEL_DETAILS                       |
+         *     + - - - - - - - - - - - - - - - - - - - - - -+
+         * 
+ * Please note that some entries are not added depending on the user's preferences, + * the item's {@link StreamType} and the current player state. + * + * @param activity + * @param context + * @param fragment + * @param infoItem the item for this dialog; all entries and their actions work with + * this {@link org.schabi.newpipe.extractor.InfoItem} + */ public Builder(@NonNull final Activity activity, + @NonNull final Context context, @NonNull final Fragment fragment, @NonNull final StreamInfoItem infoItem) { - this(activity, fragment, infoItem, true); + this(activity, context, fragment, infoItem, true); } /** - *

Create an instance of this Builder

+ *

Create an instance of this Builder.

+ *

If {@code addDefaultEntriesAutomatically} is set to {@code true}, + * some default entries are added to the top and bottom of the dialog.

+ * The dialog has the following structure: + *
+         *     + - - - - - - - - - - - - - - - - - - - - - -+
+         *     | ENQUEUE                                    |
+         *     | ENQUEUE_HERE                               |
+         *     | START_ON_BACKGROUND                        |
+         *     | START_ON_POPUP                             |
+         *     + - - - - - - - - - - - - - - - - - - - - - -+
+         *     | entries added manually with                |
+         *     | addEntry() and addAllEntries()             |
+         *     + - - - - - - - - - - - - - - - - - - - - - -+
+         *     | APPEND_PLAYLIST                            |
+         *     | SHARE                                      |
+         *     | OPEN_IN_BROWSER                            |
+         *     | PLAY_WITH_KODI                             |
+         *     | MARK_AS_WATCHED                            |
+         *     | SHOW_CHANNEL_DETAILS                       |
+         *     + - - - - - - - - - - - - - - - - - - - - - -+
+         * 
+ * Please note that some entries are not added depending on the user's preferences, + * the item's {@link StreamType} and the current player state. + * * @param activity + * @param context * @param fragment * @param infoItem - * @param addDefaultEntriesAutomatically whether default entries added with - * {@link #addDefaultEntriesAtBeginning()} and - * {@link #addDefaultEntriesAtEnd()} - * are added automatically when generating - * the {@link InfoItemDialog}. + * @param addDefaultEntriesAutomatically + * whether default entries added with {@link #addDefaultBeginningEntries()} + * and {@link #addDefaultEndEntries()} are added automatically when generating + * the {@link InfoItemDialog}. + *
+ * Entries added with {@link #addEntry(StreamDialogDefaultEntry)} and + * {@link #addAllEntries(StreamDialogDefaultEntry...)} are added in between. */ public Builder(@NonNull final Activity activity, + @NonNull final Context context, @NonNull final Fragment fragment, @NonNull final StreamInfoItem infoItem, final boolean addDefaultEntriesAutomatically) { this.activity = activity; + this.context = context; this.fragment = fragment; this.infoItem = infoItem; this.addDefaultEntriesAutomatically = addDefaultEntriesAutomatically; if (addDefaultEntriesAutomatically) { - addDefaultEntriesAtBeginning(); + addDefaultBeginningEntries(); } } @@ -139,12 +208,11 @@ public final class InfoItemDialog { } } - public void addChannelDetailsEntryIfPossible() { - if (!isNullOrEmpty(infoItem.getUploaderUrl())) { - addEntry(StreamDialogDefaultEntry.SHOW_CHANNEL_DETAILS); - } - } - + /** + * Adds {@link StreamDialogDefaultEntry#ENQUEUE} if the player is open and + * {@link StreamDialogDefaultEntry#ENQUEUE_NEXT} if there are multiple streams + * in the play queue. + */ public void addEnqueueEntriesIfNeeded() { if (PlayerHolder.getInstance().isPlayerOpen()) { addEntry(StreamDialogDefaultEntry.ENQUEUE); @@ -155,6 +223,11 @@ public final class InfoItemDialog { } } + /** + * Adds the {@link StreamDialogDefaultEntry#START_HERE_ON_BACKGROUND}. + * If the {@link #infoItem} is not a pure audio (live) stream, + * {@link StreamDialogDefaultEntry#START_HERE_ON_POPUP} is added, too. + */ public void addStartHereEntries() { addEntry(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND); if (infoItem.getStreamType() != StreamType.AUDIO_STREAM @@ -169,8 +242,8 @@ public final class InfoItemDialog { */ public void addMarkAsWatchedEntryIfNeeded() { final boolean isWatchHistoryEnabled = PreferenceManager - .getDefaultSharedPreferences(activity) - .getBoolean(activity.getString(R.string.enable_watch_history_key), false); + .getDefaultSharedPreferences(context) + .getBoolean(context.getString(R.string.enable_watch_history_key), false); if (isWatchHistoryEnabled && infoItem.getStreamType() != StreamType.LIVE_STREAM && infoItem.getStreamType() != StreamType.AUDIO_LIVE_STREAM) { @@ -179,17 +252,26 @@ public final class InfoItemDialog { } public void addPlayWithKodiEntryIfNeeded() { - if (KoreUtils.shouldShowPlayWithKodi(activity, infoItem.getServiceId())) { + if (KoreUtils.shouldShowPlayWithKodi(context, infoItem.getServiceId())) { addEntry(StreamDialogDefaultEntry.PLAY_WITH_KODI); } } - public void addDefaultEntriesAtBeginning() { + /** + * Add the entries which are usually at the top of the action list. + *
+ * This method adds the "enqueue" (see {@link #addEnqueueEntriesIfNeeded()}) + * and "start here" (see {@link #addStartHereEntries()} entries. + */ + public void addDefaultBeginningEntries() { addEnqueueEntriesIfNeeded(); addStartHereEntries(); } - public void addDefaultEntriesAtEnd() { + /** + * Add the entries which are usually at the bottom of the action list. + */ + public void addDefaultEndEntries() { addAllEntries( StreamDialogDefaultEntry.APPEND_PLAYLIST, StreamDialogDefaultEntry.SHARE, @@ -197,7 +279,7 @@ public final class InfoItemDialog { ); addPlayWithKodiEntryIfNeeded(); addMarkAsWatchedEntryIfNeeded(); - addChannelDetailsEntryIfPossible(); + addEntry(StreamDialogDefaultEntry.SHOW_CHANNEL_DETAILS); } /** @@ -206,7 +288,7 @@ public final class InfoItemDialog { */ public InfoItemDialog create() { if (addDefaultEntriesAutomatically) { - addDefaultEntriesAtEnd(); + addDefaultEndEntries(); } return new InfoItemDialog(this.activity, this.fragment, this.infoItem, this.entries); } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt index 63e918739..5c1c78902 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt @@ -357,7 +357,7 @@ class FeedFragment : BaseStateFragment() { val activity: Activity? = getActivity() if (context == null || context.resources == null || activity == null) return - InfoItemDialog.Builder(activity, this, item).create().show() + InfoItemDialog.Builder(activity, context, this, item).create().show() } private val listenerStreamItem = object : OnItemClickListener, OnItemLongClickListener { diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java index 8567f3804..b33a5b5af 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java @@ -332,8 +332,8 @@ public class StatisticsPlaylistFragment } final StreamInfoItem infoItem = item.toStreamInfoItem(); - final InfoItemDialog.Builder dialogBuilder = new InfoItemDialog.Builder( - activity, this, infoItem); + final InfoItemDialog.Builder dialogBuilder = + new InfoItemDialog.Builder(activity, context, this, infoItem); // set entries in the middle; the others are added automatically dialogBuilder.addEntry(StreamDialogDefaultEntry.DELETE); diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index bd6957cd0..a46370f8c 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -747,8 +747,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment + * This enum provides entries that are accepted + * by the {@link org.schabi.newpipe.info_list.InfoItemDialog.Builder}. + *

+ *

+ * These entries contain a String {@link #resource} which is displayed in the dialog and + * a default {@link #action} that is executed + * when the entry is selected (via onClick()). + *
+ * They action can be overridden by using the Builder's + * {@link org.schabi.newpipe.info_list.InfoItemDialog.Builder#setAction( + * StreamDialogDefaultEntry, StreamDialogEntry.StreamDialogEntryAction)} + * method. + *

+ */ public enum StreamDialogDefaultEntry { - ////////////////////////////////////// - // enum values with DEFAULT actions // - ////////////////////////////////////// - SHOW_CHANNEL_DETAILS(R.string.show_channel_details, (fragment, item) -> { if (isNullOrEmpty(item.getUploaderUrl())) { final int serviceId = item.getServiceId(); @@ -125,6 +140,7 @@ public enum StreamDialogDefaultEntry { public final int resource; @NonNull public final StreamDialogEntry.StreamDialogEntryAction action; + StreamDialogDefaultEntry(@StringRes final int resource, @NonNull final StreamDialogEntry.StreamDialogEntryAction action) { this.resource = resource; @@ -136,10 +152,6 @@ public enum StreamDialogDefaultEntry { return new StreamDialogEntry(resource, action); } - ///////////////////////////////////////////// - // private method to open channel fragment // - ///////////////////////////////////////////// - private static void openChannelFragment(@NonNull final Fragment fragment, @NonNull final StreamInfoItem item, final String uploaderUrl) { diff --git a/app/src/main/java/org/schabi/newpipe/util/external_communication/KoreUtils.java b/app/src/main/java/org/schabi/newpipe/util/external_communication/KoreUtils.java index 6801f24ef..0df579d88 100644 --- a/app/src/main/java/org/schabi/newpipe/util/external_communication/KoreUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/external_communication/KoreUtils.java @@ -10,6 +10,10 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.util.NavigationHelper; +/** + * Util class that provides methods which are related to the Kodi Media Center and its Kore app. + * @see Kodi website + */ public final class KoreUtils { private KoreUtils() { } From ef0d562702932a6728bf2b154ac9b50e1f4bbfd8 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Thu, 23 Dec 2021 14:12:22 +0100 Subject: [PATCH 05/11] Use ErrorActivity to notifiy about errors occourred while loading channel details --- .../newpipe/util/StreamDialogDefaultEntry.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java b/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java index 17b4236dd..e3d26c833 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java +++ b/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java @@ -59,12 +59,14 @@ public enum StreamDialogDefaultEntry { .setUploaderUrl(serviceId, url, result.getUploaderUrl()) .subscribeOn(Schedulers.io()).subscribe(); openChannelFragment(fragment, item, result.getUploaderUrl()); - }, throwable -> Toast.makeText( - // TODO: Open the Error Activity - fragment.getContext(), - R.string.error_show_channel_details, - Toast.LENGTH_SHORT - ).show()); + }, throwable -> ErrorUtil.openActivity( + fragment.requireContext(), + new ErrorInfo( + throwable, + UserAction.REQUESTED_CHANNEL, + url, + serviceId + ))); } else { openChannelFragment(fragment, item, item.getUploaderUrl()); } From 646d8f431c7dc2feb33b9e763f49f938f3e53f83 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Mon, 27 Dec 2021 16:14:26 +0100 Subject: [PATCH 06/11] Use identical method names for creating the InfoItemDialog in Fragments --- .../org/schabi/newpipe/fragments/list/BaseListFragment.java | 4 ++-- .../newpipe/fragments/list/playlist/PlaylistFragment.java | 2 +- .../main/java/org/schabi/newpipe/local/feed/FeedFragment.kt | 4 ++-- .../newpipe/local/history/StatisticsPlaylistFragment.java | 4 ++-- .../schabi/newpipe/local/playlist/LocalPlaylistFragment.java | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) 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 199b8b2da..63adcb64b 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 @@ -261,7 +261,7 @@ public abstract class BaseListFragment extends BaseStateFragment @Override public void held(final StreamInfoItem selectedItem) { - showStreamDialog(selectedItem); + showInfoItemDialog(selectedItem); } }); @@ -402,7 +402,7 @@ public abstract class BaseListFragment extends BaseStateFragment } } - protected void showStreamDialog(final StreamInfoItem item) { + protected void showInfoItemDialog(final StreamInfoItem item) { final Context context = getContext(); final Activity activity = getActivity(); if (context == null || context.getResources() == null || activity == null) { 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 3485678d6..b12cc59fb 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 @@ -135,7 +135,7 @@ public class PlaylistFragment extends BaseListInfoFragment { } @Override - protected void showStreamDialog(final StreamInfoItem item) { + protected void showInfoItemDialog(final StreamInfoItem item) { final Context context = getContext(); final Activity activity = getActivity(); if (context == null || context.getResources() == null || activity == null) { diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt index 5c1c78902..e229a8789 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt @@ -352,7 +352,7 @@ class FeedFragment : BaseStateFragment() { feedBinding.loadingProgressBar.max = progressState.maxProgress } - private fun showStreamDialog(item: StreamInfoItem) { + private fun showInfoItemDialog(item: StreamInfoItem) { val context = context val activity: Activity? = getActivity() if (context == null || context.resources == null || activity == null) return @@ -373,7 +373,7 @@ class FeedFragment : BaseStateFragment() { override fun onItemLongClick(item: Item<*>, view: View): Boolean { if (item is StreamItem && !isRefreshing) { - showStreamDialog(item.streamWithState.stream.toStreamInfoItem()) + showInfoItemDialog(item.streamWithState.stream.toStreamInfoItem()) return true } return false diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java index b33a5b5af..03c922ba4 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java @@ -150,7 +150,7 @@ public class StatisticsPlaylistFragment @Override public void held(final LocalItem selectedItem) { if (selectedItem instanceof StreamStatisticsEntry) { - showStreamDialog((StreamStatisticsEntry) selectedItem); + showInfoItemDialog((StreamStatisticsEntry) selectedItem); } } }); @@ -324,7 +324,7 @@ public class StatisticsPlaylistFragment return getPlayQueue(Math.max(itemListAdapter.getItemsList().indexOf(infoItem), 0)); } - private void showStreamDialog(final StreamStatisticsEntry item) { + private void showInfoItemDialog(final StreamStatisticsEntry item) { final Context context = getContext(); final Activity activity = getActivity(); if (context == null || context.getResources() == null || activity == null) { diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index a46370f8c..2b690ff7b 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -178,7 +178,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment Date: Fri, 18 Feb 2022 23:44:14 +0100 Subject: [PATCH 07/11] Apply feedback Return this in InfoIrtemDialog.Builder methoods. Move null checks for InfoIrtemDialog.Builder into constructor. Fix and add some more docs. --- .../org/schabi/newpipe/error/UserAction.java | 4 +- .../fragments/list/BaseListFragment.java | 13 +-- .../list/playlist/PlaylistFragment.java | 27 +++-- .../newpipe/info_list/InfoItemDialog.java | 103 ++++++++++++++---- .../history/StatisticsPlaylistFragment.java | 37 ++++--- .../local/playlist/LocalPlaylistFragment.java | 52 +++++---- .../util/StreamDialogDefaultEntry.java | 48 ++++---- .../newpipe/util/StreamDialogEntry.java | 67 +++++++++++- 8 files changed, 240 insertions(+), 111 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/error/UserAction.java b/app/src/main/java/org/schabi/newpipe/error/UserAction.java index e8dec9556..d291f0491 100644 --- a/app/src/main/java/org/schabi/newpipe/error/UserAction.java +++ b/app/src/main/java/org/schabi/newpipe/error/UserAction.java @@ -28,8 +28,8 @@ public enum UserAction { DOWNLOAD_FAILED("download failed"), PREFERENCES_MIGRATION("migration of preferences"), SHARE_TO_NEWPIPE("share to newpipe"), - CHECK_FOR_NEW_APP_VERSION("check for new app version"); - + CHECK_FOR_NEW_APP_VERSION("check for new app version"), + OPEN_INFO_ITEM_DIALOG("open info item dialog"); private final String message; 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 63adcb64b..b1fa0059c 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 @@ -3,7 +3,6 @@ package org.schabi.newpipe.fragments.list; import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; -import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; @@ -30,8 +29,8 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; 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.InfoItemDialog; import org.schabi.newpipe.info_list.InfoListAdapter; +import org.schabi.newpipe.info_list.dialog.InfoItemDialog; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; import org.schabi.newpipe.util.StateSaver; @@ -403,13 +402,11 @@ public abstract class BaseListFragment extends BaseStateFragment } protected void showInfoItemDialog(final StreamInfoItem item) { - final Context context = getContext(); - final Activity activity = getActivity(); - if (context == null || context.getResources() == null || activity == null) { - return; + try { + new InfoItemDialog.Builder(getActivity(), getContext(), this, item).create().show(); + } catch (final IllegalArgumentException e) { + InfoItemDialog.Builder.reportErrorDuringInitialization(e, item); } - - new InfoItemDialog.Builder(activity, context, this, item).create().show(); } /*////////////////////////////////////////////////////////////////////////// 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 b12cc59fb..15110183c 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 @@ -3,7 +3,6 @@ package org.schabi.newpipe.fragments.list.playlist; import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; -import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.text.TextUtils; @@ -137,20 +136,20 @@ public class PlaylistFragment extends BaseListInfoFragment { @Override protected void showInfoItemDialog(final StreamInfoItem item) { final Context context = getContext(); - final Activity activity = getActivity(); - if (context == null || context.getResources() == null || activity == null) { - return; + try { + final InfoItemDialog.Builder dialogBuilder = + new InfoItemDialog.Builder(getActivity(), context, this, item); + + dialogBuilder + .setAction( + StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND, + (f, infoItem) -> NavigationHelper.playOnBackgroundPlayer( + context, getPlayQueueStartingAt(infoItem), true)) + .create() + .show(); + } catch (final IllegalArgumentException e) { + InfoItemDialog.Builder.reportErrorDuringInitialization(e, item); } - - final InfoItemDialog.Builder dialogBuilder = - new InfoItemDialog.Builder(activity, context, this, item); - - dialogBuilder.setAction(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND, - (fragment, infoItem) -> NavigationHelper.playOnBackgroundPlayer( - context, getPlayQueueStartingAt(infoItem), true)); - - dialogBuilder.create().show(); - } @Override 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 index 6db66ef14..183b2d8d9 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java @@ -1,8 +1,12 @@ package org.schabi.newpipe.info_list; +import static org.schabi.newpipe.MainActivity.DEBUG; + import android.app.Activity; import android.content.Context; import android.content.DialogInterface; +import android.os.Build; +import android.util.Log; import android.view.View; import android.widget.TextView; @@ -11,7 +15,12 @@ import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.preference.PreferenceManager; +import org.schabi.newpipe.App; import org.schabi.newpipe.R; +import org.schabi.newpipe.error.ErrorInfo; +import org.schabi.newpipe.error.ErrorUtil; +import org.schabi.newpipe.error.UserAction; +import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.player.helper.PlayerHolder; @@ -21,13 +30,15 @@ import org.schabi.newpipe.util.external_communication.KoreUtils; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; /** * Dialog for a {@link StreamInfoItem}. - * The dialog'S content are actions that can be performed on the {@link StreamInfoItem}. + * The dialog's content are actions that can be performed on the {@link StreamInfoItem}. * This dialog is mostly used for longpress context menus. */ public final class InfoItemDialog { + private static final String TAG = Build.class.getSimpleName(); /** * Ideally, {@link InfoItemDialog} would extend {@link AlertDialog}. * However, extending {@link AlertDialog} requires many additional lines @@ -42,6 +53,7 @@ public final class InfoItemDialog { @NonNull final StreamInfoItem info, @NonNull final List entries) { + // Create the dialog's title final View bannerView = View.inflate(activity, R.layout.dialog_title, null); bannerView.setSelected(true); @@ -56,9 +68,11 @@ public final class InfoItemDialog { detailsView.setVisibility(View.GONE); } + // Get the entry's descriptions which are displayed in the dialog final String[] items = entries.stream() .map(entry -> entry.getString(activity)).toArray(String[]::new); + // Call an entry's action / onClick method when the entry is selected. final DialogInterface.OnClickListener action = (d, index) -> entries.get(index).action.onClick(fragment, info); @@ -96,7 +110,7 @@ public final class InfoItemDialog { *
          *     + - - - - - - - - - - - - - - - - - - - - - -+
          *     | ENQUEUE                                    |
-         *     | ENQUEUE_HERE                               |
+         *     | ENQUEUE_NEXT                               |
          *     | START_ON_BACKGROUND                        |
          *     | START_ON_POPUP                             |
          *     + - - - - - - - - - - - - - - - - - - - - - -+
@@ -118,10 +132,12 @@ public final class InfoItemDialog {
          * @param context
          * @param fragment
          * @param infoItem the item for this dialog; all entries and their actions work with
-         *                this {@link org.schabi.newpipe.extractor.InfoItem}
+         *                this {@link StreamInfoItem}
+         * @throws IllegalArgumentException if activity, context
+         *         or resources is null
          */
-        public Builder(@NonNull final Activity activity,
-                       @NonNull final Context context,
+        public Builder(final Activity activity,
+                       final Context context,
                        @NonNull final Fragment fragment,
                        @NonNull final StreamInfoItem infoItem) {
             this(activity, context, fragment, infoItem, true);
@@ -135,7 +151,7 @@ public final class InfoItemDialog {
          * 
          *     + - - - - - - - - - - - - - - - - - - - - - -+
          *     | ENQUEUE                                    |
-         *     | ENQUEUE_HERE                               |
+         *     | ENQUEUE_NEXT                               |
          *     | START_ON_BACKGROUND                        |
          *     | START_ON_POPUP                             |
          *     + - - - - - - - - - - - - - - - - - - - - - -+
@@ -164,12 +180,21 @@ public final class InfoItemDialog {
          *        
* Entries added with {@link #addEntry(StreamDialogDefaultEntry)} and * {@link #addAllEntries(StreamDialogDefaultEntry...)} are added in between. + * @throws IllegalArgumentException if activity, context + * or resources is null */ - public Builder(@NonNull final Activity activity, - @NonNull final Context context, + public Builder(final Activity activity, + final Context context, @NonNull final Fragment fragment, @NonNull final StreamInfoItem infoItem, final boolean addDefaultEntriesAutomatically) { + if (activity == null || context == null || context.getResources() == null) { + if (DEBUG) { + Log.d(TAG, "activity, context or resources is null: activity = " + + activity + ", context = " + context); + } + throw new IllegalArgumentException("activity, context or resources is null"); + } this.activity = activity; this.context = context; this.fragment = fragment; @@ -180,14 +205,24 @@ public final class InfoItemDialog { } } - public void addEntry(@NonNull final StreamDialogDefaultEntry entry) { + /** + * Adds a new entry and appends it to the current entry list. + * @param entry the entry to add + * @return the current {@link Builder} instance + */ + public Builder addEntry(@NonNull final StreamDialogDefaultEntry entry) { entries.add(entry.toStreamDialogEntry()); + return this; } - public void addAllEntries(@NonNull final StreamDialogDefaultEntry... newEntries) { - for (final StreamDialogDefaultEntry entry: newEntries) { - this.entries.add(entry.toStreamDialogEntry()); - } + /** + * Adds new entries. These are appended to the current entry list. + * @param newEntries the entries to add + * @return the current {@link Builder} instance + */ + public Builder addAllEntries(@NonNull final StreamDialogDefaultEntry... newEntries) { + Stream.of(newEntries).forEach(this::addEntry); + return this; } /** @@ -197,23 +232,26 @@ public final class InfoItemDialog { * does not have an effect.

* @param entry the entry to change * @param action the action to perform when the entry is selected + * @return the current {@link Builder} instance */ - public void setAction(@NonNull final StreamDialogDefaultEntry entry, + public Builder setAction(@NonNull final StreamDialogDefaultEntry entry, @NonNull final StreamDialogEntry.StreamDialogEntryAction action) { for (int i = 0; i < entries.size(); i++) { if (entries.get(i).resource == entry.resource) { entries.set(i, new StreamDialogEntry(entry.resource, action)); - return; + return this; } } + return this; } /** * Adds {@link StreamDialogDefaultEntry#ENQUEUE} if the player is open and * {@link StreamDialogDefaultEntry#ENQUEUE_NEXT} if there are multiple streams * in the play queue. + * @return the current {@link Builder} instance */ - public void addEnqueueEntriesIfNeeded() { + public Builder addEnqueueEntriesIfNeeded() { if (PlayerHolder.getInstance().isPlayerOpen()) { addEntry(StreamDialogDefaultEntry.ENQUEUE); @@ -221,26 +259,30 @@ public final class InfoItemDialog { addEntry(StreamDialogDefaultEntry.ENQUEUE_NEXT); } } + return this; } /** * Adds the {@link StreamDialogDefaultEntry#START_HERE_ON_BACKGROUND}. * If the {@link #infoItem} is not a pure audio (live) stream, * {@link StreamDialogDefaultEntry#START_HERE_ON_POPUP} is added, too. + * @return the current {@link Builder} instance */ - public void addStartHereEntries() { + public Builder addStartHereEntries() { addEntry(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND); if (infoItem.getStreamType() != StreamType.AUDIO_STREAM && infoItem.getStreamType() != StreamType.AUDIO_LIVE_STREAM) { addEntry(StreamDialogDefaultEntry.START_HERE_ON_POPUP); } + return this; } /** * Adds {@link StreamDialogDefaultEntry.MARK_AS_WATCHED} if the watch history is enabled * and the stream is not a livestream. + * @return the current {@link Builder} instance */ - public void addMarkAsWatchedEntryIfNeeded() { + public Builder addMarkAsWatchedEntryIfNeeded() { final boolean isWatchHistoryEnabled = PreferenceManager .getDefaultSharedPreferences(context) .getBoolean(context.getString(R.string.enable_watch_history_key), false); @@ -249,12 +291,18 @@ public final class InfoItemDialog { && infoItem.getStreamType() != StreamType.AUDIO_LIVE_STREAM) { addEntry(StreamDialogDefaultEntry.MARK_AS_WATCHED); } + return this; } - public void addPlayWithKodiEntryIfNeeded() { + /** + * Adds the {@link StreamDialogDefaultEntry.PLAY_WITH_KODI} entry if it is needed. + * @return the current {@link Builder} instance + */ + public Builder addPlayWithKodiEntryIfNeeded() { if (KoreUtils.shouldShowPlayWithKodi(context, infoItem.getServiceId())) { addEntry(StreamDialogDefaultEntry.PLAY_WITH_KODI); } + return this; } /** @@ -262,16 +310,19 @@ public final class InfoItemDialog { *
* This method adds the "enqueue" (see {@link #addEnqueueEntriesIfNeeded()}) * and "start here" (see {@link #addStartHereEntries()} entries. + * @return the current {@link Builder} instance */ - public void addDefaultBeginningEntries() { + public Builder addDefaultBeginningEntries() { addEnqueueEntriesIfNeeded(); addStartHereEntries(); + return this; } /** * Add the entries which are usually at the bottom of the action list. + * @return the current {@link Builder} instance */ - public void addDefaultEndEntries() { + public Builder addDefaultEndEntries() { addAllEntries( StreamDialogDefaultEntry.APPEND_PLAYLIST, StreamDialogDefaultEntry.SHARE, @@ -280,6 +331,7 @@ public final class InfoItemDialog { addPlayWithKodiEntryIfNeeded(); addMarkAsWatchedEntryIfNeeded(); addEntry(StreamDialogDefaultEntry.SHOW_CHANNEL_DETAILS); + return this; } /** @@ -292,5 +344,14 @@ public final class InfoItemDialog { } return new InfoItemDialog(this.activity, this.fragment, this.infoItem, this.entries); } + + public static void reportErrorDuringInitialization(final Throwable throwable, + final InfoItem item) { + ErrorUtil.showSnackbar(App.getApp().getBaseContext(), new ErrorInfo( + throwable, + UserAction.OPEN_INFO_ITEM_DIALOG, + "none", + item.getServiceId())); + } } } diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java index 03c922ba4..832fb580f 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java @@ -1,6 +1,5 @@ package org.schabi.newpipe.local.history; -import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.os.Parcelable; @@ -326,26 +325,28 @@ public class StatisticsPlaylistFragment private void showInfoItemDialog(final StreamStatisticsEntry item) { final Context context = getContext(); - final Activity activity = getActivity(); - if (context == null || context.getResources() == null || activity == null) { - return; - } final StreamInfoItem infoItem = item.toStreamInfoItem(); - final InfoItemDialog.Builder dialogBuilder = - new InfoItemDialog.Builder(activity, context, this, infoItem); + try { + final InfoItemDialog.Builder dialogBuilder = + new InfoItemDialog.Builder(getActivity(), context, this, infoItem); - // set entries in the middle; the others are added automatically - dialogBuilder.addEntry(StreamDialogDefaultEntry.DELETE); - - // set custom actions - dialogBuilder.setAction(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND, - (fragment, infoItemDuplicate) -> NavigationHelper - .playOnBackgroundPlayer(context, getPlayQueueStartingAt(item), true)); - dialogBuilder.setAction(StreamDialogDefaultEntry.DELETE, (fragment, infoItemDuplicate) -> - deleteEntry(Math.max(itemListAdapter.getItemsList().indexOf(item), 0))); - - dialogBuilder.create().show(); + // set entries in the middle; the others are added automatically + dialogBuilder + .addEntry(StreamDialogDefaultEntry.DELETE) + .setAction( + StreamDialogDefaultEntry.DELETE, + (f, i) -> deleteEntry( + Math.max(itemListAdapter.getItemsList().indexOf(item), 0))) + .setAction( + StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND, + (f, i) -> NavigationHelper.playOnBackgroundPlayer( + context, getPlayQueueStartingAt(item), true)) + .create() + .show(); + } catch (final IllegalArgumentException e) { + InfoItemDialog.Builder.reportErrorDuringInitialization(e, infoItem); + } } private void deleteEntry(final int index) { diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index 2b690ff7b..ed2b109b5 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -3,7 +3,6 @@ package org.schabi.newpipe.local.playlist; import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout; -import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; @@ -740,33 +739,38 @@ public class LocalPlaylistFragment extends BaseLocalListFragment NavigationHelper.playOnBackgroundPlayer( - context, getPlayQueueStartingAt(item), true)); - dialogBuilder.setAction(StreamDialogDefaultEntry.SET_AS_PLAYLIST_THUMBNAIL, - (fragment, infoItemDuplicate) -> - changeThumbnailUrl(item.getStreamEntity().getThumbnailUrl())); - dialogBuilder.setAction(StreamDialogDefaultEntry.DELETE, - (fragment, infoItemDuplicate) -> deleteItem(item)); - - dialogBuilder.create().show(); + // set custom actions + // all entries modified below have already been added within the builder + dialogBuilder + .setAction( + StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND, + (f, i) -> NavigationHelper.playOnBackgroundPlayer( + context, getPlayQueueStartingAt(item), true)) + .setAction( + StreamDialogDefaultEntry.SET_AS_PLAYLIST_THUMBNAIL, + (f, i) -> + changeThumbnailUrl(item.getStreamEntity().getThumbnailUrl())) + .setAction( + StreamDialogDefaultEntry.DELETE, + (f, i) -> deleteItem(item)) + .create() + .show(); + } catch (final IllegalArgumentException e) { + InfoItemDialog.Builder.reportErrorDuringInitialization(e, infoItem); + } } private void setInitialData(final long pid, final String title) { diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java b/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java index e3d26c833..b9e1f17c5 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java +++ b/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java @@ -1,13 +1,14 @@ package org.schabi.newpipe.util; import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; +import static org.schabi.newpipe.util.StreamDialogEntry.fetchItemInfoIfSparse; +import static org.schabi.newpipe.util.StreamDialogEntry.openChannelFragment; import android.net.Uri; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.StringRes; -import androidx.fragment.app.Fragment; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; @@ -15,11 +16,9 @@ import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.error.UserAction; -import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; import org.schabi.newpipe.local.dialog.PlaylistDialog; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.util.external_communication.KoreUtils; import org.schabi.newpipe.util.external_communication.ShareUtils; @@ -73,32 +72,45 @@ public enum StreamDialogDefaultEntry { }), /** - * Enqueues the stream automatically to the current PlayerType.
- *
- * Info: Add this entry within showStreamDialog. + * Enqueues the stream automatically to the current PlayerType. */ ENQUEUE(R.string.enqueue_stream, (fragment, item) -> - NavigationHelper.enqueueOnPlayer(fragment.getContext(), new SinglePlayQueue(item)) + fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue -> + NavigationHelper.enqueueOnPlayer(fragment.getContext(), singlePlayQueue)) ), + /** + * Enqueues the stream automatically to the current PlayerType + * after the currently playing stream. + */ ENQUEUE_NEXT(R.string.enqueue_next_stream, (fragment, item) -> - NavigationHelper.enqueueNextOnPlayer(fragment.getContext(), new SinglePlayQueue(item)) + fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue -> + NavigationHelper.enqueueNextOnPlayer(fragment.getContext(), singlePlayQueue)) ), START_HERE_ON_BACKGROUND(R.string.start_here_on_background, (fragment, item) -> - NavigationHelper.playOnBackgroundPlayer(fragment.getContext(), - new SinglePlayQueue(item), true)), + fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue -> + NavigationHelper.playOnBackgroundPlayer( + fragment.getContext(), singlePlayQueue, true))), START_HERE_ON_POPUP(R.string.start_here_on_popup, (fragment, item) -> - NavigationHelper.playOnPopupPlayer(fragment.getContext(), - new SinglePlayQueue(item), true)), + fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue -> + NavigationHelper.playOnPopupPlayer(fragment.getContext(), singlePlayQueue, true))), SET_AS_PLAYLIST_THUMBNAIL(R.string.set_as_playlist_thumbnail, (fragment, item) -> { - }), // has to be set manually + throw new UnsupportedOperationException("This needs to be implemented manually " + + "by using InfoItemDialog.Builder.setAction()"); + }), DELETE(R.string.delete, (fragment, item) -> { - }), // has to be set manually + throw new UnsupportedOperationException("This needs to be implemented manually " + + "by using InfoItemDialog.Builder.setAction()"); + }), + /** + * Opens a {@link PlaylistDialog} to either append the stream to a playlist + * or create a new playlist if there are no local playlists. + */ APPEND_PLAYLIST(R.string.add_to_playlist, (fragment, item) -> PlaylistDialog.createCorrespondingDialog( fragment.getContext(), @@ -154,12 +166,4 @@ public enum StreamDialogDefaultEntry { return new StreamDialogEntry(resource, action); } - private static void openChannelFragment(@NonNull final Fragment fragment, - @NonNull final StreamInfoItem item, - final String uploaderUrl) { - // For some reason `getParentFragmentManager()` doesn't work, but this does. - NavigationHelper.openChannelFragment( - fragment.requireActivity().getSupportFragmentManager(), - item.getServiceId(), uploaderUrl, item.getUploaderName()); - } } diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java b/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java index bb59d0f29..24d616819 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java +++ b/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java @@ -2,16 +2,22 @@ package org.schabi.newpipe.util; import android.content.Context; - import androidx.annotation.NonNull; import androidx.annotation.StringRes; import androidx.fragment.app.Fragment; +import org.schabi.newpipe.error.ErrorInfo; +import org.schabi.newpipe.error.ErrorUtil; +import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.extractor.stream.StreamInfoItem; +import org.schabi.newpipe.extractor.stream.StreamType; +import org.schabi.newpipe.local.history.HistoryRecordManager; +import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import java.util.function.Consumer; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.schedulers.Schedulers; public class StreamDialogEntry { @@ -33,4 +39,61 @@ public class StreamDialogEntry { public interface StreamDialogEntryAction { void onClick(Fragment fragment, StreamInfoItem infoItem); } + + public static void openChannelFragment(@NonNull final Fragment fragment, + @NonNull final StreamInfoItem item, + final String uploaderUrl) { + // For some reason `getParentFragmentManager()` doesn't work, but this does. + NavigationHelper.openChannelFragment( + fragment.requireActivity().getSupportFragmentManager(), + item.getServiceId(), uploaderUrl, item.getUploaderName()); + } + + /** + * Fetches a {@link StreamInfoItem} if it is incomplete and executes the callback. + *
+ * This method is required if the info has been fetched + * via a {@link org.schabi.newpipe.extractor.feed.FeedExtractor}. + * FeedExtractors provide a fast and lightweight method to fetch info, + * but the info might be incomplete + * (see {@link org.schabi.newpipe.local.feed.service.FeedLoadService} for more details). + * @param context + * @param item the item which is checked and eventually loaded completely + * @param callback + */ + public static void fetchItemInfoIfSparse(@NonNull final Context context, + @NonNull final StreamInfoItem item, + @NonNull final Consumer callback) { + if (!(item.getStreamType() == StreamType.LIVE_STREAM + || item.getStreamType() == StreamType.AUDIO_LIVE_STREAM) + && item.getDuration() < 0) { + // Sparse item: fetched by fast fetch + ExtractorHelper.getStreamInfo( + item.getServiceId(), + item.getUrl(), + false + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + final HistoryRecordManager recordManager = + new HistoryRecordManager(context); + recordManager.saveStreamState(result, 0) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .doOnError(throwable -> ErrorUtil.showSnackbar( + context, + new ErrorInfo(throwable, UserAction.REQUESTED_STREAM, + item.getUrl(), item.getServiceId()))) + .subscribe(); + + callback.accept(new SinglePlayQueue(result)); + }, throwable -> ErrorUtil.createNotification(context, + new ErrorInfo(throwable, UserAction.REQUESTED_CHANNEL, + "Could not fetch missing stream info"))); + } else { + callback.accept(new SinglePlayQueue(item)); + } + } + } From a7d5d9a1d693e7794c8df03c4f0657f33040b6a9 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Sat, 19 Feb 2022 00:02:15 +0100 Subject: [PATCH 08/11] Fix rebase --- .../newpipe/info_list/InfoItemDialog.java | 2 +- .../util/StreamDialogDefaultEntry.java | 37 ++----------------- 2 files changed, 5 insertions(+), 34 deletions(-) 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 index 183b2d8d9..924c03cd8 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java @@ -252,7 +252,7 @@ public final class InfoItemDialog { * @return the current {@link Builder} instance */ public Builder addEnqueueEntriesIfNeeded() { - if (PlayerHolder.getInstance().isPlayerOpen()) { + if (PlayerHolder.getInstance().isPlayQueueReady()) { addEntry(StreamDialogDefaultEntry.ENQUEUE); if (PlayerHolder.getInstance().getQueueSize() > 1) { diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java b/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java index b9e1f17c5..a395d1ec1 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java +++ b/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java @@ -1,21 +1,15 @@ package org.schabi.newpipe.util; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.util.StreamDialogEntry.fetchItemInfoIfSparse; import static org.schabi.newpipe.util.StreamDialogEntry.openChannelFragment; import android.net.Uri; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.StringRes; -import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; import org.schabi.newpipe.database.stream.model.StreamEntity; -import org.schabi.newpipe.error.ErrorInfo; -import org.schabi.newpipe.error.ErrorUtil; -import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; import org.schabi.newpipe.local.dialog.PlaylistDialog; import org.schabi.newpipe.local.history.HistoryRecordManager; @@ -25,7 +19,6 @@ import org.schabi.newpipe.util.external_communication.ShareUtils; import java.util.Collections; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.schedulers.Schedulers; /** *

@@ -44,32 +37,10 @@ import io.reactivex.rxjava3.schedulers.Schedulers; *

*/ public enum StreamDialogDefaultEntry { - SHOW_CHANNEL_DETAILS(R.string.show_channel_details, (fragment, item) -> { - if (isNullOrEmpty(item.getUploaderUrl())) { - final int serviceId = item.getServiceId(); - final String url = item.getUrl(); - Toast.makeText(fragment.getContext(), R.string.loading_channel_details, - Toast.LENGTH_SHORT).show(); - ExtractorHelper.getStreamInfo(serviceId, url, false) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> { - NewPipeDatabase.getInstance(fragment.requireContext()).streamDAO() - .setUploaderUrl(serviceId, url, result.getUploaderUrl()) - .subscribeOn(Schedulers.io()).subscribe(); - openChannelFragment(fragment, item, result.getUploaderUrl()); - }, throwable -> ErrorUtil.openActivity( - fragment.requireContext(), - new ErrorInfo( - throwable, - UserAction.REQUESTED_CHANNEL, - url, - serviceId - ))); - } else { - openChannelFragment(fragment, item, item.getUploaderUrl()); - } - }), + SHOW_CHANNEL_DETAILS(R.string.show_channel_details, (fragment, item) -> + SaveUploaderUrlHelper.saveUploaderUrlIfNeeded(fragment, item, + uploaderUrl -> openChannelFragment(fragment, item, uploaderUrl)) + ), /** * Enqueues the stream automatically to the current PlayerType. From 277f21d5b2ccc8e50241d5997a098e9022b3a1f0 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Fri, 18 Feb 2022 23:46:23 +0100 Subject: [PATCH 09/11] Move Classes related to InfoItemDIalog into own package --- .../newpipe/fragments/list/BaseListFragment.java | 2 +- .../fragments/list/playlist/PlaylistFragment.java | 4 ++-- .../info_list/{ => dialog}/InfoItemDialog.java | 4 +--- .../dialog}/StreamDialogDefaultEntry.java | 12 +++++++----- .../dialog}/StreamDialogEntry.java | 4 +++- .../org/schabi/newpipe/local/feed/FeedFragment.kt | 2 +- .../local/history/StatisticsPlaylistFragment.java | 4 ++-- .../local/playlist/LocalPlaylistFragment.java | 4 ++-- 8 files changed, 19 insertions(+), 17 deletions(-) rename app/src/main/java/org/schabi/newpipe/info_list/{ => dialog}/InfoItemDialog.java (99%) rename app/src/main/java/org/schabi/newpipe/{util => info_list/dialog}/StreamDialogDefaultEntry.java (92%) rename app/src/main/java/org/schabi/newpipe/{util => info_list/dialog}/StreamDialogEntry.java (96%) 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 b1fa0059c..db14fead9 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 @@ -29,8 +29,8 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; 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.InfoListAdapter; import org.schabi.newpipe.info_list.dialog.InfoItemDialog; +import org.schabi.newpipe.info_list.InfoListAdapter; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; import org.schabi.newpipe.util.StateSaver; 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 15110183c..50dd73ffc 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 @@ -35,7 +35,7 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; -import org.schabi.newpipe.info_list.InfoItemDialog; +import org.schabi.newpipe.info_list.dialog.InfoItemDialog; import org.schabi.newpipe.local.playlist.RemotePlaylistManager; import org.schabi.newpipe.player.MainPlayer.PlayerType; import org.schabi.newpipe.player.playqueue.PlayQueue; @@ -44,7 +44,7 @@ import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.PicassoHelper; -import org.schabi.newpipe.util.StreamDialogDefaultEntry; +import org.schabi.newpipe.info_list.dialog.StreamDialogDefaultEntry; import org.schabi.newpipe.util.external_communication.ShareUtils; import java.util.ArrayList; diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java b/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java similarity index 99% rename from app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java rename to app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java index 924c03cd8..c573831b4 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemDialog.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java @@ -1,4 +1,4 @@ -package org.schabi.newpipe.info_list; +package org.schabi.newpipe.info_list.dialog; import static org.schabi.newpipe.MainActivity.DEBUG; @@ -24,8 +24,6 @@ import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.player.helper.PlayerHolder; -import org.schabi.newpipe.util.StreamDialogDefaultEntry; -import org.schabi.newpipe.util.StreamDialogEntry; import org.schabi.newpipe.util.external_communication.KoreUtils; import java.util.ArrayList; diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java b/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogDefaultEntry.java similarity index 92% rename from app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java rename to app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogDefaultEntry.java index a395d1ec1..66894ff88 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StreamDialogDefaultEntry.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogDefaultEntry.java @@ -1,7 +1,7 @@ -package org.schabi.newpipe.util; +package org.schabi.newpipe.info_list.dialog; -import static org.schabi.newpipe.util.StreamDialogEntry.fetchItemInfoIfSparse; -import static org.schabi.newpipe.util.StreamDialogEntry.openChannelFragment; +import static org.schabi.newpipe.info_list.dialog.StreamDialogEntry.fetchItemInfoIfSparse; +import static org.schabi.newpipe.info_list.dialog.StreamDialogEntry.openChannelFragment; import android.net.Uri; @@ -13,6 +13,8 @@ import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; import org.schabi.newpipe.local.dialog.PlaylistDialog; import org.schabi.newpipe.local.history.HistoryRecordManager; +import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.SaveUploaderUrlHelper; import org.schabi.newpipe.util.external_communication.KoreUtils; import org.schabi.newpipe.util.external_communication.ShareUtils; @@ -23,7 +25,7 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; /** *

* This enum provides entries that are accepted - * by the {@link org.schabi.newpipe.info_list.InfoItemDialog.Builder}. + * by the {@link InfoItemDialog.Builder}. *

*

* These entries contain a String {@link #resource} which is displayed in the dialog and @@ -31,7 +33,7 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; * when the entry is selected (via onClick()). *
* They action can be overridden by using the Builder's - * {@link org.schabi.newpipe.info_list.InfoItemDialog.Builder#setAction( + * {@link InfoItemDialog.Builder#setAction( * StreamDialogDefaultEntry, StreamDialogEntry.StreamDialogEntryAction)} * method. *

diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java b/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogEntry.java similarity index 96% rename from app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java rename to app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogEntry.java index 24d616819..98c3d9aa3 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogEntry.java @@ -1,4 +1,4 @@ -package org.schabi.newpipe.util; +package org.schabi.newpipe.info_list.dialog; import android.content.Context; @@ -13,6 +13,8 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; +import org.schabi.newpipe.util.ExtractorHelper; +import org.schabi.newpipe.util.NavigationHelper; import java.util.function.Consumer; diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt index e229a8789..8285d21e6 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/FeedFragment.kt @@ -70,7 +70,7 @@ import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException import org.schabi.newpipe.extractor.stream.StreamInfoItem import org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty import org.schabi.newpipe.fragments.BaseStateFragment -import org.schabi.newpipe.info_list.InfoItemDialog +import org.schabi.newpipe.info_list.dialog.InfoItemDialog import org.schabi.newpipe.ktx.animate import org.schabi.newpipe.ktx.animateHideRecyclerViewAllowingScrolling import org.schabi.newpipe.ktx.slideUp diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java index 832fb580f..01df34292 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java @@ -28,14 +28,14 @@ import org.schabi.newpipe.databinding.StatisticPlaylistControlBinding; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.extractor.stream.StreamInfoItem; -import org.schabi.newpipe.info_list.InfoItemDialog; +import org.schabi.newpipe.info_list.dialog.InfoItemDialog; import org.schabi.newpipe.local.BaseLocalListFragment; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.settings.HistorySettingsFragment; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; -import org.schabi.newpipe.util.StreamDialogDefaultEntry; +import org.schabi.newpipe.info_list.dialog.StreamDialogDefaultEntry; import java.util.ArrayList; import java.util.Collections; diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index ed2b109b5..9ea6c020d 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -40,7 +40,7 @@ import org.schabi.newpipe.databinding.PlaylistControlBinding; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.extractor.stream.StreamInfoItem; -import org.schabi.newpipe.info_list.InfoItemDialog; +import org.schabi.newpipe.info_list.dialog.InfoItemDialog; import org.schabi.newpipe.local.BaseLocalListFragment; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.player.MainPlayer.PlayerType; @@ -49,7 +49,7 @@ import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; -import org.schabi.newpipe.util.StreamDialogDefaultEntry; +import org.schabi.newpipe.info_list.dialog.StreamDialogDefaultEntry; import java.util.ArrayList; import java.util.Collections; From ee477b25e55f2a90e9a75a897b98f6c78c5ec592 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Sun, 20 Feb 2022 20:26:23 +0100 Subject: [PATCH 10/11] Move StreamDialogEntry.openChannelFragment to NavigationHelper --- .../info_list/dialog/StreamDialogDefaultEntry.java | 2 +- .../newpipe/info_list/dialog/StreamDialogEntry.java | 10 ---------- .../java/org/schabi/newpipe/util/NavigationHelper.java | 10 ++++++++++ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogDefaultEntry.java b/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogDefaultEntry.java index 66894ff88..eda9e19bc 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogDefaultEntry.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogDefaultEntry.java @@ -1,7 +1,7 @@ package org.schabi.newpipe.info_list.dialog; import static org.schabi.newpipe.info_list.dialog.StreamDialogEntry.fetchItemInfoIfSparse; -import static org.schabi.newpipe.info_list.dialog.StreamDialogEntry.openChannelFragment; +import static org.schabi.newpipe.util.NavigationHelper.openChannelFragment; import android.net.Uri; diff --git a/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogEntry.java b/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogEntry.java index 98c3d9aa3..a8d361447 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogEntry.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogEntry.java @@ -14,7 +14,6 @@ import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.util.ExtractorHelper; -import org.schabi.newpipe.util.NavigationHelper; import java.util.function.Consumer; @@ -42,15 +41,6 @@ public class StreamDialogEntry { void onClick(Fragment fragment, StreamInfoItem infoItem); } - public static void openChannelFragment(@NonNull final Fragment fragment, - @NonNull final StreamInfoItem item, - final String uploaderUrl) { - // For some reason `getParentFragmentManager()` doesn't work, but this does. - NavigationHelper.openChannelFragment( - fragment.requireActivity().getSupportFragmentManager(), - item.getServiceId(), uploaderUrl, item.getUploaderName()); - } - /** * Fetches a {@link StreamInfoItem} if it is incomplete and executes the callback. *
diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index 22e0a2dd0..2502bef8a 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -31,6 +31,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.StreamInfo; +import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.fragments.MainFragment; import org.schabi.newpipe.fragments.detail.VideoDetailFragment; @@ -402,6 +403,15 @@ public final class NavigationHelper { .commit(); } + public static void openChannelFragment(@NonNull final Fragment fragment, + @NonNull final StreamInfoItem item, + final String uploaderUrl) { + // For some reason `getParentFragmentManager()` doesn't work, but this does. + openChannelFragment( + fragment.requireActivity().getSupportFragmentManager(), + item.getServiceId(), uploaderUrl, item.getUploaderName()); + } + public static void openPlaylistFragment(final FragmentManager fragmentManager, final int serviceId, final String url, @NonNull final String name) { From d3bc18497157572e758f73bfdf5a69a447027fe4 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Mon, 21 Feb 2022 21:50:30 +0100 Subject: [PATCH 11/11] Clarify that only StramInfoItems are accepted by the builder --- .../schabi/newpipe/info_list/dialog/InfoItemDialog.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java b/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java index c573831b4..2264ab370 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java @@ -86,7 +86,7 @@ public final class InfoItemDialog { } /** - *

Builder to generate a {@link InfoItemDialog}.

+ *

Builder to generate a {@link InfoItemDialog} for a {@link StreamInfoItem}.

* Use {@link #addEntry(StreamDialogDefaultEntry)} * and {@link #addAllEntries(StreamDialogDefaultEntry...)} to add options to the dialog. *
@@ -102,7 +102,8 @@ public final class InfoItemDialog { private final boolean addDefaultEntriesAutomatically; /** - *

Create a Builder instance that automatically adds the some default entries + *

Create a {@link Builder builder} instance for a {@link StreamInfoItem} + * that automatically adds the some default entries * at the top and bottom of the dialog.

* The dialog has the following structure: *
@@ -142,7 +143,7 @@ public final class InfoItemDialog {
         }
 
         /**
-         * 

Create an instance of this Builder.

+ *

Create an instance of this {@link Builder} for a {@link StreamInfoItem}.

*

If {@code addDefaultEntriesAutomatically} is set to {@code true}, * some default entries are added to the top and bottom of the dialog.

* The dialog has the following structure: