mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-30 23:03:00 +00:00 
			
		
		
		
	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.
This commit is contained in:
		| @@ -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<I, N> extends BaseStateFragment<I> | ||||
|         implements ListViewContract<I, N>, StateSaver.WriteRead, | ||||
|         SharedPreferences.OnSharedPreferenceChangeListener { | ||||
| @@ -415,49 +409,22 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> | ||||
|         if (context == null || context.getResources() == null || activity == null) { | ||||
|             return; | ||||
|         } | ||||
|         final List<StreamDialogEntry> 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(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|   | ||||
| @@ -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<PlaylistInfo> { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         final ArrayList<StreamDialogEntry> 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 | ||||
|   | ||||
| @@ -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<StreamDialogEntry> 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(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * <p>Builder to generate a {@link InfoItemDialog}.</p> | ||||
|      * Use {@link #addEntry(StreamDialogDefaultEntry)} | ||||
|      * and {@link #addAllEntries(StreamDialogDefaultEntry...)} to add options to the dialog. | ||||
|      * <br> | ||||
|      * 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<StreamDialogEntry> 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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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<FeedState>() { | ||||
| @@ -361,48 +358,20 @@ class FeedFragment : BaseStateFragment<FeedState>() { | ||||
|         val activity: Activity? = getActivity() | ||||
|         if (context == null || context.resources == null || activity == null) return | ||||
|  | ||||
|         val entries = ArrayList<StreamDialogEntry>() | ||||
|         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 { | ||||
|   | ||||
| @@ -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<StreamDialogEntry> 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) { | ||||
|   | ||||
| @@ -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<List<PlaylistStreamEntry>, 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<List<PlaylistSt | ||||
|         } | ||||
|         final StreamInfoItem infoItem = item.toStreamInfoItem(); | ||||
|  | ||||
|         final ArrayList<StreamDialogEntry> 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) { | ||||
|   | ||||
| @@ -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.<br> | ||||
|      * <br> | ||||
|      * 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()); | ||||
|     } | ||||
| } | ||||
| @@ -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.<br> | ||||
|      * <br> | ||||
|      * 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<StreamDialogEntry> 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<SinglePlayQueue> 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)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 TobiGr
					TobiGr