mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2024-12-23 16:40:32 +00:00
-Added play queue dropdown to channel info items.
-Added play queue dropdown to playlist info items. -Added Channel Play Queue. -Renamed External Play Queue to Playlist Play Queue. -Modified Playlist Play Queue to allow loading from initial state.
This commit is contained in:
parent
87febf8679
commit
b8a17580c5
@ -1,6 +1,5 @@
|
||||
package org.schabi.newpipe.fragments.list.playlist;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
@ -24,10 +23,7 @@ import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||
import org.schabi.newpipe.player.BackgroundPlayer;
|
||||
import org.schabi.newpipe.player.MainVideoPlayer;
|
||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||
import org.schabi.newpipe.playlist.ExternalPlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlaylistPlayQueue;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
@ -185,7 +181,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
}
|
||||
|
||||
private PlayQueue getPlayQueue() {
|
||||
return new ExternalPlayQueue(
|
||||
return new PlaylistPlayQueue(
|
||||
currentInfo.service_id,
|
||||
currentInfo.url,
|
||||
currentInfo.next_streams_url,
|
||||
|
@ -1,7 +1,11 @@
|
||||
package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||
@ -10,7 +14,9 @@ import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
import org.schabi.newpipe.playlist.ChannelPlayQueue;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
|
||||
import de.hdodenhof.circleimageview.CircleImageView;
|
||||
|
||||
@ -18,6 +24,7 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
|
||||
public final CircleImageView itemThumbnailView;
|
||||
public final TextView itemTitleView;
|
||||
public final TextView itemAdditionalDetailView;
|
||||
public final ImageButton itemActionDropdown;
|
||||
|
||||
ChannelMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
|
||||
super(infoItemBuilder, layoutId, parent);
|
||||
@ -25,6 +32,7 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
|
||||
itemThumbnailView = itemView.findViewById(R.id.itemThumbnailView);
|
||||
itemTitleView = itemView.findViewById(R.id.itemTitleView);
|
||||
itemAdditionalDetailView = itemView.findViewById(R.id.itemAdditionalDetails);
|
||||
itemActionDropdown = itemView.findViewById(R.id.itemActionDropdown);
|
||||
}
|
||||
|
||||
public ChannelMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
|
||||
@ -50,6 +58,55 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
enableActionDropdown(item);
|
||||
}
|
||||
|
||||
private void enableActionDropdown(final ChannelInfoItem item) {
|
||||
itemActionDropdown.setVisibility(View.VISIBLE);
|
||||
itemActionDropdown.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
final PopupMenu actionMenu = getStreamDropdown(itemBuilder.getContext(), itemActionDropdown, item);
|
||||
if (itemBuilder.getOnChannelSelectedListener() != null) {
|
||||
itemBuilder.getOnChannelSelectedListener().dropdownClicked(item, actionMenu);
|
||||
}
|
||||
actionMenu.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private PopupMenu getStreamDropdown(final Context context, final View anchor, final ChannelInfoItem infoItem) {
|
||||
PopupMenu actionMenu = new PopupMenu(context, anchor);
|
||||
|
||||
final MenuItem mainPlay = actionMenu.getMenu().add(R.string.play_all);
|
||||
mainPlay.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
NavigationHelper.playOnMainPlayer(context, new ChannelPlayQueue(infoItem));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final MenuItem popupPlay = actionMenu.getMenu().add(R.string.controls_popup_title);
|
||||
popupPlay.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
NavigationHelper.playOnPopupPlayer(context, new ChannelPlayQueue(infoItem));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final MenuItem backgroundPlay = actionMenu.getMenu().add(R.string.controls_background_title);
|
||||
backgroundPlay.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
NavigationHelper.playOnBackgroundPlayer(context, new ChannelPlayQueue(infoItem));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return actionMenu;
|
||||
}
|
||||
|
||||
protected String getDetailLine(final ChannelInfoItem item) {
|
||||
|
@ -1,8 +1,12 @@
|
||||
package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||
@ -11,12 +15,15 @@ import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
import org.schabi.newpipe.playlist.PlaylistPlayQueue;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
|
||||
public class PlaylistInfoItemHolder extends InfoItemHolder {
|
||||
public final ImageView itemThumbnailView;
|
||||
public final TextView itemStreamCountView;
|
||||
public final TextView itemTitleView;
|
||||
public final TextView itemUploaderView;
|
||||
public final ImageButton itemActionDropdown;
|
||||
|
||||
public PlaylistInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
|
||||
super(infoItemBuilder, R.layout.list_playlist_item, parent);
|
||||
@ -25,6 +32,7 @@ public class PlaylistInfoItemHolder extends InfoItemHolder {
|
||||
itemTitleView = itemView.findViewById(R.id.itemTitleView);
|
||||
itemStreamCountView = itemView.findViewById(R.id.itemStreamCountView);
|
||||
itemUploaderView = itemView.findViewById(R.id.itemUploaderView);
|
||||
itemActionDropdown = itemView.findViewById(R.id.itemActionDropdown);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -47,8 +55,56 @@ public class PlaylistInfoItemHolder extends InfoItemHolder {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
enableActionDropdown(item);
|
||||
}
|
||||
|
||||
private void enableActionDropdown(final PlaylistInfoItem item) {
|
||||
itemActionDropdown.setVisibility(View.VISIBLE);
|
||||
itemActionDropdown.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
final PopupMenu actionMenu = getStreamDropdown(itemBuilder.getContext(), itemActionDropdown, item);
|
||||
if (itemBuilder.getOnPlaylistSelectedListener() != null) {
|
||||
itemBuilder.getOnPlaylistSelectedListener().dropdownClicked(item, actionMenu);
|
||||
}
|
||||
actionMenu.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private PopupMenu getStreamDropdown(final Context context, final View anchor, final PlaylistInfoItem infoItem) {
|
||||
PopupMenu actionMenu = new PopupMenu(context, anchor);
|
||||
|
||||
final MenuItem mainPlay = actionMenu.getMenu().add(R.string.play_all);
|
||||
mainPlay.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
NavigationHelper.playOnMainPlayer(context, new PlaylistPlayQueue(infoItem));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final MenuItem popupPlay = actionMenu.getMenu().add(R.string.controls_popup_title);
|
||||
popupPlay.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
NavigationHelper.playOnPopupPlayer(context, new PlaylistPlayQueue(infoItem));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final MenuItem backgroundPlay = actionMenu.getMenu().add(R.string.controls_background_title);
|
||||
backgroundPlay.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
NavigationHelper.playOnBackgroundPlayer(context, new PlaylistPlayQueue(infoItem));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return actionMenu;
|
||||
}
|
||||
/**
|
||||
* Display options for playlist thumbnails
|
||||
*/
|
||||
|
@ -2,7 +2,6 @@ package org.schabi.newpipe.info_list.holder;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -10,7 +9,6 @@ import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||
|
||||
@ -19,9 +17,6 @@ import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||
import org.schabi.newpipe.info_list.InfoItemBuilder;
|
||||
import org.schabi.newpipe.player.BackgroundPlayer;
|
||||
import org.schabi.newpipe.player.PopupVideoPlayer;
|
||||
import org.schabi.newpipe.playlist.PlayQueue;
|
||||
import org.schabi.newpipe.playlist.SinglePlayQueue;
|
||||
import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
@ -91,11 +86,13 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
||||
case AUDIO_LIVE_STREAM:
|
||||
case NONE:
|
||||
default:
|
||||
disableActionDropdown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void enableActionDropdown(final StreamInfoItem item) {
|
||||
itemActionDropdown.setClickable(true);
|
||||
itemActionDropdown.setVisibility(View.VISIBLE);
|
||||
itemActionDropdown.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
@ -109,10 +106,34 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
||||
});
|
||||
}
|
||||
|
||||
private void disableActionDropdown() {
|
||||
itemActionDropdown.setVisibility(View.GONE);
|
||||
itemActionDropdown.setClickable(false);
|
||||
itemActionDropdown.setOnClickListener(null);
|
||||
}
|
||||
|
||||
private PopupMenu getStreamDropdown(final Context context, final View anchor, final StreamInfoItem infoItem) {
|
||||
PopupMenu actionMenu = new PopupMenu(context, anchor);
|
||||
|
||||
final MenuItem mainPlay = actionMenu.getMenu().add(R.string.play_btn_text);
|
||||
final MenuItem backgroundEnqueue = actionMenu.getMenu().add(R.string.enqueue_on_background);
|
||||
backgroundEnqueue.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(infoItem));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final MenuItem popupEnqueue = actionMenu.getMenu().add(R.string.enqueue_on_popup);
|
||||
popupEnqueue.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(infoItem));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final MenuItem mainPlay = actionMenu.getMenu().add(R.string.play_all);
|
||||
mainPlay.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
@ -139,24 +160,6 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
|
||||
}
|
||||
});
|
||||
|
||||
final MenuItem backgroundEnqueue = actionMenu.getMenu().add(R.string.enqueue_on_background);
|
||||
backgroundEnqueue.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(infoItem));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final MenuItem popupEnqueue = actionMenu.getMenu().add(R.string.enqueue_on_popup);
|
||||
popupEnqueue.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(infoItem));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return actionMenu;
|
||||
}
|
||||
|
||||
|
@ -157,7 +157,7 @@ public class MediaSourceManager {
|
||||
}
|
||||
|
||||
private void onPlayQueueChanged(final PlayQueueEvent event) {
|
||||
if (playQueue.isEmpty()) {
|
||||
if (playQueue.isEmpty() && playQueue.isComplete()) {
|
||||
playbackListener.shutdown();
|
||||
return;
|
||||
}
|
||||
|
@ -0,0 +1,151 @@
|
||||
package org.schabi.newpipe.playlist;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.SingleObserver;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.annotations.NonNull;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public final class ChannelPlayQueue extends PlayQueue {
|
||||
private final String TAG = "ChannelPlayQueue@" + Integer.toHexString(hashCode());
|
||||
|
||||
private boolean isInitial;
|
||||
private boolean isComplete;
|
||||
|
||||
private int serviceId;
|
||||
private String baseUrl;
|
||||
private String nextUrl;
|
||||
|
||||
private transient Disposable fetchReactor;
|
||||
|
||||
public ChannelPlayQueue(final ChannelInfoItem item) {
|
||||
this(item.service_id, item.url, item.url, Collections.<InfoItem>emptyList(), 0);
|
||||
}
|
||||
|
||||
public ChannelPlayQueue(final int serviceId,
|
||||
final String url,
|
||||
final String nextPageUrl,
|
||||
final List<InfoItem> streams,
|
||||
final int index) {
|
||||
super(index, extractChannelItems(streams));
|
||||
|
||||
this.baseUrl = url;
|
||||
this.nextUrl = nextPageUrl;
|
||||
this.serviceId = serviceId;
|
||||
|
||||
this.isInitial = streams.isEmpty();
|
||||
this.isComplete = !isInitial && (nextPageUrl == null || nextPageUrl.isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
return isComplete;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fetch() {
|
||||
if (isInitial) {
|
||||
ExtractorHelper.getChannelInfo(this.serviceId, this.baseUrl, false)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getChannelInitialObserver());
|
||||
} else {
|
||||
ExtractorHelper.getMoreChannelItems(this.serviceId, this.baseUrl, this.nextUrl)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getChannelNextItemsObserver());
|
||||
}
|
||||
}
|
||||
|
||||
private SingleObserver<ChannelInfo> getChannelInitialObserver() {
|
||||
return new SingleObserver<ChannelInfo>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
if (isComplete || (fetchReactor != null && !fetchReactor.isDisposed())) {
|
||||
d.dispose();
|
||||
} else {
|
||||
fetchReactor = d;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(@NonNull ChannelInfo result) {
|
||||
if (!result.has_more_streams) isComplete = true;
|
||||
nextUrl = result.next_streams_url;
|
||||
|
||||
append(extractChannelItems(result.related_streams));
|
||||
|
||||
isInitial = false;
|
||||
fetchReactor.dispose();
|
||||
fetchReactor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e(TAG, "Error fetching more playlist, marking playlist as complete.", e);
|
||||
isComplete = true;
|
||||
append(); // Notify change
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private SingleObserver<ListExtractor.NextItemsResult> getChannelNextItemsObserver() {
|
||||
return new SingleObserver<ListExtractor.NextItemsResult>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
if (isComplete || (fetchReactor != null && !fetchReactor.isDisposed())) {
|
||||
d.dispose();
|
||||
} else {
|
||||
fetchReactor = d;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(@NonNull ListExtractor.NextItemsResult result) {
|
||||
if (!result.hasMoreStreams()) isComplete = true;
|
||||
nextUrl = result.nextItemsUrl;
|
||||
|
||||
append(extractChannelItems(result.nextItemsList));
|
||||
|
||||
fetchReactor.dispose();
|
||||
fetchReactor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e(TAG, "Error fetching more playlist, marking playlist as complete.", e);
|
||||
isComplete = true;
|
||||
append(); // Notify change
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
if (fetchReactor != null) fetchReactor.dispose();
|
||||
}
|
||||
|
||||
private static List<PlayQueueItem> extractChannelItems(final List<InfoItem> infos) {
|
||||
List<PlayQueueItem> result = new ArrayList<>();
|
||||
for (final InfoItem stream : infos) {
|
||||
if (stream instanceof StreamInfoItem) {
|
||||
result.add(new PlayQueueItem((StreamInfoItem) stream));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@ import android.util.Log;
|
||||
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
|
||||
@ -17,9 +19,10 @@ import io.reactivex.annotations.NonNull;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public final class ExternalPlayQueue extends PlayQueue {
|
||||
private final String TAG = "ExternalPlayQueue@" + Integer.toHexString(hashCode());
|
||||
public final class PlaylistPlayQueue extends PlayQueue {
|
||||
private final String TAG = "PlaylistPlayQueue@" + Integer.toHexString(hashCode());
|
||||
|
||||
private boolean isInitial;
|
||||
private boolean isComplete;
|
||||
|
||||
private int serviceId;
|
||||
@ -28,7 +31,11 @@ public final class ExternalPlayQueue extends PlayQueue {
|
||||
|
||||
private transient Disposable fetchReactor;
|
||||
|
||||
public ExternalPlayQueue(final int serviceId,
|
||||
public PlaylistPlayQueue(final PlaylistInfoItem item) {
|
||||
this(item.service_id, item.url, item.url, Collections.<InfoItem>emptyList(), 0);
|
||||
}
|
||||
|
||||
public PlaylistPlayQueue(final int serviceId,
|
||||
final String url,
|
||||
final String nextPageUrl,
|
||||
final List<InfoItem> streams,
|
||||
@ -39,7 +46,8 @@ public final class ExternalPlayQueue extends PlayQueue {
|
||||
this.nextUrl = nextPageUrl;
|
||||
this.serviceId = serviceId;
|
||||
|
||||
this.isComplete = nextPageUrl == null || nextPageUrl.isEmpty();
|
||||
this.isInitial = streams.isEmpty();
|
||||
this.isComplete = !isInitial && (nextPageUrl == null || nextPageUrl.isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -49,13 +57,51 @@ public final class ExternalPlayQueue extends PlayQueue {
|
||||
|
||||
@Override
|
||||
public void fetch() {
|
||||
if (isInitial) {
|
||||
ExtractorHelper.getPlaylistInfo(this.serviceId, this.baseUrl, false)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getInitialPlaylistObserver());
|
||||
} else {
|
||||
ExtractorHelper.getMorePlaylistItems(this.serviceId, this.baseUrl, this.nextUrl)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getPlaylistObserver());
|
||||
.subscribe(getPlaylistNextItemsObserver());
|
||||
}
|
||||
}
|
||||
|
||||
private SingleObserver<ListExtractor.NextItemsResult> getPlaylistObserver() {
|
||||
private SingleObserver<PlaylistInfo> getInitialPlaylistObserver() {
|
||||
return new SingleObserver<PlaylistInfo>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
if (isComplete || (fetchReactor != null && !fetchReactor.isDisposed())) {
|
||||
d.dispose();
|
||||
} else {
|
||||
fetchReactor = d;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(@NonNull PlaylistInfo result) {
|
||||
if (!result.has_more_streams) isComplete = true;
|
||||
nextUrl = result.next_streams_url;
|
||||
|
||||
append(extractPlaylistItems(result.related_streams));
|
||||
|
||||
fetchReactor.dispose();
|
||||
fetchReactor = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e(TAG, "Error fetching more playlist, marking playlist as complete.", e);
|
||||
isComplete = true;
|
||||
append(); // Notify change
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private SingleObserver<ListExtractor.NextItemsResult> getPlaylistNextItemsObserver() {
|
||||
return new SingleObserver<ListExtractor.NextItemsResult>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
@ -7,7 +7,9 @@
|
||||
android:layout_height="@dimen/video_item_search_height"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:padding="@dimen/video_item_search_padding">
|
||||
android:focusable="true"
|
||||
android:paddingTop="@dimen/video_item_search_padding"
|
||||
android:paddingBottom="@dimen/video_item_search_padding">
|
||||
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
android:id="@+id/itemThumbnailView"
|
||||
@ -16,18 +18,37 @@
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
|
||||
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
|
||||
android:contentDescription="@string/list_thumbnail_view_description"
|
||||
android:src="@drawable/buddy"
|
||||
tools:ignore="RtlHardcoded"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/itemActionDropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
|
||||
android:layout_marginStart="@dimen/video_item_search_image_right_margin"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:src="?attr/more_vertical"
|
||||
android:visibility="gone"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemTitleView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginBottom="@dimen/video_item_search_image_right_margin"
|
||||
android:layout_toLeftOf="@id/itemActionDropdown"
|
||||
android:layout_toStartOf="@id/itemActionDropdown"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
@ -40,7 +61,10 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@+id/itemAdditionalDetails"
|
||||
android:layout_marginBottom="@dimen/channel_item_description_to_details_margin"
|
||||
android:layout_toLeftOf="@id/itemActionDropdown"
|
||||
android:layout_toStartOf="@id/itemActionDropdown"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||
android:ellipsize="end"
|
||||
android:lines="2"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
@ -52,7 +76,10 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_toLeftOf="@id/itemActionDropdown"
|
||||
android:layout_toStartOf="@id/itemActionDropdown"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:layout_toEndOf="@+id/itemThumbnailView"
|
||||
android:lines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
||||
|
@ -7,23 +7,43 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:padding="@dimen/video_item_search_padding">
|
||||
android:focusable="true"
|
||||
android:paddingTop="@dimen/video_item_search_padding"
|
||||
android:paddingBottom="@dimen/video_item_search_padding">
|
||||
|
||||
<de.hdodenhof.circleimageview.CircleImageView
|
||||
android:id="@+id/itemThumbnailView"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="42dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
|
||||
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
|
||||
android:contentDescription="@string/list_thumbnail_view_description"
|
||||
android:src="@drawable/buddy_channel_item"
|
||||
tools:ignore="RtlHardcoded"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/itemActionDropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="42dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
|
||||
android:layout_marginStart="@dimen/video_item_search_image_right_margin"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:src="?attr/more_vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:ignore="ContentDescription"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemTitleView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toLeftOf="@id/itemActionDropdown"
|
||||
android:layout_toStartOf="@id/itemActionDropdown"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="3dp"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
@ -40,6 +60,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/itemTitleView"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:layout_toLeftOf="@id/itemActionDropdown"
|
||||
android:layout_toStartOf="@id/itemActionDropdown"
|
||||
android:lines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="@dimen/video_item_search_upload_date_text_size"
|
||||
|
@ -7,7 +7,9 @@
|
||||
android:layout_height="@dimen/video_item_search_height"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:padding="@dimen/video_item_search_padding">
|
||||
android:focusable="true"
|
||||
android:paddingTop="@dimen/video_item_search_padding"
|
||||
android:paddingBottom="@dimen/video_item_search_padding">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/itemThumbnailView"
|
||||
@ -16,17 +18,35 @@
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
|
||||
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
|
||||
android:contentDescription="@string/list_thumbnail_view_description"
|
||||
android:scaleType="fitEnd"
|
||||
android:src="@drawable/dummy_thumbnail_playlist"
|
||||
tools:ignore="RtlHardcoded"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/itemActionDropdown"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
|
||||
android:layout_marginStart="@dimen/video_item_search_image_right_margin"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:src="?attr/more_vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
tools:ignore="ContentDescription"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/itemStreamCountView"
|
||||
android:layout_width="@dimen/playlist_item_thumbnail_stream_count_width"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignBottom="@id/itemThumbnailView"
|
||||
android:layout_toLeftOf="@id/itemActionDropdown"
|
||||
android:layout_toStartOf="@id/itemActionDropdown"
|
||||
android:layout_alignRight="@id/itemThumbnailView"
|
||||
android:layout_alignTop="@id/itemThumbnailView"
|
||||
android:background="@color/playlist_stream_count_background_color"
|
||||
@ -46,6 +66,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toLeftOf="@id/itemActionDropdown"
|
||||
android:layout_toStartOf="@id/itemActionDropdown"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
@ -59,6 +81,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/itemTitleView"
|
||||
android:layout_toLeftOf="@id/itemActionDropdown"
|
||||
android:layout_toStartOf="@id/itemActionDropdown"
|
||||
android:layout_toRightOf="@+id/itemThumbnailView"
|
||||
android:lines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
|
@ -18,7 +18,7 @@
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginLeft="@dimen/video_item_search_padding"
|
||||
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
|
||||
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
|
||||
android:contentDescription="@string/list_thumbnail_view_description"
|
||||
android:scaleType="centerCrop"
|
||||
|
@ -51,6 +51,8 @@
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
|
||||
android:layout_marginStart="@dimen/video_item_search_image_right_margin"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:src="?attr/more_vertical"
|
||||
android:visibility="gone"
|
||||
|
Loading…
Reference in New Issue
Block a user