Skip to content

Commit

Permalink
Use SparseArrayCompat instead of SparseArray in StreamItemAdapter.
Browse files Browse the repository at this point in the history
  • Loading branch information
Isira-Seneviratne committed Aug 28, 2022
1 parent e65e25a commit 9b8c250
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 120 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package org.schabi.newpipe.util

import android.content.Context
import android.util.SparseArray
import android.view.View
import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.widget.Spinner
import androidx.collection.SparseArrayCompat
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
Expand Down Expand Up @@ -39,9 +39,7 @@ class StreamItemAdapterTest {
@Test
fun videoStreams_noSecondaryStream() {
val adapter = StreamItemAdapter<VideoStream, AudioStream>(
context,
getVideoStreams(true, true, true, true),
null
getVideoStreams(true, true, true, true)
)

spinner.adapter = adapter
Expand All @@ -54,7 +52,6 @@ class StreamItemAdapterTest {
@Test
fun videoStreams_hasSecondaryStream() {
val adapter = StreamItemAdapter(
context,
getVideoStreams(false, true, false, true),
getAudioStreams(false, true, false, true)
)
Expand All @@ -69,7 +66,6 @@ class StreamItemAdapterTest {
@Test
fun videoStreams_Mixed() {
val adapter = StreamItemAdapter(
context,
getVideoStreams(true, true, true, true, true, false, true, true),
getAudioStreams(false, true, false, false, false, true, true, true)
)
Expand All @@ -88,7 +84,6 @@ class StreamItemAdapterTest {
@Test
fun subtitleStreams_noIcon() {
val adapter = StreamItemAdapter<SubtitlesStream, Stream>(
context,
StreamItemAdapter.StreamSizeWrapper(
(0 until 5).map {
SubtitlesStream.Builder()
Expand All @@ -99,8 +94,7 @@ class StreamItemAdapterTest {
.build()
},
context
),
null
)
)
spinner.adapter = adapter
for (i in 0 until spinner.count) {
Expand All @@ -111,7 +105,6 @@ class StreamItemAdapterTest {
@Test
fun audioStreams_noIcon() {
val adapter = StreamItemAdapter<AudioStream, Stream>(
context,
StreamItemAdapter.StreamSizeWrapper(
(0 until 5).map {
AudioStream.Builder()
Expand All @@ -122,8 +115,7 @@ class StreamItemAdapterTest {
.build()
},
context
),
null
)
)
spinner.adapter = adapter
for (i in 0 until spinner.count) {
Expand Down Expand Up @@ -200,7 +192,7 @@ class StreamItemAdapterTest {
* Helper function that builds a secondary stream list.
*/
private fun <T : Stream> getSecondaryStreamsFromList(streams: List<T?>) =
SparseArray<SecondaryStreamHelper<T>?>(streams.size).apply {
SparseArrayCompat<SecondaryStreamHelper<T>?>(streams.size).apply {
streams.forEachIndexed { index, stream ->
val secondaryStreamHelper: SecondaryStreamHelper<T>? = stream?.let {
SecondaryStreamHelper(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import android.os.Environment;
import android.os.IBinder;
import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
Expand All @@ -36,6 +35,7 @@
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.view.menu.ActionMenuItemView;
import androidx.appcompat.widget.Toolbar;
import androidx.collection.SparseArrayCompat;
import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.DialogFragment;
import androidx.preference.PreferenceManager;
Expand Down Expand Up @@ -205,8 +205,7 @@ public void onCreate(@Nullable final Bundle savedInstanceState) {
setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(context));
Icepick.restoreInstanceState(this, savedInstanceState);

final SparseArray<SecondaryStreamHelper<AudioStream>> secondaryStreams =
new SparseArray<>(4);
final var secondaryStreams = new SparseArrayCompat<SecondaryStreamHelper<AudioStream>>(4);
final List<VideoStream> videoStreams = wrappedVideoStreams.getStreamsList();

for (int i = 0; i < videoStreams.size(); i++) {
Expand All @@ -230,10 +229,10 @@ public void onCreate(@Nullable final Bundle savedInstanceState) {
}
}

this.videoStreamsAdapter = new StreamItemAdapter<>(context, wrappedVideoStreams,
this.videoStreamsAdapter = new StreamItemAdapter<>(wrappedVideoStreams,
secondaryStreams);
this.audioStreamsAdapter = new StreamItemAdapter<>(context, wrappedAudioStreams);
this.subtitleStreamsAdapter = new StreamItemAdapter<>(context, wrappedSubtitleStreams);
this.audioStreamsAdapter = new StreamItemAdapter<>(wrappedAudioStreams);
this.subtitleStreamsAdapter = new StreamItemAdapter<>(wrappedSubtitleStreams);

final Intent intent = new Intent(context, DownloadManagerService.class);
context.startService(intent);
Expand Down
136 changes: 35 additions & 101 deletions app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package org.schabi.newpipe.util;

import android.content.Context;
import android.util.SparseArray;
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;

import androidx.annotation.NonNull;
import androidx.collection.SparseArrayCompat;

import org.schabi.newpipe.DownloaderImpl;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.StreamQualityItemBinding;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Stream;
Expand All @@ -29,7 +28,6 @@
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.schedulers.Schedulers;
import us.shandian.giga.util.Utility;

/**
* A list adapter for a list of {@link Stream streams}.
Expand All @@ -39,10 +37,8 @@
* @param <U> the secondary stream type's class extending {@link Stream}
*/
public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseAdapter {
private final Context context;

private final StreamSizeWrapper<T> streamsWrapper;
private final SparseArray<SecondaryStreamHelper<U>> secondaryStreams;
private final SparseArrayCompat<SecondaryStreamHelper<U>> secondaryStreams;

/**
* Indicates that at least one of the primary streams is an instance of {@link VideoStream},
Expand All @@ -51,25 +47,24 @@ public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseA
*/
private final boolean hasAnyVideoOnlyStreamWithNoSecondaryStream;

public StreamItemAdapter(final Context context, final StreamSizeWrapper<T> streamsWrapper,
final SparseArray<SecondaryStreamHelper<U>> secondaryStreams) {
this.context = context;
public StreamItemAdapter(final StreamSizeWrapper<T> streamsWrapper,
final SparseArrayCompat<SecondaryStreamHelper<U>> secondaryStreams) {
this.streamsWrapper = streamsWrapper;
this.secondaryStreams = secondaryStreams;

this.hasAnyVideoOnlyStreamWithNoSecondaryStream =
checkHasAnyVideoOnlyStreamWithNoSecondaryStream();
}

public StreamItemAdapter(final Context context, final StreamSizeWrapper<T> streamsWrapper) {
this(context, streamsWrapper, null);
public StreamItemAdapter(final StreamSizeWrapper<T> streamsWrapper) {
this(streamsWrapper, new SparseArrayCompat<>(0));
}

public List<T> getAll() {
return streamsWrapper.getStreamsList();
}

public SparseArray<SecondaryStreamHelper<U>> getAllSecondary() {
public SparseArrayCompat<SecondaryStreamHelper<U>> getAllSecondary() {
return secondaryStreams;
}

Expand All @@ -89,8 +84,7 @@ public long getItemId(final int position) {
}

@Override
public View getDropDownView(final int position,
final View convertView,
public View getDropDownView(final int position, final View convertView,
final ViewGroup parent) {
return getCustomView(position, convertView, parent, true);
}
Expand All @@ -102,34 +96,20 @@ public View getView(final int position, final View convertView, final ViewGroup
}

@NonNull
private View getCustomView(final int position,
final View view,
final ViewGroup parent,
private View getCustomView(final int position, final View view, final ViewGroup parent,
final boolean isDropdownItem) {
View convertView = view;
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(
R.layout.stream_quality_item, parent, false);
}

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 Context context = parent.getContext();
final var binding = view != null ? StreamQualityItemBinding.bind(view)
: StreamQualityItemBinding.inflate(LayoutInflater.from(context), parent, false);
final T stream = getItem(position);
final MediaFormat mediaFormat = stream.getFormat();

int woSoundIconVisibility = View.GONE;
String qualityString;

if (stream instanceof VideoStream) {
final VideoStream videoStream = ((VideoStream) stream);
qualityString = videoStream.getResolution();

if (hasAnyVideoOnlyStreamWithNoSecondaryStream) {
if (videoStream.isVideoOnly()) {
woSoundIconVisibility = hasSecondaryStream(position)
woSoundIconVisibility = secondaryStreams.get(position) != null
// It has a secondary stream associated with it, so check if it's a
// dropdown view so it doesn't look out of place (missing margin)
// compared to those that don't.
Expand All @@ -140,68 +120,41 @@ private View getCustomView(final int position,
woSoundIconVisibility = View.INVISIBLE;
}
}
}

binding.streamQuality.setText(getQualityString(context, stream));
binding.woSoundIcon.setVisibility(woSoundIconVisibility);

return binding.getRoot();
}

private String getQualityString(final Context context, final T stream) {
final MediaFormat mediaFormat = stream.getFormat();

if (stream instanceof VideoStream) {
return ((VideoStream) stream).getResolution();
} else if (stream instanceof AudioStream) {
final AudioStream audioStream = ((AudioStream) stream);
if (audioStream.getAverageBitrate() > 0) {
qualityString = audioStream.getAverageBitrate() + "kbps";
return audioStream.getAverageBitrate() + "kbps";
} else if (mediaFormat != null) {
qualityString = mediaFormat.getName();
return mediaFormat.getName();
} else {
qualityString = context.getString(R.string.unknown_quality);
return context.getString(R.string.unknown_quality);
}
} else if (stream instanceof SubtitlesStream) {
qualityString = ((SubtitlesStream) stream).getDisplayLanguageName();
String qualityString = ((SubtitlesStream) stream).getDisplayLanguageName();
if (((SubtitlesStream) stream).isAutoGenerated()) {
qualityString += " (" + context.getString(R.string.caption_auto_generated) + ")";
}
return qualityString;
} else {
if (mediaFormat == null) {
qualityString = context.getString(R.string.unknown_quality);
} else {
qualityString = mediaFormat.getSuffix();
}
}

if (streamsWrapper.getSizeInBytes(position) > 0) {
final SecondaryStreamHelper<U> secondary = secondaryStreams == null ? null
: secondaryStreams.get(position);
if (secondary != null) {
final long size = secondary.getSizeInBytes()
+ streamsWrapper.getSizeInBytes(position);
sizeView.setText(Utility.formatBytes(size));
} else {
sizeView.setText(streamsWrapper.getFormattedSize(position));
}
sizeView.setVisibility(View.VISIBLE);
} else {
sizeView.setVisibility(View.GONE);
}

if (stream instanceof SubtitlesStream) {
formatNameView.setText(((SubtitlesStream) stream).getLanguageTag());
} else {
if (mediaFormat == null) {
formatNameView.setText(context.getString(R.string.unknown_format));
} else if (mediaFormat == MediaFormat.WEBMA_OPUS) {
// noinspection AndroidLintSetTextI18n
formatNameView.setText("opus");
return context.getString(R.string.unknown_quality);
} else {
formatNameView.setText(mediaFormat.getName());
return mediaFormat.getSuffix();
}
}

qualityView.setText(qualityString);
woSoundIconView.setVisibility(woSoundIconVisibility);

return convertView;
}

/**
* @param position which primary stream to check.
* @return whether the primary stream at position has a secondary stream associated with it.
*/
private boolean hasSecondaryStream(final int position) {
return secondaryStreams != null && secondaryStreams.get(position) != null;
}

/**
Expand All @@ -213,7 +166,7 @@ private boolean checkHasAnyVideoOnlyStreamWithNoSecondaryStream() {
final T stream = streamsWrapper.getStreamsList().get(i);
if (stream instanceof VideoStream) {
final boolean videoOnly = ((VideoStream) stream).isVideoOnly();
if (videoOnly && !hasSecondaryStream(i)) {
if (videoOnly && secondaryStreams.get(i) == null) {
return true;
}
}
Expand Down Expand Up @@ -293,25 +246,6 @@ public long getSizeInBytes(final T stream) {
return streamSizes[streamsList.indexOf(stream)];
}

public String getFormattedSize(final int streamIndex) {
return formatSize(getSizeInBytes(streamIndex));
}

public String getFormattedSize(final T stream) {
return formatSize(getSizeInBytes(stream));
}

private String formatSize(final long size) {
if (size > -1) {
return Utility.formatBytes(size);
}
return unknownSize;
}

public void setSize(final int streamIndex, final long sizeInBytes) {
streamSizes[streamIndex] = sizeInBytes;
}

public void setSize(final T stream, final long sizeInBytes) {
streamSizes[streamsList.indexOf(stream)] = sizeInBytes;
}
Expand Down

0 comments on commit 9b8c250

Please sign in to comment.