Skip to content

Commit 62804be

Browse files
committed
feat(podcast): prioritize background audio and podcast recommendations flow
1 parent 1424b52 commit 62804be

5 files changed

Lines changed: 245 additions & 11 deletions

File tree

app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
import org.schabi.newpipe.fragments.list.comments.CommentReplyFragment;
7979
import org.schabi.newpipe.fragments.list.comments.CommentsFragment;
8080
import org.schabi.newpipe.fragments.list.comments.CommentsFragmentContainer;
81+
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
8182
import org.schabi.newpipe.fragments.list.sponsorblock.SponsorBlockFragment;
8283
import org.schabi.newpipe.fragments.list.sponsorblock.SponsorBlockFragmentListener;
8384
import org.schabi.newpipe.fragments.list.videos.RelatedItemsFragment;
@@ -208,6 +209,8 @@ private void onSharedPreferencesChanged(final SharedPreferences sharedPreference
208209
@State
209210
protected boolean autoPlayEnabled = true;
210211
@State
212+
protected boolean forceHideRelatedItems = false;
213+
@State
211214
SponsorBlockMode currentSponsorBlockMode = null;
212215

213216
@Nullable
@@ -1080,11 +1083,11 @@ private void initTabs() {
10801083
}
10811084
}
10821085

1083-
if (showRelatedItems && binding.relatedItemsLayout == null) {
1086+
if (shouldShowRelatedItemsTab() && binding.relatedItemsLayout == null) {
10841087
// temp empty fragment. will be updated in handleResult
10851088
try {
10861089
pageAdapter.addFragment(EmptyFragment.newInstance(false), RELATED_TAB_TAG);
1087-
tabIcons.add(R.drawable.ic_art_track);
1090+
tabIcons.add(forceHideRelatedItems ? R.drawable.ic_podcast : R.drawable.ic_art_track);
10881091
tabContentDescriptions.add(R.string.related_items_tab_description);
10891092
} catch (IllegalStateException e) {
10901093
// Fragment already added
@@ -1143,13 +1146,15 @@ private void updateTabIconsAndContentDescriptions() {
11431146
}
11441147

11451148
private void updateTabs(@NonNull final StreamInfo info) {
1146-
if (info.isRoundPlayStream() || (showRelatedItems && info.isSupportRelatedItems())) {
1149+
if (info.isRoundPlayStream() || (shouldShowRelatedItemsTab()
1150+
&& (forceHideRelatedItems || info.isSupportRelatedItems()))) {
11471151
try {
1152+
final Fragment relatedItemsFragment = getRelatedItemsFragment(info);
11481153
if (binding.relatedItemsLayout == null) { // phone
1149-
pageAdapter.updateItem(RELATED_TAB_TAG, RelatedItemsFragment.getInstance(info));
1154+
pageAdapter.updateItem(RELATED_TAB_TAG, relatedItemsFragment);
11501155
} else { // tablet + TV
11511156
getChildFragmentManager().beginTransaction()
1152-
.replace(R.id.relatedItemsLayout, RelatedItemsFragment.getInstance(info))
1157+
.replace(R.id.relatedItemsLayout, relatedItemsFragment)
11531158
.commitAllowingStateLoss();
11541159
binding.relatedItemsLayout.setVisibility(
11551160
isPlayerAvailable() && player.isFullscreen() ? View.GONE : View.VISIBLE);
@@ -1159,7 +1164,7 @@ private void updateTabs(@NonNull final StreamInfo info) {
11591164
Log.e(TAG, "updateTabs() error updating related tab", e);
11601165
}
11611166
}
1162-
if (!info.isSupportRelatedItems()){
1167+
if (!forceHideRelatedItems && !info.isSupportRelatedItems()){
11631168
int index = pageAdapter.getItemPositionByTitle(RELATED_TAB_TAG);
11641169
if(index != -1){
11651170
pageAdapter.removeItem(index);
@@ -1229,6 +1234,24 @@ private boolean shouldShowComments() {
12291234
}
12301235
}
12311236

1237+
private boolean shouldShowRelatedItemsTab() {
1238+
return showRelatedItems;
1239+
}
1240+
1241+
@NonNull
1242+
private Fragment getRelatedItemsFragment(@NonNull final StreamInfo info) {
1243+
if (!forceHideRelatedItems) {
1244+
return RelatedItemsFragment.getInstance(info);
1245+
}
1246+
1247+
try {
1248+
return KioskFragment.getInstance(info.getServiceId(), "Recommended Podcasts");
1249+
} catch (final Exception e) {
1250+
Log.e(TAG, "getRelatedItemsFragment() fallback to related items", e);
1251+
return RelatedItemsFragment.getInstance(info);
1252+
}
1253+
}
1254+
12321255
private boolean shouldShowSponsorBlock() {
12331256
try {
12341257
return showSponsorBlock && NewPipe.getService(serviceId)
@@ -1467,6 +1490,10 @@ public void setAutoPlay(final boolean autoPlay) {
14671490
this.autoPlayEnabled = autoPlay;
14681491
}
14691492

1493+
public void setForceHideRelatedItems(final boolean hideRelatedItems) {
1494+
this.forceHideRelatedItems = hideRelatedItems;
1495+
}
1496+
14701497
private void startOnExternalPlayer(@NonNull final Context context,
14711498
@NonNull final StreamInfo info,
14721499
@NonNull final Stream selectedStream) {
@@ -1721,7 +1748,7 @@ public void showLoading() {
17211748
binding.detailSecondaryControlPanel.setVisibility(View.GONE);
17221749

17231750
if (binding.relatedItemsLayout != null) {
1724-
if (showRelatedItems) {
1751+
if (shouldShowRelatedItemsTab()) {
17251752
binding.relatedItemsLayout.setVisibility(
17261753
isPlayerAvailable() && player.isFullscreen() ? View.GONE : View.INVISIBLE);
17271754
} else {
@@ -2170,7 +2197,8 @@ public void onFullscreenStateChanged(final boolean fullscreen) {
21702197
}
21712198

21722199
if (binding.relatedItemsLayout != null) {
2173-
binding.relatedItemsLayout.setVisibility(fullscreen ? View.GONE : View.VISIBLE);
2200+
binding.relatedItemsLayout.setVisibility(fullscreen || !shouldShowRelatedItemsTab()
2201+
? View.GONE : View.VISIBLE);
21742202
}
21752203
scrollToTop();
21762204

app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelTabFragment.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,24 @@
1313
import org.schabi.newpipe.extractor.InfoItem;
1414
import org.schabi.newpipe.extractor.ListExtractor;
1515
import org.schabi.newpipe.extractor.channel.ChannelTabInfo;
16+
import org.schabi.newpipe.extractor.linkhandler.ChannelTabs;
1617
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
18+
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
19+
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
1720
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
21+
import org.schabi.newpipe.player.playqueue.PlayQueue;
22+
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
1823
import org.schabi.newpipe.util.Constants;
1924
import org.schabi.newpipe.util.ExtractorHelper;
25+
import org.schabi.newpipe.util.NavigationHelper;
26+
import org.schabi.newpipe.util.OnClickGesture;
2027

2128
import icepick.State;
2229
import io.reactivex.rxjava3.core.Single;
2330

31+
import java.util.ArrayList;
32+
import java.util.List;
33+
2434
public class ChannelTabFragment extends BaseListInfoFragment<InfoItem, ChannelTabInfo> {
2535

2636
@State
@@ -73,8 +83,77 @@ protected Single<ListExtractor.InfoItemsPage<InfoItem>> loadMoreItemsLogic() {
7383
return ExtractorHelper.getMoreChannelTabItems(serviceId, tabHandler, currentNextPage);
7484
}
7585

86+
@Override
87+
protected void initListeners() {
88+
super.initListeners();
89+
90+
if (!isPodcastsTab()) {
91+
return;
92+
}
93+
94+
infoListAdapter.setOnStreamSelectedListener(new OnClickGesture<>() {
95+
@Override
96+
public void selected(final StreamInfoItem selectedItem) {
97+
onItemSelected(selectedItem);
98+
final PlayQueue playQueue = getPodcastsStreamPlayQueueStartingAt(selectedItem);
99+
NavigationHelper.playOnBackgroundPlayer(requireContext(), playQueue, true);
100+
NavigationHelper.openVideoDetailFragment(requireContext(), getFM(),
101+
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName(),
102+
playQueue, false, false, true);
103+
}
104+
105+
@Override
106+
public void held(final StreamInfoItem selectedItem) {
107+
showInfoItemDialog(selectedItem);
108+
}
109+
});
110+
111+
infoListAdapter.setOnPlaylistSelectedListener(new OnClickGesture<>() {
112+
@Override
113+
public void selected(final PlaylistInfoItem selectedItem) {
114+
onItemSelected(selectedItem);
115+
NavigationHelper.openPlaylistFragment(getFM(),
116+
selectedItem.getServiceId(),
117+
selectedItem.getUrl(),
118+
selectedItem.getName(),
119+
true);
120+
}
121+
122+
@Override
123+
public void held(final PlaylistInfoItem selectedItem) {
124+
NavigationHelper.openPlaylistFragment(getFM(),
125+
selectedItem.getServiceId(),
126+
selectedItem.getUrl(),
127+
selectedItem.getName(),
128+
true);
129+
}
130+
});
131+
}
132+
76133
@Override
77134
public void setTitle(final String title) {
78135
super.setTitle(channelName);
79136
}
137+
138+
private boolean isPodcastsTab() {
139+
if (tabHandler == null || tabHandler.getContentFilters().isEmpty()) {
140+
return false;
141+
}
142+
return ChannelTabs.PODCASTS.equals(tabHandler.getContentFilters().get(0).getName());
143+
}
144+
145+
private PlayQueue getPodcastsStreamPlayQueueStartingAt(
146+
@NonNull final StreamInfoItem selectedItem) {
147+
final List<StreamInfoItem> streamItems = new ArrayList<>();
148+
for (final InfoItem item : infoListAdapter.getItemsList()) {
149+
if (item instanceof StreamInfoItem) {
150+
streamItems.add((StreamInfoItem) item);
151+
}
152+
}
153+
154+
if (streamItems.isEmpty()) {
155+
return new SinglePlayQueue(selectedItem);
156+
}
157+
return new SinglePlayQueue(streamItems, Math.max(streamItems.indexOf(selectedItem), 0));
158+
}
80159
}

app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.schabi.newpipe.R;
1515
import org.schabi.newpipe.error.ErrorInfo;
1616
import org.schabi.newpipe.error.UserAction;
17+
import org.schabi.newpipe.extractor.InfoItem;
1718
import org.schabi.newpipe.extractor.ListExtractor;
1819
import org.schabi.newpipe.extractor.NewPipe;
1920
import org.schabi.newpipe.extractor.StreamingService;
@@ -23,9 +24,16 @@
2324
import org.schabi.newpipe.extractor.localization.ContentCountry;
2425
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
2526
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
27+
import org.schabi.newpipe.player.playqueue.PlayQueue;
28+
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
2629
import org.schabi.newpipe.util.ExtractorHelper;
2730
import org.schabi.newpipe.util.KioskTranslator;
2831
import org.schabi.newpipe.util.Localization;
32+
import org.schabi.newpipe.util.NavigationHelper;
33+
import org.schabi.newpipe.util.OnClickGesture;
34+
35+
import java.util.ArrayList;
36+
import java.util.List;
2937

3038
import icepick.State;
3139
import io.reactivex.rxjava3.core.Single;
@@ -55,6 +63,8 @@
5563
*/
5664

5765
public class KioskFragment extends BaseListInfoFragment<StreamInfoItem, KioskInfo> {
66+
private static final String RECOMMENDED_PODCASTS_KIOSK_ID = "Recommended Podcasts";
67+
5868
@State
5969
String kioskId = "";
6070
String kioskTranslatedName;
@@ -99,6 +109,40 @@ public void onCreate(final Bundle savedInstanceState) {
99109
contentCountry = Localization.getPreferredContentCountry(requireContext());
100110
}
101111

112+
@Override
113+
protected void initViews(@NonNull final View rootView, @Nullable final Bundle savedInstanceState) {
114+
super.initViews(rootView, savedInstanceState);
115+
if (isRecommendedPodcastsKiosk()) {
116+
infoListAdapter.setUseMiniVariant(true);
117+
}
118+
}
119+
120+
@Override
121+
protected void initListeners() {
122+
super.initListeners();
123+
124+
if (!isRecommendedPodcastsKiosk()) {
125+
return;
126+
}
127+
128+
infoListAdapter.setOnStreamSelectedListener(new OnClickGesture<>() {
129+
@Override
130+
public void selected(final StreamInfoItem selectedItem) {
131+
onItemSelected(selectedItem);
132+
final PlayQueue playQueue = getPodcastRecommendationsPlayQueueStartingAt(selectedItem);
133+
NavigationHelper.playOnBackgroundPlayer(requireContext(), playQueue, true);
134+
NavigationHelper.openVideoDetailFragment(requireContext(), getFM(),
135+
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName(),
136+
playQueue, false, false, true);
137+
}
138+
139+
@Override
140+
public void held(final StreamInfoItem selectedItem) {
141+
showInfoItemDialog(selectedItem);
142+
}
143+
});
144+
}
145+
102146
@Override
103147
public void onResume() {
104148
super.onResume();
@@ -161,4 +205,24 @@ public void handleResult(@NonNull final KioskInfo result) {
161205
name = kioskTranslatedName;
162206
setTitle(kioskTranslatedName);
163207
}
208+
209+
private boolean isRecommendedPodcastsKiosk() {
210+
return RECOMMENDED_PODCASTS_KIOSK_ID.equals(kioskId);
211+
}
212+
213+
@NonNull
214+
private PlayQueue getPodcastRecommendationsPlayQueueStartingAt(
215+
@NonNull final StreamInfoItem selectedItem) {
216+
final List<StreamInfoItem> streamItems = new ArrayList<>();
217+
for (final InfoItem item : infoListAdapter.getItemsList()) {
218+
if (item instanceof StreamInfoItem) {
219+
streamItems.add((StreamInfoItem) item);
220+
}
221+
}
222+
223+
if (streamItems.isEmpty()) {
224+
return new SinglePlayQueue(selectedItem);
225+
}
226+
return new SinglePlayQueue(streamItems, Math.max(streamItems.indexOf(selectedItem), 0));
227+
}
164228
}

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.schabi.newpipe.util.ExtractorHelper;
5353
import org.schabi.newpipe.util.Localization;
5454
import org.schabi.newpipe.util.NavigationHelper;
55+
import org.schabi.newpipe.util.OnClickGesture;
5556
import org.schabi.newpipe.util.PicassoHelper;
5657
import org.schabi.newpipe.info_list.dialog.StreamDialogDefaultEntry;
5758
import org.schabi.newpipe.util.external_communication.ShareUtils;
@@ -67,11 +68,15 @@
6768
import io.reactivex.rxjava3.core.Single;
6869
import io.reactivex.rxjava3.disposables.CompositeDisposable;
6970
import io.reactivex.rxjava3.disposables.Disposable;
71+
import icepick.State;
7072

7173
public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, PlaylistInfo> {
7274

7375
private static final String PICASSO_PLAYLIST_TAG = "PICASSO_PLAYLIST_TAG";
7476

77+
@State
78+
protected boolean podcastMode;
79+
7580
private CompositeDisposable disposables;
7681
private Subscription bookmarkReactor;
7782
private AtomicBoolean isBookmarkButtonReady;
@@ -94,8 +99,15 @@ public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, Playl
9499

95100
public static PlaylistFragment getInstance(final int serviceId, final String url,
96101
final String name) {
102+
return getInstance(serviceId, url, name, false);
103+
}
104+
105+
public static PlaylistFragment getInstance(final int serviceId, final String url,
106+
final String name,
107+
final boolean podcastMode) {
97108
final PlaylistFragment instance = new PlaylistFragment();
98109
instance.setInitialData(serviceId, url, name);
110+
instance.podcastMode = podcastMode;
99111
return instance;
100112
}
101113

@@ -145,6 +157,32 @@ protected void initViews(final View rootView, final Bundle savedInstanceState) {
145157
infoListAdapter.setUseMiniVariant(true);
146158
}
147159

160+
@Override
161+
protected void initListeners() {
162+
super.initListeners();
163+
164+
if (!podcastMode) {
165+
return;
166+
}
167+
168+
infoListAdapter.setOnStreamSelectedListener(new OnClickGesture<>() {
169+
@Override
170+
public void selected(final StreamInfoItem selectedItem) {
171+
onItemSelected(selectedItem);
172+
final PlayQueue playQueue = getPlayQueueStartingAt(selectedItem);
173+
NavigationHelper.playOnBackgroundPlayer(requireContext(), playQueue, true);
174+
NavigationHelper.openVideoDetailFragment(requireContext(), getFM(),
175+
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName(),
176+
playQueue, false, false, true);
177+
}
178+
179+
@Override
180+
public void held(final StreamInfoItem selectedItem) {
181+
showInfoItemDialog(selectedItem);
182+
}
183+
});
184+
}
185+
148186
private PlayQueue getPlayQueueStartingAt(final StreamInfoItem infoItem) {
149187
return getPlayQueue(Math.max(infoListAdapter.getItemsList().indexOf(infoItem), 0));
150188
}

0 commit comments

Comments
 (0)