Skip to content

Commit 80157fc

Browse files
committed
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.
1 parent af80d96 commit 80157fc

8 files changed

Lines changed: 388 additions & 491 deletions

File tree

app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java

Lines changed: 19 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package org.schabi.newpipe.fragments.list;
22

3+
import static org.schabi.newpipe.ktx.ViewUtils.animate;
4+
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
5+
36
import android.app.Activity;
47
import android.content.Context;
58
import android.content.SharedPreferences;
@@ -25,29 +28,20 @@
2528
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
2629
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
2730
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
28-
import org.schabi.newpipe.extractor.stream.StreamType;
2931
import org.schabi.newpipe.fragments.BaseStateFragment;
3032
import org.schabi.newpipe.fragments.OnScrollBelowItemsListener;
3133
import org.schabi.newpipe.info_list.InfoItemDialog;
3234
import org.schabi.newpipe.info_list.InfoListAdapter;
33-
import org.schabi.newpipe.player.helper.PlayerHolder;
34-
import org.schabi.newpipe.util.external_communication.KoreUtils;
3535
import org.schabi.newpipe.util.NavigationHelper;
3636
import org.schabi.newpipe.util.OnClickGesture;
3737
import org.schabi.newpipe.util.StateSaver;
38-
import org.schabi.newpipe.util.StreamDialogEntry;
38+
import org.schabi.newpipe.util.StreamDialogDefaultEntry;
3939
import org.schabi.newpipe.views.SuperScrollLayoutManager;
4040

41-
import java.util.ArrayList;
42-
import java.util.Arrays;
4341
import java.util.List;
4442
import java.util.Queue;
4543
import java.util.function.Supplier;
4644

47-
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
48-
import static org.schabi.newpipe.ktx.ViewUtils.animate;
49-
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
50-
5145
public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
5246
implements ListViewContract<I, N>, StateSaver.WriteRead,
5347
SharedPreferences.OnSharedPreferenceChangeListener {
@@ -415,49 +409,22 @@ protected void showStreamDialog(final StreamInfoItem item) {
415409
if (context == null || context.getResources() == null || activity == null) {
416410
return;
417411
}
418-
final List<StreamDialogEntry> entries = new ArrayList<>();
419-
420-
if (PlayerHolder.getInstance().isPlayQueueReady()) {
421-
entries.add(StreamDialogEntry.enqueue);
422-
423-
if (PlayerHolder.getInstance().getQueueSize() > 1) {
424-
entries.add(StreamDialogEntry.enqueue_next);
425-
}
426-
}
427-
428-
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
429-
entries.addAll(Arrays.asList(
430-
StreamDialogEntry.start_here_on_background,
431-
StreamDialogEntry.append_playlist,
432-
StreamDialogEntry.share
433-
));
434-
} else {
435-
entries.addAll(Arrays.asList(
436-
StreamDialogEntry.start_here_on_background,
437-
StreamDialogEntry.start_here_on_popup,
438-
StreamDialogEntry.append_playlist,
439-
StreamDialogEntry.share
440-
));
441-
}
442-
entries.add(StreamDialogEntry.open_in_browser);
443-
if (KoreUtils.shouldShowPlayWithKodi(context, item.getServiceId())) {
444-
entries.add(StreamDialogEntry.play_with_kodi);
445-
}
446-
447-
// show "mark as watched" only when watch history is enabled
448-
if (StreamDialogEntry.shouldAddMarkAsWatched(item.getStreamType(), context)) {
449-
entries.add(
450-
StreamDialogEntry.mark_as_watched
451-
);
452-
}
453-
if (!isNullOrEmpty(item.getUploaderUrl())) {
454-
entries.add(StreamDialogEntry.show_channel_details);
455-
}
456-
457-
StreamDialogEntry.setEnabledEntries(entries);
458412

459-
new InfoItemDialog(activity, item, StreamDialogEntry.getCommands(context),
460-
(dialog, which) -> StreamDialogEntry.clickOn(which, this, item)).show();
413+
final InfoItemDialog.Builder dialogBuilder = new InfoItemDialog.Builder(
414+
activity, this, item);
415+
416+
dialogBuilder.addEnqueueEntriesIfNeeded();
417+
dialogBuilder.addStartHereEntries();
418+
dialogBuilder.addAllEntries(
419+
StreamDialogDefaultEntry.APPEND_PLAYLIST,
420+
StreamDialogDefaultEntry.SHARE,
421+
StreamDialogDefaultEntry.OPEN_IN_BROWSER
422+
);
423+
dialogBuilder.addPlayWithKodiEntryIfNeeded();
424+
dialogBuilder.addMarkAsWatchedEntryIfNeeded(item.getStreamType());
425+
dialogBuilder.addChannelDetailsEntryIfPossible();
426+
427+
dialogBuilder.create().show();
461428
}
462429

