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));
+ }
+ }
+
}