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 deleted file mode 100644 index cbaae2834..000000000 --- a/app/src/main/java/org/schabi/newpipe/info_list/dialog/InfoItemDialog.java +++ /dev/null @@ -1,356 +0,0 @@ -package org.schabi.newpipe.info_list.dialog; - -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; - -import androidx.annotation.NonNull; -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; -import org.schabi.newpipe.util.StreamTypeUtil; -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}. - * 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 - * 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, - @NonNull final Fragment fragment, - @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); - - final TextView titleView = bannerView.findViewById(R.id.itemTitleView); - titleView.setText(info.getName()); - - final TextView detailsView = bannerView.findViewById(R.id.itemAdditionalDetails); - if (info.getUploaderName() != null) { - detailsView.setText(info.getUploaderName()); - detailsView.setVisibility(View.VISIBLE); - } else { - 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); - - dialog = new AlertDialog.Builder(activity) - .setCustomTitle(bannerView) - .setItems(items, action) - .create(); - - } - - public void show() { - dialog.show(); - } - - /** - *

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

- * 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 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 {@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: - *
-         *     + - - - - - - - - - - - - - - - - - - - - - -+
-         *     | ENQUEUE                                    |
-         *     | ENQUEUE_NEXT                               |
-         *     | 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 StreamInfoItem} - * @throws IllegalArgumentException if activity, context - * or resources is null - */ - public Builder(final Activity activity, - final Context context, - @NonNull final Fragment fragment, - @NonNull final StreamInfoItem infoItem) { - this(activity, context, fragment, infoItem, true); - } - - /** - *

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: - *
-         *     + - - - - - - - - - - - - - - - - - - - - - -+
-         *     | ENQUEUE                                    |
-         *     | ENQUEUE_NEXT                               |
-         *     | 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 #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. - * @throws IllegalArgumentException if activity, context - * or resources is null - */ - 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; - this.infoItem = infoItem; - this.addDefaultEntriesAutomatically = addDefaultEntriesAutomatically; - if (addDefaultEntriesAutomatically) { - addDefaultBeginningEntries(); - } - } - - /** - * 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; - } - - /** - * 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; - } - - /** - *

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 - * @return the current {@link Builder} instance - */ - 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 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 Builder addEnqueueEntriesIfNeeded() { - final PlayerHolder holder = PlayerHolder.INSTANCE; - if (holder.isPlayQueueReady()) { - addEntry(StreamDialogDefaultEntry.ENQUEUE); - - if (holder.getQueuePosition() < holder.getQueueSize() - 1) { - 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 Builder addStartHereEntries() { - addEntry(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND); - if (!StreamTypeUtil.isAudio(infoItem.getStreamType())) { - 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 Builder addMarkAsWatchedEntryIfNeeded() { - final boolean isWatchHistoryEnabled = PreferenceManager - .getDefaultSharedPreferences(context) - .getBoolean(context.getString(R.string.enable_watch_history_key), false); - if (isWatchHistoryEnabled && !StreamTypeUtil.isLiveStream(infoItem.getStreamType())) { - addEntry(StreamDialogDefaultEntry.MARK_AS_WATCHED); - } - return this; - } - - /** - * 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; - } - - /** - * 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. - * @return the current {@link Builder} instance - */ - 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 Builder addDefaultEndEntries() { - addAllEntries( - StreamDialogDefaultEntry.DOWNLOAD, - StreamDialogDefaultEntry.APPEND_PLAYLIST, - StreamDialogDefaultEntry.SHARE, - StreamDialogDefaultEntry.OPEN_IN_BROWSER - ); - addPlayWithKodiEntryIfNeeded(); - addMarkAsWatchedEntryIfNeeded(); - addEntry(StreamDialogDefaultEntry.SHOW_CHANNEL_DETAILS); - return this; - } - - /** - * Creates the {@link InfoItemDialog}. - * @return a new instance of {@link InfoItemDialog} - */ - public InfoItemDialog create() { - if (addDefaultEntriesAutomatically) { - addDefaultEndEntries(); - } - return new InfoItemDialog(this.activity, this.fragment, this.infoItem, this.entries); - } - - public static void reportErrorDuringInitialization(final Throwable throwable, - final InfoItem item) { - ErrorUtil.showSnackbar(App.getInstance().getBaseContext(), new ErrorInfo( - throwable, - UserAction.OPEN_INFO_ITEM_DIALOG, - "none", - item.getServiceId())); - } - } -} 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 deleted file mode 100644 index 5676fee95..000000000 --- a/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogDefaultEntry.java +++ /dev/null @@ -1,171 +0,0 @@ -package org.schabi.newpipe.info_list.dialog; - -import static org.schabi.newpipe.util.NavigationHelper.openChannelFragment; -import static org.schabi.newpipe.util.SparseItemUtil.fetchItemInfoIfSparse; -import static org.schabi.newpipe.util.SparseItemUtil.fetchStreamInfoAndSaveToDatabase; -import static org.schabi.newpipe.util.SparseItemUtil.fetchUploaderUrlIfSparse; - -import android.net.Uri; - -import androidx.annotation.NonNull; -import androidx.annotation.StringRes; - -import org.schabi.newpipe.R; -import org.schabi.newpipe.database.stream.model.StreamEntity; -import org.schabi.newpipe.download.DownloadDialog; -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; -import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.external_communication.KoreUtils; -import org.schabi.newpipe.util.external_communication.ShareUtils; - -import java.util.List; - -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; - -/** - *

- * This enum provides entries that are accepted - * by the {@link 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 InfoItemDialog.Builder#setAction( - * StreamDialogDefaultEntry, StreamDialogEntry.StreamDialogEntryAction)} - * method. - *

- */ -public enum StreamDialogDefaultEntry { - SHOW_CHANNEL_DETAILS(R.string.show_channel_details, (fragment, item) -> { - final var activity = fragment.requireActivity(); - fetchUploaderUrlIfSparse(activity, item.getServiceId(), item.getUrl(), - item.getUploaderUrl(), url -> openChannelFragment(activity, item, url)); - }), - - /** - * Enqueues the stream automatically to the current PlayerType. - */ - ENQUEUE(R.string.enqueue_stream, (fragment, 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) -> - fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue -> - NavigationHelper.enqueueNextOnPlayer(fragment.getContext(), singlePlayQueue)) - ), - - START_HERE_ON_BACKGROUND(R.string.start_here_on_background, (fragment, item) -> - fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue -> - NavigationHelper.playOnBackgroundPlayer( - fragment.getContext(), singlePlayQueue, true))), - - START_HERE_ON_POPUP(R.string.start_here_on_popup, (fragment, item) -> - fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue -> - NavigationHelper.playOnPopupPlayer(fragment.getContext(), singlePlayQueue, true))), - - SET_AS_PLAYLIST_THUMBNAIL(R.string.set_as_playlist_thumbnail, (fragment, item) -> { - throw new UnsupportedOperationException("This needs to be implemented manually " - + "by using InfoItemDialog.Builder.setAction()"); - }), - - DELETE(R.string.delete, (fragment, item) -> { - 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(), - List.of(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) -> - KoreUtils.playWithKore(fragment.requireContext(), Uri.parse(item.getUrl()))), - - SHARE(R.string.share, (fragment, item) -> - ShareUtils.shareText(fragment.requireContext(), item.getName(), item.getUrl(), - item.getThumbnails())), - - /** - * Opens a {@link DownloadDialog} after fetching some stream info. - * If the user quits the current fragment, it will not open a DownloadDialog. - */ - DOWNLOAD(R.string.download, (fragment, item) -> - fetchStreamInfoAndSaveToDatabase(fragment.requireContext(), item.getServiceId(), - item.getUrl(), info -> { - // Ensure the fragment is attached and its state hasn't been saved to avoid - // showing dialog during lifecycle changes or when the activity is paused, - // e.g. by selecting the download option and opening a different fragment. - if (fragment.isAdded() && !fragment.isStateSaved()) { - final DownloadDialog downloadDialog = - new DownloadDialog(fragment.requireContext(), info); - downloadDialog.show(fragment.getChildFragmentManager(), - "downloadDialog"); - } - }) - ), - - 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) - .doOnError(error -> { - ErrorUtil.showSnackbar( - fragment.requireContext(), - new ErrorInfo( - error, - UserAction.OPEN_INFO_ITEM_DIALOG, - "Got an error when trying to mark as watched" - ) - ); - }) - .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); - } - -} 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 deleted file mode 100644 index 9d82e3b58..000000000 --- a/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogEntry.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.schabi.newpipe.info_list.dialog; - -import android.content.Context; - -import androidx.annotation.NonNull; -import androidx.annotation.StringRes; -import androidx.fragment.app.Fragment; - -import org.schabi.newpipe.extractor.stream.StreamInfoItem; - -public class StreamDialogEntry { - - @StringRes - public final int resource; - @NonNull - public final StreamDialogEntryAction action; - - public StreamDialogEntry(@StringRes final int resource, - @NonNull final StreamDialogEntryAction action) { - this.resource = resource; - this.action = action; - } - - public String getString(@NonNull final Context context) { - return context.getString(resource); - } - - public interface StreamDialogEntryAction { - void onClick(Fragment fragment, StreamInfoItem infoItem); - } -}