463430
/*//////////////////////////////////////////////////////////////////////////

app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java

Lines changed: 17 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.schabi.newpipe.fragments.list.playlist;
22

3-
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
43
import static org.schabi.newpipe.ktx.ViewUtils.animate;
54
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
65

@@ -36,24 +35,20 @@
3635
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
3736
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
3837
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
39-
import org.schabi.newpipe.extractor.stream.StreamType;
4038
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
4139
import org.schabi.newpipe.info_list.InfoItemDialog;
4240
import org.schabi.newpipe.local.playlist.RemotePlaylistManager;
4341
import org.schabi.newpipe.player.MainPlayer.PlayerType;
44-
import org.schabi.newpipe.player.helper.PlayerHolder;
4542
import org.schabi.newpipe.player.playqueue.PlayQueue;
4643
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
4744
import org.schabi.newpipe.util.ExtractorHelper;
4845
import org.schabi.newpipe.util.Localization;
4946
import org.schabi.newpipe.util.NavigationHelper;
5047
import org.schabi.newpipe.util.PicassoHelper;
51-
import org.schabi.newpipe.util.StreamDialogEntry;
52-
import org.schabi.newpipe.util.external_communication.KoreUtils;
48+
import org.schabi.newpipe.util.StreamDialogDefaultEntry;
5349
import org.schabi.newpipe.util.external_communication.ShareUtils;
5450

5551
import java.util.ArrayList;
56-
import java.util.Arrays;
5752
import java.util.List;
5853
import java.util.concurrent.atomic.AtomicBoolean;
5954
import java.util.function.Supplier;
@@ -147,53 +142,26 @@ protected void showStreamDialog(final StreamInfoItem item) {
147142
return;
148143
}
149144

150-
final ArrayList<StreamDialogEntry> entries = new ArrayList<>();
145+
final InfoItemDialog.Builder dialogBuilder = new InfoItemDialog.Builder(
146+
activity, this, item);
151147

152-
if (PlayerHolder.getInstance().isPlayQueueReady()) {
153-
entries.add(StreamDialogEntry.enqueue);
154-
155-
if (PlayerHolder.getInstance().getQueueSize() > 1) {
156-
entries.add(StreamDialogEntry.enqueue_next);
157-
}
158-
}
159-
160-
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
161-
entries.addAll(Arrays.asList(
162-
StreamDialogEntry.start_here_on_background,
163-
StreamDialogEntry.append_playlist,
164-
StreamDialogEntry.share
165-
));
166-
} else {
167-
entries.addAll(Arrays.asList(
168-
StreamDialogEntry.start_here_on_background,
169-
StreamDialogEntry.start_here_on_popup,
170-
StreamDialogEntry.append_playlist,
171-
StreamDialogEntry.share
172-
));
173-
}
174-
entries.add(StreamDialogEntry.open_in_browser);
175-
if (KoreUtils.shouldShowPlayWithKodi(context, item.getServiceId())) {
176-
entries.add(StreamDialogEntry.play_with_kodi);
177-
}
178-
179-
// show "mark as watched" only when watch history is enabled
180-
if (StreamDialogEntry.shouldAddMarkAsWatched(item.getStreamType(), context)) {
181-
entries.add(
182-
StreamDialogEntry.mark_as_watched
183-
);
184-
}
185-
if (!isNullOrEmpty(item.getUploaderUrl())) {
186-
entries.add(StreamDialogEntry.show_channel_details);
187-
}
148+
dialogBuilder.addEnqueueEntriesIfNeeded();
149+
dialogBuilder.addStartHereEntries();
150+
dialogBuilder.addAllEntries(
151+
StreamDialogDefaultEntry.APPEND_PLAYLIST,
152+
StreamDialogDefaultEntry.SHARE,
153+
StreamDialogDefaultEntry.OPEN_IN_BROWSER
154+
);
155+
dialogBuilder.addPlayWithKodiEntryIfNeeded();
156+
dialogBuilder.addMarkAsWatchedEntryIfNeeded(item.getStreamType());
157+
dialogBuilder.addChannelDetailsEntryIfPossible();
188158

189-
StreamDialogEntry.setEnabledEntries(entries);
159+
dialogBuilder.setAction(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND,
160+
(fragment, infoItem) -> NavigationHelper.playOnBackgroundPlayer(
161+
context, getPlayQueueStartingAt(infoItem), true));
190162

191-
StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItem) ->
192-
NavigationHelper.playOnBackgroundPlayer(context,
193-
getPlayQueueStartingAt(infoItem), true));
163+
dialogBuilder.create().show();
194164

195-
new InfoItemDialog(activity, item, StreamDialogEntry.getCommands(context),
196-
(dialog, which) -> StreamDialogEntry.clickOn(which, this, item)).show();
197165
}
198166

199167
@Override
Lines changed: 128 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,164 @@
11
package org.schabi.newpipe.info_list;
22

3+
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
4+
35
import android.app.Activity;
46
import android.content.DialogInterface;
57
import android.view.View;
68
import android.widget.TextView;
79

810
import androidx.annotation.NonNull;
9-
import androidx.annotation.Nullable;
1011
import androidx.appcompat.app.AlertDialog;
12+
import androidx.fragment.app.Fragment;
13+
import androidx.preference.PreferenceManager;
1114

1215
import org.schabi.newpipe.R;
1316
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
17+
import org.schabi.newpipe.extractor.stream.StreamType;
18+
import org.schabi.newpipe.player.helper.PlayerHolder;
19+
import org.schabi.newpipe.util.StreamDialogDefaultEntry;
20+
import org.schabi.newpipe.util.StreamDialogEntry;
21+
import org.schabi.newpipe.util.external_communication.KoreUtils;
1422

15-
public class InfoItemDialog {
16-
private final AlertDialog dialog;
23+
import java.util.ArrayList;
24+
import java.util.List;
1725

18-
public InfoItemDialog(@NonNull final Activity activity,
19-
@NonNull final StreamInfoItem info,
20-
@NonNull final String[] commands,
21-
@NonNull final DialogInterface.OnClickListener actions) {
22-
this(activity, commands, actions, info.getName(), info.getUploaderName());
23-
}
26+
/**
27+
* Dialog with actions for a {@link StreamInfoItem}.
28+
* This dialog is mostly used for longpress context menus.
29+
*/
30+
public final class InfoItemDialog {
31+
private final AlertDialog dialog;
2432

25-
public InfoItemDialog(@NonNull final Activity activity,
26-
@NonNull final String[] commands,
27-
@NonNull final DialogInterface.OnClickListener actions,
28-
@NonNull final String title,
29-
@Nullable final String additionalDetail) {
33+
private InfoItemDialog(@NonNull final Activity activity,
34+
@NonNull final Fragment fragment,
35+
@NonNull final StreamInfoItem info,
36+
@NonNull final List<StreamDialogEntry> entries) {
3037

3138
final View bannerView = View.inflate(activity, R.layout.dialog_title, null);
3239
bannerView.setSelected(true);
3340

3441
final TextView titleView = bannerView.findViewById(R.id.itemTitleView);
35-
titleView.setText(title);
42+
titleView.setText(info.getName());
3643

3744
final TextView detailsView = bannerView.findViewById(R.id.itemAdditionalDetails);
38-
if (additionalDetail != null) {
39-
detailsView.setText(additionalDetail);
45+
if (info.getUploaderName() != null) {
46+
detailsView.setText(info.getUploaderName());
4047
detailsView.setVisibility(View.VISIBLE);
4148
} else {
4249
detailsView.setVisibility(View.GONE);
4350
}
4451

52+
final String[] items = entries.stream()
53+
.map(entry -> entry.getString(activity)).toArray(String[]::new);
54+
55+
final DialogInterface.OnClickListener action = (d, index) ->
56+
entries.get(index).action.onClick(fragment, info);
57+
4558
dialog = new AlertDialog.Builder(activity)
4659
.setCustomTitle(bannerView)
47-
.setItems(commands, actions)
60+
.setItems(items, action)
4861
.create();
62+
4963
}
5064

5165
public void show() {
5266
dialog.show();
5367
}
68+
69+
/**
70+
* <p>Builder to generate a {@link InfoItemDialog}.</p>
71+
* Use {@link #addEntry(StreamDialogDefaultEntry)}
72+
* and {@link #addAllEntries(StreamDialogDefaultEntry...)} to add options to the dialog.
73+
* <br>
74+
* Custom actions for entries can be set using
75+
* {@link #setAction(StreamDialogDefaultEntry, StreamDialogEntry.StreamDialogEntryAction)}.
76+
*/
77+
public static class Builder {
78+
@NonNull private final Activity activity;
79+
@NonNull private final StreamInfoItem info;
80+
@NonNull private final Fragment fragment;
81+
@NonNull private final List<StreamDialogEntry> entries = new ArrayList<>();
82+
83+
public Builder(@NonNull final Activity activity,
84+
@NonNull final Fragment fragment,
85+
@NonNull final StreamInfoItem info) {
86+
this.activity = activity;
87+
this.fragment = fragment;
88+
this.info = info;
89+
}
90+
91+
public void addEntry(@NonNull final StreamDialogDefaultEntry entry) {
92+
entries.add(entry.toStreamDialogEntry());
93+
}
94+
95+
public void addAllEntries(@NonNull final StreamDialogDefaultEntry... newEntries) {
96+
for (final StreamDialogDefaultEntry entry: newEntries) {
97+
this.entries.add(entry.toStreamDialogEntry());
98+
}
99+
}
100+
101+
public void setAction(@NonNull final StreamDialogDefaultEntry entry,
102+
@NonNull final StreamDialogEntry.StreamDialogEntryAction action) {
103+
for (int i = 0; i < entries.size(); i++) {
104+
if (entries.get(i).resource == entry.resource) {
105+
entries.set(i, new StreamDialogEntry(entry.resource, action));
106+
}
107+
}
108+
}
109+
110+
public void addChannelDetailsEntryIfPossible() {
111+
if (!isNullOrEmpty(info.getUploaderUrl())) {
112+
addEntry(StreamDialogDefaultEntry.SHOW_CHANNEL_DETAILS);
113+
}
114+
}
115+
116+
public void addEnqueueEntriesIfNeeded() {
117+
if (PlayerHolder.getInstance().isPlayerOpen()) {
118+
addEntry(StreamDialogDefaultEntry.ENQUEUE);
119+
120+
if (PlayerHolder.getInstance().getQueueSize() > 1) {
121+
addEntry(StreamDialogDefaultEntry.ENQUEUE_NEXT);
122+
}
123+
}
124+
}
125+
126+
public void addStartHereEntries() {
127+
addEntry(StreamDialogDefaultEntry.START_HERE_ON_BACKGROUND);
128+
if (info.getStreamType() != StreamType.AUDIO_STREAM
129+
&& info.getStreamType() != StreamType.AUDIO_LIVE_STREAM) {
130+
addEntry(StreamDialogDefaultEntry.START_HERE_ON_POPUP);
131+
}
132+
}
133+
134+
/**
135+
* Adds {@link StreamDialogDefaultEntry.MARK_AS_WATCHED} if the watch history is enabled
136+
* and the stream is not a livestream.
137+
* @param streamType the item's stream type
138+
*/
139+
public void addMarkAsWatchedEntryIfNeeded(final StreamType streamType) {
140+
final boolean isWatchHistoryEnabled = PreferenceManager
141+
.getDefaultSharedPreferences(activity)
142+
.getBoolean(activity.getString(R.string.enable_watch_history_key), false);
143+
if (streamType != StreamType.AUDIO_LIVE_STREAM
144+
&& streamType != StreamType.LIVE_STREAM
145+
&& isWatchHistoryEnabled) {
146+
addEntry(StreamDialogDefaultEntry.MARK_AS_WATCHED);
147+
}
148+
}
149+
150+
public void addPlayWithKodiEntryIfNeeded() {
151+
if (KoreUtils.shouldShowPlayWithKodi(activity, info.getServiceId())) {
152+
addEntry(StreamDialogDefaultEntry.PLAY_WITH_KODI);
153+
}
154+
}
155+
156+
/**
157+
* Creates the {@link InfoItemDialog}.
158+
* @return a new instance of {@link InfoItemDialog}
159+
*/
160+
public InfoItemDialog create() {
161+
return new InfoItemDialog(this.activity, this.fragment, this.info, this.entries);
162+
}
163+
}
54164
}

0 commit comments

Comments
 (0)