-Refactored Channel and Playlist PlayQueue into AbstractInfo playQueue.

-Increase list item action dropdown padding.
This commit is contained in:
John Zhen Mo 2017-11-02 20:34:12 -07:00
parent 1d136c6c35
commit b32f149a1b
9 changed files with 171 additions and 252 deletions

View File

@ -63,7 +63,6 @@ import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.history.HistoryListener; import org.schabi.newpipe.history.HistoryListener;
import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.player.BackgroundPlayer;
import org.schabi.newpipe.player.MainVideoPlayer; import org.schabi.newpipe.player.MainVideoPlayer;
import org.schabi.newpipe.player.PopupVideoPlayer; import org.schabi.newpipe.player.PopupVideoPlayer;
import org.schabi.newpipe.player.helper.PlayerHelper; import org.schabi.newpipe.player.helper.PlayerHelper;

View File

@ -0,0 +1,131 @@
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.ListInfo;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import io.reactivex.SingleObserver;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> extends PlayQueue {
boolean isInitial;
boolean isComplete;
int serviceId;
String baseUrl;
String nextUrl;
transient Disposable fetchReactor;
AbstractInfoPlayQueue(final U item) {
this(item.service_id, item.url, item.url, Collections.<InfoItem>emptyList(), 0);
}
AbstractInfoPlayQueue(final int serviceId,
final String url,
final String nextPageUrl,
final List<InfoItem> streams,
final int index) {
super(index, extractListItems(streams));
this.baseUrl = url;
this.nextUrl = nextPageUrl;
this.serviceId = serviceId;
this.isInitial = streams.isEmpty();
this.isComplete = !isInitial && (nextPageUrl == null || nextPageUrl.isEmpty());
}
abstract protected String getTag();
@Override
public boolean isComplete() {
return isComplete;
}
SingleObserver<T> getHeadListObserver() {
return new SingleObserver<T>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
if (isComplete || (fetchReactor != null && !fetchReactor.isDisposed())) {
d.dispose();
} else {
fetchReactor = d;
}
}
@Override
public void onSuccess(@NonNull T result) {
if (!result.has_more_streams) isComplete = true;
nextUrl = result.next_streams_url;
append(extractListItems(result.related_streams));
fetchReactor.dispose();
fetchReactor = null;
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(getTag(), "Error fetching more playlist, marking playlist as complete.", e);
isComplete = true;
append(); // Notify change
}
};
}
SingleObserver<ListExtractor.NextItemsResult> getNextItemsObserver() {
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(extractListItems(result.nextItemsList));
fetchReactor.dispose();
fetchReactor = null;
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(getTag(), "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> extractListItems(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;
}
}

View File

@ -1,151 +1,45 @@
package org.schabi.newpipe.playlist; package org.schabi.newpipe.playlist;
import android.util.Log;
import org.schabi.newpipe.extractor.InfoItem; 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.ChannelInfo;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ExtractorHelper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import io.reactivex.SingleObserver;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
public final class ChannelPlayQueue extends PlayQueue { public final class ChannelPlayQueue extends AbstractInfoPlayQueue<ChannelInfo, ChannelInfoItem> {
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) { public ChannelPlayQueue(final ChannelInfoItem item) {
this(item.service_id, item.url, item.url, Collections.<InfoItem>emptyList(), 0); super(item);
} }
public ChannelPlayQueue(final int serviceId, public ChannelPlayQueue(final int serviceId,
final String url, final String url,
final String nextPageUrl, final String nextPageUrl,
final List<InfoItem> streams, final List<InfoItem> streams,
final int index) { final int index) {
super(index, extractChannelItems(streams)); super(serviceId, url, nextPageUrl, streams, index);
this.baseUrl = url;
this.nextUrl = nextPageUrl;
this.serviceId = serviceId;
this.isInitial = streams.isEmpty();
this.isComplete = !isInitial && (nextPageUrl == null || nextPageUrl.isEmpty());
} }
@Override @Override
public boolean isComplete() { protected String getTag() {
return isComplete; return "ChannelPlayQueue@" + Integer.toHexString(hashCode());
} }
@Override @Override
public void fetch() { public void fetch() {
if (isInitial) { if (this.isInitial) {
ExtractorHelper.getChannelInfo(this.serviceId, this.baseUrl, false) ExtractorHelper.getChannelInfo(this.serviceId, this.baseUrl, false)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(getChannelInitialObserver()); .subscribe(getHeadListObserver());
} else { } else {
ExtractorHelper.getMoreChannelItems(this.serviceId, this.baseUrl, this.nextUrl) ExtractorHelper.getMoreChannelItems(this.serviceId, this.baseUrl, this.nextUrl)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(getChannelNextItemsObserver()); .subscribe(getNextItemsObserver());
} }
} }
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;
}
} }

View File

