Apply feedback
Return this in InfoIrtemDialog.Builder methoods. Move null checks for InfoIrtemDialog.Builder into constructor. Fix and add some more docs.
This commit is contained in:
parent
646d8f431c
commit
fd0d76e866
|
@ -28,8 +28,8 @@ public enum UserAction {
|
||||||
DOWNLOAD_FAILED("download failed"),
|
DOWNLOAD_FAILED("download failed"),
|
||||||
PREFERENCES_MIGRATION("migration of preferences"),
|
PREFERENCES_MIGRATION("migration of preferences"),
|
||||||
SHARE_TO_NEWPIPE("share to newpipe"),
|
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;
|
private final String message;
|
||||||
|
|
||||||
|
|
|
@ -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.animate;
|
||||||
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
|
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.Configuration;
|
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.extractor.stream.StreamInfoItem;
|
||||||
import org.schabi.newpipe.fragments.BaseStateFragment;
|
import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||||
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
|
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.InfoListAdapter;
|
||||||
|
import org.schabi.newpipe.info_list.dialog.InfoItemDialog;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
import org.schabi.newpipe.util.OnClickGesture;
|
import org.schabi.newpipe.util.OnClickGesture;
|
||||||
import org.schabi.newpipe.util.StateSaver;
|
import org.schabi.newpipe.util.StateSaver;
|
||||||
|
@ -403,13 +402,11 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void showInfoItemDialog(final StreamInfoItem item) {
|
protected void showInfoItemDialog(final StreamInfoItem item) {
|
||||||
final Context context = getContext();
|
try {
|
||||||
final Activity activity = getActivity();
|
new InfoItemDialog.Builder(getActivity(), getContext(), this, item).create().show();
|
||||||
if (context == null || context.getResources() == null || activity == null) {
|
} catch (final IllegalArgumentException e) {
|
||||||
return;
|
InfoItemDialog.Builder.reportErrorDuringInitialization(e, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
new InfoItemDialog.Builder(activity, context, this, item).create().show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -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.animate;
|
||||||
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
|
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -137,20 +136,20 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||||
@Override
|
@Override
|
||||||
protected void showInfoItemDialog(final StreamInfoItem item) {
|
protected void showInfoItemDialog(final StreamInfoItem item) {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
final Activity activity = getActivity();
|
try {
|
||||||
if (context == null || context.getResources() == null || activity == null) {
|
final InfoItemDialog.Builder dialogBuilder =
|
||||||
return;
|
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
|
@Override
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
package org.schabi.newpipe.info_list;
|
package org.schabi.newpipe.info_list;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.MainActivity.DEBUG;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
@ -11,7 +15,12 @@ import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.App;
|
||||||
import org.schabi.newpipe.R;
|
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.StreamInfoItem;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
import org.schabi.newpipe.player.helper.PlayerHolder;
|
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog for a {@link StreamInfoItem}.
|
* 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.
|
* This dialog is mostly used for longpress context menus.
|
||||||
*/
|
*/
|
||||||
public final class InfoItemDialog {
|
public final class InfoItemDialog {
|
||||||
|
private static final String TAG = Build.class.getSimpleName();
|
||||||
/**
|
/**
|
||||||
* Ideally, {@link InfoItemDialog} would extend {@link AlertDialog}.
|
* Ideally, {@link InfoItemDialog} would extend {@link AlertDialog}.
|
||||||
* However, extending {@link AlertDialog} requires many additional lines
|
* However, extending {@link AlertDialog} requires many additional lines
|
||||||
|
@ -42,6 +53,7 @@ public final class InfoItemDialog {
|
||||||
@NonNull final StreamInfoItem info,
|
@NonNull final StreamInfoItem info,
|
||||||
@NonNull final List<StreamDialogEntry> entries) {
|
@NonNull final List<StreamDialogEntry> entries) {
|
||||||
|
|
||||||
|
// Create the dialog's title
|
||||||
final View bannerView = View.inflate(activity, R.layout.dialog_title, null);
|
final View bannerView = View.inflate(activity, R.layout.dialog_title, null);
|
||||||
bannerView.setSelected(true);
|
bannerView.setSelected(true);
|
||||||
|
|
||||||
|
@ -56,9 +68,11 @@ public final class InfoItemDialog {
|
||||||
detailsView.setVisibility(View.GONE);
|
detailsView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the entry's descriptions which are displayed in the dialog
|
||||||
final String[] items = entries.stream()
|
final String[] items = entries.stream()
|
||||||
.map(entry -> entry.getString(activity)).toArray(String[]::new);
|
.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) ->
|
final DialogInterface.OnClickListener action = (d, index) ->
|
||||||
entries.get(index).action.onClick(fragment, info);
|
entries.get(index).action.onClick(fragment, info);
|
||||||
|
|
||||||
|
@ -96,7 +110,7 @@ public final class InfoItemDialog {
|
||||||
* <pre>
|
* <pre>
|
||||||
* + - - - - - - - - - - - - - - - - - - - - - -+
|
* + - - - - - - - - - - - - - - - - - - - - - -+
|
||||||
* | ENQUEUE |
|
* | ENQUEUE |
|
||||||
* | ENQUEUE_HERE |
|
* | ENQUEUE_NEXT |
|
||||||
* | START_ON_BACKGROUND |
|
* | START_ON_BACKGROUND |
|
||||||
* | START_ON_POPUP |
|
* | START_ON_POPUP |
|
||||||
* + - - - - - - - - - - - - - - - - - - - - - -+
|
* + - - - - - - - - - - - - - - - - - - - - - -+
|
||||||
|
@ -118,10 +132,12 @@ public final class InfoItemDialog {
|
||||||
* @param context
|
* @param context
|
||||||
* @param fragment
|
* @param fragment
|
||||||
* @param infoItem the item for this dialog; all entries and their actions work with
|
* @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 <code>activity, context</code>
|
||||||
|
* or resources is <code>null</code>
|
||||||
*/
|
*/
|
||||||
public Builder(@NonNull final Activity activity,
|
public Builder(final Activity activity,
|
||||||
@NonNull final Context context,
|
final Context context,
|
||||||
@NonNull final Fragment fragment,
|
@NonNull final Fragment fragment,
|
||||||
@NonNull final StreamInfoItem infoItem) {
|
@NonNull final StreamInfoItem infoItem) {
|
||||||
this(activity, context, fragment, infoItem, true);
|
this(activity, context, fragment, infoItem, true);
|
||||||
|
@ -135,7 +151,7 @@ public final class InfoItemDialog {
|
||||||
* <pre>
|
* <pre>
|
||||||
* + - - - - - - - - - - - - - - - - - - - - - -+
|
* + - - - - - - - - - - - - - - - - - - - - - -+
|
||||||
* | ENQUEUE |
|
* | ENQUEUE |
|
||||||
* | ENQUEUE_HERE |
|
* | ENQUEUE_NEXT |
|
||||||
* | START_ON_BACKGROUND |
|
* | START_ON_BACKGROUND |
|
||||||
* | START_ON_POPUP |
|
* | START_ON_POPUP |
|
||||||
* + - - - - - - - - - - - - - - - - - - - - - -+
|
* + - - - - - - - - - - - - - - - - - - - - - -+
|
||||||
|
@ -164,12 +180,21 @@ public final class InfoItemDialog {
|
||||||
* <br/>
|
* <br/>
|
||||||
* Entries added with {@link #addEntry(StreamDialogDefaultEntry)} and
|
* Entries added with {@link #addEntry(StreamDialogDefaultEntry)} and
|
||||||
* {@link #addAllEntries(StreamDialogDefaultEntry...)} are added in between.
|
* {@link #addAllEntries(StreamDialogDefaultEntry...)} are added in between.
|
||||||
|
* @throws IllegalArgumentException if <code>activity, context</code>
|
||||||
|
* or resources is <code>null</code>
|
||||||
*/
|
*/
|
||||||
public Builder(@NonNull final Activity activity,
|
public Builder(final Activity activity,
|
||||||
@NonNull final Context context,
|
final Context context,
|
||||||
@NonNull final Fragment fragment,
|
@NonNull final Fragment fragment,
|
||||||
@NonNull final StreamInfoItem infoItem,
|
@NonNull final StreamInfoItem infoItem,
|
||||||
final boolean addDefaultEntriesAutomatically) {
|
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.activity = activity;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.fragment = fragment;
|
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());
|
entries.add(entry.toStreamDialogEntry());
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAllEntries(@NonNull final StreamDialogDefaultEntry... newEntries) {
|
/**
|
||||||
for (final StreamDialogDefaultEntry entry: newEntries) {
|
* Adds new entries. These are appended to the current entry list.
|
||||||
this.entries.add(entry.toStreamDialogEntry());
|
* @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.</p>
|
* does not have an effect.</p>
|
||||||
* @param entry the entry to change
|
* @param entry the entry to change
|
||||||
* @param action the action to perform when the entry is selected
|
* @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) {
|
@NonNull final StreamDialogEntry.StreamDialogEntryAction action) {
|
||||||
for (int i = 0; i < entries.size(); i++) {
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
if (entries.get(i).resource == entry.resource) {
|
if (entries.get(i).resource == entry.resource) {
|
||||||
entries.set(i, new StreamDialogEntry(entry.resource, action));
|
entries.set(i, new StreamDialogEntry(entry.resource, action));
|
||||||
return;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds {@link StreamDialogDefaultEntry#ENQUEUE} if the player is open and
|
* Adds {@link StreamDialogDefaultEntry#ENQUEUE} if the player is open and
|
||||||
* {@link StreamDialogDefaultEntry#ENQUEUE_NEXT} if there are multiple streams
|
* {@link StreamDialogDefaultEntry#ENQUEUE_NEXT} if there are multiple streams
|
||||||
* in the play queue.
|
* in the play queue.
|
||||||
|
* @return the current {@link Builder} instance
|
||||||
*/
|
*/
|
||||||
public void addEnqueueEntriesIfNeeded() {
|
public Builder addEnqueueEntriesIfNeeded() {
|
||||||
if (PlayerHolder.getInstance().isPlayerOpen()) {
|
if (PlayerHolder.getInstance().isPlayerOpen()) {
|
||||||
addEntry(StreamDialogDefaultEntry.ENQUEUE);
|
addEntry(StreamDialogDefaultEntry.ENQUEUE);
|
||||||
|
|
||||||
|
@ -221,26 +259,30 @@ public final class InfoItemDialog {
|
||||||
addEntry(StreamDialogDefaultEntry.ENQUEUE_NEXT);
|
addEntry(StreamDialogDefaultEntry.ENQUEUE_NEXT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the {@link StreamDialogDefaultEntry#START_HERE_ON_BACKGROUND}.
|
* Adds the {@link StreamDialogDefaultEntry#START_HERE_ON_BACKGROUND}.
|
||||||
* If the {@link #infoItem} is not a pure audio (live) stream,
|
* If the {@link #infoItem} is not a pure audio (live) stream,
|
||||||
* {@link StreamDialogDefaultEntry#START_HERE_ON_POPUP} is added, too.
|
* {@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);
|
addEntry(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND);
|
||||||
if (infoItem.getStreamType() != StreamType.AUDIO_STREAM
|
if (infoItem.getStreamType() != StreamType.AUDIO_STREAM
|
||||||
&& infoItem.getStreamType() != StreamType.AUDIO_LIVE_STREAM) {
|
&& infoItem.getStreamType() != StreamType.AUDIO_LIVE_STREAM) {
|
||||||
addEntry(StreamDialogDefaultEntry.START_HERE_ON_POPUP);
|
addEntry(StreamDialogDefaultEntry.START_HERE_ON_POPUP);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds {@link StreamDialogDefaultEntry.MARK_AS_WATCHED} if the watch history is enabled
|
* Adds {@link StreamDialogDefaultEntry.MARK_AS_WATCHED} if the watch history is enabled
|
||||||
* and the stream is not a livestream.
|
* and the stream is not a livestream.
|
||||||
|
* @return the current {@link Builder} instance
|
||||||
*/
|
*/
|
||||||
public void addMarkAsWatchedEntryIfNeeded() {
|
public Builder addMarkAsWatchedEntryIfNeeded() {
|
||||||
final boolean isWatchHistoryEnabled = PreferenceManager
|
final boolean isWatchHistoryEnabled = PreferenceManager
|
||||||
.getDefaultSharedPreferences(context)
|
.getDefaultSharedPreferences(context)
|
||||||
.getBoolean(context.getString(R.string.enable_watch_history_key), false);
|
.getBoolean(context.getString(R.string.enable_watch_history_key), false);
|
||||||
|
@ -249,12 +291,18 @@ public final class InfoItemDialog {
|
||||||
&& infoItem.getStreamType() != StreamType.AUDIO_LIVE_STREAM) {
|
&& infoItem.getStreamType() != StreamType.AUDIO_LIVE_STREAM) {
|
||||||
addEntry(StreamDialogDefaultEntry.MARK_AS_WATCHED);
|
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())) {
|
if (KoreUtils.shouldShowPlayWithKodi(context, infoItem.getServiceId())) {
|
||||||
addEntry(StreamDialogDefaultEntry.PLAY_WITH_KODI);
|
addEntry(StreamDialogDefaultEntry.PLAY_WITH_KODI);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -262,16 +310,19 @@ public final class InfoItemDialog {
|
||||||
* <br/>
|
* <br/>
|
||||||
* This method adds the "enqueue" (see {@link #addEnqueueEntriesIfNeeded()})
|
* This method adds the "enqueue" (see {@link #addEnqueueEntriesIfNeeded()})
|
||||||
* and "start here" (see {@link #addStartHereEntries()} entries.
|
* and "start here" (see {@link #addStartHereEntries()} entries.
|
||||||
|
* @return the current {@link Builder} instance
|
||||||
*/
|
*/
|
||||||
public void addDefaultBeginningEntries() {
|
public Builder addDefaultBeginningEntries() {
|
||||||
addEnqueueEntriesIfNeeded();
|
addEnqueueEntriesIfNeeded();
|
||||||
addStartHereEntries();
|
addStartHereEntries();
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add the entries which are usually at the bottom of the action list.
|
* 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(
|
addAllEntries(
|
||||||
StreamDialogDefaultEntry.APPEND_PLAYLIST,
|
StreamDialogDefaultEntry.APPEND_PLAYLIST,
|
||||||
StreamDialogDefaultEntry.SHARE,
|
StreamDialogDefaultEntry.SHARE,
|
||||||
|
@ -280,6 +331,7 @@ public final class InfoItemDialog {
|
||||||
addPlayWithKodiEntryIfNeeded();
|
addPlayWithKodiEntryIfNeeded();
|
||||||
addMarkAsWatchedEntryIfNeeded();
|
addMarkAsWatchedEntryIfNeeded();
|
||||||
addEntry(StreamDialogDefaultEntry.SHOW_CHANNEL_DETAILS);
|
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);
|
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()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package org.schabi.newpipe.local.history;
|
package org.schabi.newpipe.local.history;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
@ -326,26 +325,28 @@ public class StatisticsPlaylistFragment
|
||||||
|
|
||||||
private void showInfoItemDialog(final StreamStatisticsEntry item) {
|
private void showInfoItemDialog(final StreamStatisticsEntry item) {
|
||||||
final Context context = getContext();
|
final Context context = getContext();
|
||||||
final Activity activity = getActivity();
|
|
||||||
if (context == null || context.getResources() == null || activity == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final StreamInfoItem infoItem = item.toStreamInfoItem();
|
final StreamInfoItem infoItem = item.toStreamInfoItem();
|
||||||
|
|
||||||
final InfoItemDialog.Builder dialogBuilder =
|
try {
|
||||||
new InfoItemDialog.Builder(activity, context, this, infoItem);
|
final InfoItemDialog.Builder dialogBuilder =
|
||||||
|
new InfoItemDialog.Builder(getActivity(), context, this, infoItem);
|
||||||
|
|
||||||
// set entries in the middle; the others are added automatically
|
// set entries in the middle; the others are added automatically
|
||||||
dialogBuilder.addEntry(StreamDialogDefaultEntry.DELETE);
|
dialogBuilder
|
||||||
|
.addEntry(StreamDialogDefaultEntry.DELETE)
|
||||||
// set custom actions
|
.setAction(
|
||||||
dialogBuilder.setAction(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND,
|
StreamDialogDefaultEntry.DELETE,
|
||||||
(fragment, infoItemDuplicate) -> NavigationHelper
|
(f, i) -> deleteEntry(
|
||||||
.playOnBackgroundPlayer(context, getPlayQueueStartingAt(item), true));
|
Math.max(itemListAdapter.getItemsList().indexOf(item), 0)))
|
||||||
dialogBuilder.setAction(StreamDialogDefaultEntry.DELETE, (fragment, infoItemDuplicate) ->
|
.setAction(
|
||||||
deleteEntry(Math.max(itemListAdapter.getItemsList().indexOf(item), 0)));
|
StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND,
|
||||||
|
(f, i) -> NavigationHelper.playOnBackgroundPlayer(
|
||||||
dialogBuilder.create().show();
|
context, getPlayQueueStartingAt(item), true))
|
||||||
|
.create()
|
||||||
|
.show();
|
||||||
|
} catch (final IllegalArgumentException e) {
|
||||||
|
InfoItemDialog.Builder.reportErrorDuringInitialization(e, infoItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteEntry(final int index) {
|
private void deleteEntry(final int index) {
|
||||||
|
|
|
@ -3,7 +3,6 @@ package org.schabi.newpipe.local.playlist;
|
||||||
import static org.schabi.newpipe.ktx.ViewUtils.animate;
|
import static org.schabi.newpipe.ktx.ViewUtils.animate;
|
||||||
import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout;
|
import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -740,33 +739,38 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void showInfoItemDialog(final PlaylistStreamEntry item) {
|
protected void showInfoItemDialog(final PlaylistStreamEntry item) {
|
||||||
final Context context = getContext();
|
|
||||||
final Activity activity = getActivity();
|
|
||||||
if (context == null || context.getResources() == null || activity == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final StreamInfoItem infoItem = item.toStreamInfoItem();
|
final StreamInfoItem infoItem = item.toStreamInfoItem();
|
||||||
|
|
||||||
final InfoItemDialog.Builder dialogBuilder =
|
try {
|
||||||
new InfoItemDialog.Builder(activity, context, this, infoItem);
|
final Context context = getContext();
|
||||||
|
final InfoItemDialog.Builder dialogBuilder =
|
||||||
|
new InfoItemDialog.Builder(getActivity(), context, this, infoItem);
|
||||||
|
|
||||||
// add entries in the middle
|
// add entries in the middle
|
||||||
dialogBuilder.addAllEntries(
|
dialogBuilder.addAllEntries(
|
||||||
StreamDialogDefaultEntry.SET_AS_PLAYLIST_THUMBNAIL,
|
StreamDialogDefaultEntry.SET_AS_PLAYLIST_THUMBNAIL,
|
||||||
StreamDialogDefaultEntry.DELETE
|
StreamDialogDefaultEntry.DELETE
|
||||||
);
|
);
|
||||||
|
|
||||||
// set custom actions; all entries modified here have already been added within the builder
|
// set custom actions
|
||||||
dialogBuilder.setAction(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND,
|
// all entries modified below have already been added within the builder
|
||||||
(fragment, infoItemDuplicate) -> NavigationHelper.playOnBackgroundPlayer(
|
dialogBuilder
|
||||||
context, getPlayQueueStartingAt(item), true));
|
.setAction(
|
||||||
dialogBuilder.setAction(StreamDialogDefaultEntry.SET_AS_PLAYLIST_THUMBNAIL,
|
StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND,
|
||||||
(fragment, infoItemDuplicate) ->
|
(f, i) -> NavigationHelper.playOnBackgroundPlayer(
|
||||||
changeThumbnailUrl(item.getStreamEntity().getThumbnailUrl()));
|
context, getPlayQueueStartingAt(item), true))
|
||||||
dialogBuilder.setAction(StreamDialogDefaultEntry.DELETE,
|
.setAction(
|
||||||
(fragment, infoItemDuplicate) -> deleteItem(item));
|
StreamDialogDefaultEntry.SET_AS_PLAYLIST_THUMBNAIL,
|
||||||
|
(f, i) ->
|
||||||
dialogBuilder.create().show();
|
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) {
|
private void setInitialData(final long pid, final String title) {
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
package org.schabi.newpipe.util;
|
package org.schabi.newpipe.util;
|
||||||
|
|
||||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
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.net.Uri;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
|
|
||||||
import org.schabi.newpipe.NewPipeDatabase;
|
import org.schabi.newpipe.NewPipeDatabase;
|
||||||
import org.schabi.newpipe.R;
|
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.ErrorInfo;
|
||||||
import org.schabi.newpipe.error.ErrorUtil;
|
import org.schabi.newpipe.error.ErrorUtil;
|
||||||
import org.schabi.newpipe.error.UserAction;
|
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.PlaylistAppendDialog;
|
||||||
import org.schabi.newpipe.local.dialog.PlaylistDialog;
|
import org.schabi.newpipe.local.dialog.PlaylistDialog;
|
||||||
import org.schabi.newpipe.local.history.HistoryRecordManager;
|
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.KoreUtils;
|
||||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||||
|
|
||||||
|
@ -73,32 +72,45 @@ public enum StreamDialogDefaultEntry {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enqueues the stream automatically to the current PlayerType.<br>
|
* Enqueues the stream automatically to the current PlayerType.
|
||||||
* <br>
|
|
||||||
* Info: Add this entry within showStreamDialog.
|
|
||||||
*/
|
*/
|
||||||
ENQUEUE(R.string.enqueue_stream, (fragment, item) ->
|
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) ->
|
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) ->
|
START_HERE_ON_BACKGROUND(R.string.start_here_on_background, (fragment, item) ->
|
||||||
NavigationHelper.playOnBackgroundPlayer(fragment.getContext(),
|
fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue ->
|
||||||
new SinglePlayQueue(item), true)),
|
NavigationHelper.playOnBackgroundPlayer(
|
||||||
|
fragment.getContext(), singlePlayQueue, true))),
|
||||||
|
|
||||||
START_HERE_ON_POPUP(R.string.start_here_on_popup, (fragment, item) ->
|
START_HERE_ON_POPUP(R.string.start_here_on_popup, (fragment, item) ->
|
||||||
NavigationHelper.playOnPopupPlayer(fragment.getContext(),
|
fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue ->
|
||||||
new SinglePlayQueue(item), true)),
|
NavigationHelper.playOnPopupPlayer(fragment.getContext(), singlePlayQueue, true))),
|
||||||
|
|
||||||
SET_AS_PLAYLIST_THUMBNAIL(R.string.set_as_playlist_thumbnail, (fragment, item) -> {
|
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) -> {
|
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) ->
|
APPEND_PLAYLIST(R.string.add_to_playlist, (fragment, item) ->
|
||||||
PlaylistDialog.createCorrespondingDialog(
|
PlaylistDialog.createCorrespondingDialog(
|
||||||
fragment.getContext(),
|
fragment.getContext(),
|
||||||
|
@ -154,12 +166,4 @@ public enum StreamDialogDefaultEntry {
|
||||||
return new StreamDialogEntry(resource, action);
|
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,22 @@ package org.schabi.newpipe.util;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.fragment.app.Fragment;
|
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.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 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 {
|
public class StreamDialogEntry {
|
||||||
|
|
||||||
|
@ -33,4 +39,61 @@ public class StreamDialogEntry {
|
||||||
public interface StreamDialogEntryAction {
|
public interface StreamDialogEntryAction {
|
||||||
void onClick(Fragment fragment, StreamInfoItem infoItem);
|
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.
|
||||||
|
* <br />
|
||||||
|
* 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<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(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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue