2018-04-08 13:08:19 +02:00
|
|
|
package org.schabi.newpipe.util;
|
|
|
|
|
|
|
|
import android.content.Context;
|
2018-11-20 23:10:50 +01:00
|
|
|
import android.util.SparseArray;
|
2018-04-08 13:08:19 +02:00
|
|
|
import android.view.LayoutInflater;
|
|
|
|
import android.view.View;
|
|
|
|
import android.view.ViewGroup;
|
|
|
|
import android.widget.BaseAdapter;
|
|
|
|
import android.widget.ImageView;
|
|
|
|
import android.widget.Spinner;
|
|
|
|
import android.widget.TextView;
|
|
|
|
|
2019-10-28 03:35:51 +01:00
|
|
|
import org.schabi.newpipe.DownloaderImpl;
|
2018-04-08 13:08:19 +02:00
|
|
|
import org.schabi.newpipe.R;
|
|
|
|
import org.schabi.newpipe.extractor.stream.AudioStream;
|
|
|
|
import org.schabi.newpipe.extractor.stream.Stream;
|
2018-09-23 20:12:23 +02:00
|
|
|
import org.schabi.newpipe.extractor.stream.SubtitlesStream;
|
2018-04-08 13:08:19 +02:00
|
|
|
import org.schabi.newpipe.extractor.stream.VideoStream;
|
|
|
|
|
|
|
|
import java.io.Serializable;
|
2020-03-31 19:20:15 +02:00
|
|
|
import java.util.Arrays;
|
2018-04-08 13:08:19 +02:00
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.concurrent.Callable;
|
|
|
|
|
2020-10-31 21:55:45 +01:00
|
|
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
|
|
|
import io.reactivex.rxjava3.core.Single;
|
|
|
|
import io.reactivex.rxjava3.schedulers.Schedulers;
|
2018-04-08 13:08:19 +02:00
|
|
|
import us.shandian.giga.util.Utility;
|
|
|
|
|
|
|
|
/**
|
2020-03-31 19:20:15 +02:00
|
|
|
* A list adapter for a list of {@link Stream streams}.
|
|
|
|
* It currently supports {@link VideoStream}, {@link AudioStream} and {@link SubtitlesStream}.
|
|
|
|
*
|
|
|
|
* @param <T> the primary stream type's class extending {@link Stream}
|
|
|
|
* @param <U> the secondary stream type's class extending {@link Stream}
|
2018-04-08 13:08:19 +02:00
|
|
|
*/
|
2018-11-20 23:10:50 +01:00
|
|
|
public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseAdapter {
|
2018-04-08 13:08:19 +02:00
|
|
|
private final Context context;
|
|
|
|
|
2018-08-28 20:02:25 +02:00
|
|
|
private final StreamSizeWrapper<T> streamsWrapper;
|
2018-11-20 23:10:50 +01:00
|
|
|
private final SparseArray<SecondaryStreamHelper<U>> secondaryStreams;
|
2018-04-08 13:08:19 +02:00
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
public StreamItemAdapter(final Context context, final StreamSizeWrapper<T> streamsWrapper,
|
|
|
|
final SparseArray<SecondaryStreamHelper<U>> secondaryStreams) {
|
2018-04-08 13:08:19 +02:00
|
|
|
this.context = context;
|
|
|
|
this.streamsWrapper = streamsWrapper;
|
2018-11-20 23:10:50 +01:00
|
|
|
this.secondaryStreams = secondaryStreams;
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
public StreamItemAdapter(final Context context, final StreamSizeWrapper<T> streamsWrapper,
|
|
|
|
final boolean showIconNoAudio) {
|
2018-11-20 23:10:50 +01:00
|
|
|
this(context, streamsWrapper, showIconNoAudio ? new SparseArray<>() : null);
|
2018-04-08 13:08:19 +02:00
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
public StreamItemAdapter(final Context context, final StreamSizeWrapper<T> streamsWrapper) {
|
2018-11-20 23:10:50 +01:00
|
|
|
this(context, streamsWrapper, null);
|
2018-04-08 13:08:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public List<T> getAll() {
|
|
|
|
return streamsWrapper.getStreamsList();
|
|
|
|
}
|
|
|
|
|
2018-11-20 23:10:50 +01:00
|
|
|
public SparseArray<SecondaryStreamHelper<U>> getAllSecondary() {
|
|
|
|
return secondaryStreams;
|
|
|
|
}
|
|
|
|
|
2018-04-08 13:08:19 +02:00
|
|
|
@Override
|
|
|
|
public int getCount() {
|
|
|
|
return streamsWrapper.getStreamsList().size();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public T getItem(final int position) {
|
2018-04-08 13:08:19 +02:00
|
|
|
return streamsWrapper.getStreamsList().get(position);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public long getItemId(final int position) {
|
2018-04-08 13:08:19 +02:00
|
|
|
return position;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public View getDropDownView(final int position, final View convertView,
|
|
|
|
final ViewGroup parent) {
|
2018-04-08 13:08:19 +02:00
|
|
|
return getCustomView(position, convertView, parent, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-03-31 19:20:15 +02:00
|
|
|
public View getView(final int position, final View convertView, final ViewGroup parent) {
|
|
|
|
return getCustomView(((Spinner) parent).getSelectedItemPosition(),
|
|
|
|
convertView, parent, false);
|
2018-04-08 13:08:19 +02:00
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
private View getCustomView(final int position, final View view, final ViewGroup parent,
|
|
|
|
final boolean isDropdownItem) {
|
|
|
|
View convertView = view;
|
2018-04-08 13:08:19 +02:00
|
|
|
if (convertView == null) {
|
2020-03-31 19:20:15 +02:00
|
|
|
convertView = LayoutInflater.from(context).inflate(
|
|
|
|
R.layout.stream_quality_item, parent, false);
|
2018-04-08 13:08:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
final ImageView woSoundIconView = convertView.findViewById(R.id.wo_sound_icon);
|
|
|
|
final TextView formatNameView = convertView.findViewById(R.id.stream_format_name);
|
|
|
|
final TextView qualityView = convertView.findViewById(R.id.stream_quality);
|
|
|
|
final TextView sizeView = convertView.findViewById(R.id.stream_size);
|
|
|
|
|
|
|
|
final T stream = getItem(position);
|
|
|
|
|
|
|
|
int woSoundIconVisibility = View.GONE;
|
|
|
|
String qualityString;
|
|
|
|
|
|
|
|
if (stream instanceof VideoStream) {
|
2020-08-16 10:24:58 +02:00
|
|
|
final VideoStream videoStream = ((VideoStream) stream);
|
2018-11-20 23:10:50 +01:00
|
|
|
qualityString = videoStream.getResolution();
|
|
|
|
|
|
|
|
if (secondaryStreams != null) {
|
|
|
|
if (videoStream.isVideoOnly()) {
|
2020-03-31 19:20:15 +02:00
|
|
|
woSoundIconVisibility = secondaryStreams.get(position) == null ? View.VISIBLE
|
|
|
|
: View.INVISIBLE;
|
2018-11-20 23:10:50 +01:00
|
|
|
} else if (isDropdownItem) {
|
|
|
|
woSoundIconVisibility = View.INVISIBLE;
|
2018-09-23 20:12:23 +02:00
|
|
|
}
|
2018-04-08 13:08:19 +02:00
|
|
|
}
|
|
|
|
} else if (stream instanceof AudioStream) {
|
2020-08-16 10:24:58 +02:00
|
|
|
final AudioStream audioStream = ((AudioStream) stream);
|
2018-12-23 22:07:27 +01:00
|
|
|
qualityString = audioStream.getAverageBitrate() > 0
|
|
|
|
? audioStream.getAverageBitrate() + "kbps"
|
|
|
|
: audioStream.getFormat().getName();
|
2018-09-23 20:12:23 +02:00
|
|
|
} else if (stream instanceof SubtitlesStream) {
|
|
|
|
qualityString = ((SubtitlesStream) stream).getDisplayLanguageName();
|
|
|
|
if (((SubtitlesStream) stream).isAutoGenerated()) {
|
|
|
|
qualityString += " (" + context.getString(R.string.caption_auto_generated) + ")";
|
|
|
|
}
|
2018-04-08 13:08:19 +02:00
|
|
|
} else {
|
|
|
|
qualityString = stream.getFormat().getSuffix();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (streamsWrapper.getSizeInBytes(position) > 0) {
|
2020-08-16 10:24:58 +02:00
|
|
|
final SecondaryStreamHelper secondary = secondaryStreams == null ? null
|
2020-03-31 19:20:15 +02:00
|
|
|
: secondaryStreams.get(position);
|
2018-11-20 23:10:50 +01:00
|
|
|
if (secondary != null) {
|
2020-08-16 10:24:58 +02:00
|
|
|
final long size
|
|
|
|
= secondary.getSizeInBytes() + streamsWrapper.getSizeInBytes(position);
|
2018-11-20 23:10:50 +01:00
|
|
|
sizeView.setText(Utility.formatBytes(size));
|
|
|
|
} else {
|
|
|
|
sizeView.setText(streamsWrapper.getFormattedSize(position));
|
|
|
|
}
|
2018-04-08 13:08:19 +02:00
|
|
|
sizeView.setVisibility(View.VISIBLE);
|
|
|
|
} else {
|
|
|
|
sizeView.setVisibility(View.GONE);
|
|
|
|
}
|
|
|
|
|
2018-09-23 20:12:23 +02:00
|
|
|
if (stream instanceof SubtitlesStream) {
|
|
|
|
formatNameView.setText(((SubtitlesStream) stream).getLanguageTag());
|
|
|
|
} else {
|
2019-12-29 21:56:32 +01:00
|
|
|
switch (stream.getFormat()) {
|
|
|
|
case WEBMA_OPUS:
|
|
|
|
// noinspection AndroidLintSetTextI18n
|
|
|
|
formatNameView.setText("opus");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
formatNameView.setText(stream.getFormat().getName());
|
|
|
|
break;
|
|
|
|
}
|
2018-09-23 20:12:23 +02:00
|
|
|
}
|
|
|
|
|
2018-04-08 13:08:19 +02:00
|
|
|
qualityView.setText(qualityString);
|
|
|
|
woSoundIconView.setVisibility(woSoundIconVisibility);
|
|
|
|
|
|
|
|
return convertView;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A wrapper class that includes a way of storing the stream sizes.
|
2020-03-31 19:20:15 +02:00
|
|
|
*
|
|
|
|
* @param <T> the stream type's class extending {@link Stream}
|
2018-04-08 13:08:19 +02:00
|
|
|
*/
|
|
|
|
public static class StreamSizeWrapper<T extends Stream> implements Serializable {
|
2020-03-31 19:20:15 +02:00
|
|
|
private static final StreamSizeWrapper<Stream> EMPTY = new StreamSizeWrapper<>(
|
|
|
|
Collections.emptyList(), null);
|
2018-04-08 13:08:19 +02:00
|
|
|
private final List<T> streamsList;
|
2018-11-08 23:03:30 +01:00
|
|
|
private final long[] streamSizes;
|
2018-09-23 20:12:23 +02:00
|
|
|
private final String unknownSize;
|
2018-04-08 13:08:19 +02:00
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
public StreamSizeWrapper(final List<T> sL, final Context context) {
|
2018-12-23 22:07:27 +01:00
|
|
|
this.streamsList = sL != null
|
|
|
|
? sL
|
|
|
|
: Collections.emptyList();
|
2018-04-08 13:08:19 +02:00
|
|
|
this.streamSizes = new long[streamsList.size()];
|
2020-03-31 19:20:15 +02:00
|
|
|
this.unknownSize = context == null
|
|
|
|
? "--.-" : context.getString(R.string.unknown_content);
|
2018-04-08 13:08:19 +02:00
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
Arrays.fill(streamSizes, -2);
|
2018-04-08 13:08:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper method to fetch the sizes of all the streams in a wrapper.
|
|
|
|
*
|
2020-03-31 19:20:15 +02:00
|
|
|
* @param <X> the stream type's class extending {@link Stream}
|
2018-04-08 13:08:19 +02:00
|
|
|
* @param streamsWrapper the wrapper
|
|
|
|
* @return a {@link Single} that returns a boolean indicating if any elements were changed
|
|
|
|
*/
|
2020-03-31 19:20:15 +02:00
|
|
|
public static <X extends Stream> Single<Boolean> fetchSizeForWrapper(
|
|
|
|
final StreamSizeWrapper<X> streamsWrapper) {
|
2018-04-08 13:08:19 +02:00
|
|
|
final Callable<Boolean> fetchAndSet = () -> {
|
|
|
|
boolean hasChanged = false;
|
2020-08-16 10:24:58 +02:00
|
|
|
for (final X stream : streamsWrapper.getStreamsList()) {
|
2018-09-23 20:12:23 +02:00
|
|
|
if (streamsWrapper.getSizeInBytes(stream) > -2) {
|
2018-04-08 13:08:19 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
final long contentLength = DownloaderImpl.getInstance().getContentLength(
|
|
|
|
stream.getUrl());
|
2018-04-08 13:08:19 +02:00
|
|
|
streamsWrapper.setSize(stream, contentLength);
|
|
|
|
hasChanged = true;
|
|
|
|
}
|
|
|
|
return hasChanged;
|
|
|
|
};
|
|
|
|
|
|
|
|
return Single.fromCallable(fetchAndSet)
|
|
|
|
.subscribeOn(Schedulers.io())
|
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
.onErrorReturnItem(true);
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
public static <X extends Stream> StreamSizeWrapper<X> empty() {
|
|
|
|
//noinspection unchecked
|
|
|
|
return (StreamSizeWrapper<X>) EMPTY;
|
|
|
|
}
|
|
|
|
|
2018-04-08 13:08:19 +02:00
|
|
|
public List<T> getStreamsList() {
|
|
|
|
return streamsList;
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
public long getSizeInBytes(final int streamIndex) {
|
2018-04-08 13:08:19 +02:00
|
|
|
return streamSizes[streamIndex];
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
public long getSizeInBytes(final T stream) {
|
2018-04-08 13:08:19 +02:00
|
|
|
return streamSizes[streamsList.indexOf(stream)];
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
public String getFormattedSize(final int streamIndex) {
|
2018-09-23 20:12:23 +02:00
|
|
|
return formatSize(getSizeInBytes(streamIndex));
|
2018-04-08 13:08:19 +02:00
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
public String getFormattedSize(final T stream) {
|
2018-09-23 20:12:23 +02:00
|
|
|
return formatSize(getSizeInBytes(stream));
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
private String formatSize(final long size) {
|
2018-09-23 20:12:23 +02:00
|
|
|
if (size > -1) {
|
|
|
|
return Utility.formatBytes(size);
|
|
|
|
}
|
|
|
|
return unknownSize;
|
2018-04-08 13:08:19 +02:00
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
public void setSize(final int streamIndex, final long sizeInBytes) {
|
2018-04-08 13:08:19 +02:00
|
|
|
streamSizes[streamIndex] = sizeInBytes;
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:20:15 +02:00
|
|
|
public void setSize(final T stream, final long sizeInBytes) {
|
2018-04-08 13:08:19 +02:00
|
|
|
streamSizes[streamsList.indexOf(stream)] = sizeInBytes;
|
|
|
|
}
|
|
|
|
}
|
2018-11-08 23:03:30 +01:00
|
|
|
}
|