@ -1,150 +1,45 @@
package org.schabi.newpipe.playlist; package org.schabi.newpipe.playlist;
import android.util.Log;
import org.schabi.newpipe.extractor.InfoItem; 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.PlaylistInfo;
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ExtractorHelper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import io.reactivex.SingleObserver;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
public final class PlaylistPlayQueue extends PlayQueue { public final class PlaylistPlayQueue extends AbstractInfoPlayQueue<PlaylistInfo, PlaylistInfoItem> {
private final String TAG = "PlaylistPlayQueue@" + Integer.toHexString(hashCode());
private boolean isInitial;
private boolean isComplete;
private int serviceId;
private String baseUrl;
private String nextUrl;
private transient Disposable fetchReactor;
public PlaylistPlayQueue(final PlaylistInfoItem item) { public PlaylistPlayQueue(final PlaylistInfoItem item) {
this(item.service_id, item.url, item.url, Collections.<InfoItem>emptyList(), 0); super(item);
} }
public PlaylistPlayQueue(final int serviceId, public PlaylistPlayQueue(final int serviceId,
final String url, final String url,
final String nextPageUrl, final String nextPageUrl,
final List<InfoItem> streams, final List<InfoItem> streams,
final int index) { final int index) {
super(index, extractPlaylistItems(streams)); super(serviceId, url, nextPageUrl, streams, index);
this.baseUrl = url;
this.nextUrl = nextPageUrl;
this.serviceId = serviceId;
this.isInitial = streams.isEmpty();
this.isComplete = !isInitial && (nextPageUrl == null || nextPageUrl.isEmpty());
} }
@Override @Override
public boolean isComplete() { protected String getTag() {
return isComplete; return "PlaylistPlayQueue@" + Integer.toHexString(hashCode());
} }
@Override @Override
public void fetch() { public void fetch() {
if (isInitial) { if (this.isInitial) {
ExtractorHelper.getPlaylistInfo(this.serviceId, this.baseUrl, false) ExtractorHelper.getPlaylistInfo(this.serviceId, this.baseUrl, false)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(getInitialPlaylistObserver()); .subscribe(getHeadListObserver());
} else { } else {
ExtractorHelper.getMorePlaylistItems(this.serviceId, this.baseUrl, this.nextUrl) ExtractorHelper.getMorePlaylistItems(this.serviceId, this.baseUrl, this.nextUrl)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(getPlaylistNextItemsObserver()); .subscribe(getNextItemsObserver());
} }
} }
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) {
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(extractPlaylistItems(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> extractPlaylistItems(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;
}
} }

View File

@ -31,13 +31,13 @@
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_marginLeft="@dimen/video_item_search_image_right_margin" android:paddingLeft="@dimen/video_item_search_padding"
android:layout_marginStart="@dimen/video_item_search_image_right_margin" android:paddingStart="@dimen/video_item_search_padding"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:src="?attr/more_vertical" android:src="?attr/more_vertical"
android:visibility="gone" android:visibility="gone"
tools:ignore="ContentDescription" tools:visibility="visible"
tools:visibility="visible" /> tools:ignore="ContentDescription,RtlSymmetry"/>
<TextView <TextView
android:id="@+id/itemTitleView" android:id="@+id/itemTitleView"

View File

@ -29,13 +29,13 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_marginLeft="@dimen/video_item_search_image_right_margin" android:paddingLeft="@dimen/video_item_search_padding"
android:layout_marginStart="@dimen/video_item_search_image_right_margin" android:paddingStart="@dimen/video_item_search_padding"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:src="?attr/more_vertical" android:src="?attr/more_vertical"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" tools:visibility="visible"
tools:ignore="ContentDescription"/> tools:ignore="ContentDescription,RtlSymmetry"/>
<TextView <TextView
android:id="@+id/itemTitleView" android:id="@+id/itemTitleView"

View File

@ -32,13 +32,13 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_marginLeft="@dimen/video_item_search_image_right_margin" android:paddingLeft="@dimen/video_item_search_padding"
android:layout_marginStart="@dimen/video_item_search_image_right_margin" android:paddingStart="@dimen/video_item_search_padding"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:src="?attr/more_vertical" android:src="?attr/more_vertical"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" tools:visibility="visible"
tools:ignore="ContentDescription"/> tools:ignore="ContentDescription,RtlSymmetry"/>
<TextView <TextView
android:id="@+id/itemStreamCountView" android:id="@+id/itemStreamCountView"

View File

@ -52,13 +52,13 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_marginLeft="@dimen/video_item_search_image_right_margin" android:paddingLeft="@dimen/video_item_search_padding"
android:layout_marginStart="@dimen/video_item_search_image_right_margin" android:paddingStart="@dimen/video_item_search_padding"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:src="?attr/more_vertical" android:src="?attr/more_vertical"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" tools:visibility="visible"
tools:ignore="ContentDescription"/> tools:ignore="ContentDescription,RtlSymmetry"/>
<TextView <TextView
android:id="@+id/itemVideoTitleView" android:id="@+id/itemVideoTitleView"

View File

@ -51,13 +51,13 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_marginLeft="@dimen/video_item_search_image_right_margin" android:paddingLeft="@dimen/video_item_search_padding"
android:layout_marginStart="@dimen/video_item_search_image_right_margin" android:paddingStart="@dimen/video_item_search_padding"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackground"
android:src="?attr/more_vertical" android:src="?attr/more_vertical"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" tools:visibility="visible"
tools:ignore="ContentDescription"/> tools:ignore="ContentDescription,RtlSymmetry"/>
<TextView <TextView
android:id="@+id/itemVideoTitleView" android:id="@+id/itemVideoTitleView"