Use SparseArrayCompat instead of SparseArray in StreamItemAdapter.

Make additional small improvements as well.
This commit is contained in:
Isira Seneviratne 2022-11-22 18:31:58 +05:30
parent 8db90ba449
commit d6617007d4
3 changed files with 31 additions and 56 deletions

View File

@ -1,12 +1,12 @@
package org.schabi.newpipe.util package org.schabi.newpipe.util
import android.content.Context import android.content.Context
import android.util.SparseArray
import android.view.View import android.view.View
import android.view.View.GONE import android.view.View.GONE
import android.view.View.INVISIBLE import android.view.View.INVISIBLE
import android.view.View.VISIBLE import android.view.View.VISIBLE
import android.widget.Spinner import android.widget.Spinner
import androidx.collection.SparseArrayCompat
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest import androidx.test.filters.MediumTest
@ -39,9 +39,7 @@ class StreamItemAdapterTest {
@Test @Test
fun videoStreams_noSecondaryStream() { fun videoStreams_noSecondaryStream() {
val adapter = StreamItemAdapter<VideoStream, AudioStream>( val adapter = StreamItemAdapter<VideoStream, AudioStream>(
context, getVideoStreams(true, true, true, true)
getVideoStreams(true, true, true, true),
null
) )
spinner.adapter = adapter spinner.adapter = adapter
@ -54,7 +52,6 @@ class StreamItemAdapterTest {
@Test @Test
fun videoStreams_hasSecondaryStream() { fun videoStreams_hasSecondaryStream() {
val adapter = StreamItemAdapter( val adapter = StreamItemAdapter(
context,
getVideoStreams(false, true, false, true), getVideoStreams(false, true, false, true),
getAudioStreams(false, true, false, true) getAudioStreams(false, true, false, true)
) )
@ -69,7 +66,6 @@ class StreamItemAdapterTest {
@Test @Test
fun videoStreams_Mixed() { fun videoStreams_Mixed() {
val adapter = StreamItemAdapter( val adapter = StreamItemAdapter(
context,
getVideoStreams(true, true, true, true, true, false, true, true), getVideoStreams(true, true, true, true, true, false, true, true),
getAudioStreams(false, true, false, false, false, true, true, true) getAudioStreams(false, true, false, false, false, true, true, true)
) )
@ -88,7 +84,6 @@ class StreamItemAdapterTest {
@Test @Test
fun subtitleStreams_noIcon() { fun subtitleStreams_noIcon() {
val adapter = StreamItemAdapter<SubtitlesStream, Stream>( val adapter = StreamItemAdapter<SubtitlesStream, Stream>(
context,
StreamItemAdapter.StreamSizeWrapper( StreamItemAdapter.StreamSizeWrapper(
(0 until 5).map { (0 until 5).map {
SubtitlesStream.Builder() SubtitlesStream.Builder()
@ -99,8 +94,7 @@ class StreamItemAdapterTest {
.build() .build()
}, },
context context
), )
null
) )
spinner.adapter = adapter spinner.adapter = adapter
for (i in 0 until spinner.count) { for (i in 0 until spinner.count) {
@ -111,7 +105,6 @@ class StreamItemAdapterTest {
@Test @Test
fun audioStreams_noIcon() { fun audioStreams_noIcon() {
val adapter = StreamItemAdapter<AudioStream, Stream>( val adapter = StreamItemAdapter<AudioStream, Stream>(
context,
StreamItemAdapter.StreamSizeWrapper( StreamItemAdapter.StreamSizeWrapper(
(0 until 5).map { (0 until 5).map {
AudioStream.Builder() AudioStream.Builder()
@ -122,8 +115,7 @@ class StreamItemAdapterTest {
.build() .build()
}, },
context context
), )
null
) )
spinner.adapter = adapter spinner.adapter = adapter
for (i in 0 until spinner.count) { for (i in 0 until spinner.count) {
@ -200,7 +192,7 @@ class StreamItemAdapterTest {
* Helper function that builds a secondary stream list. * Helper function that builds a secondary stream list.
*/ */
private fun <T : Stream> getSecondaryStreamsFromList(streams: List<T?>) = private fun <T : Stream> getSecondaryStreamsFromList(streams: List<T?>) =
SparseArray<SecondaryStreamHelper<T>?>(streams.size).apply { SparseArrayCompat<SecondaryStreamHelper<T>?>(streams.size).apply {
streams.forEachIndexed { index, stream -> streams.forEachIndexed { index, stream ->
val secondaryStreamHelper: SecondaryStreamHelper<T>? = stream?.let { val secondaryStreamHelper: SecondaryStreamHelper<T>? = stream?.let {
SecondaryStreamHelper( SecondaryStreamHelper(

View File

@ -17,7 +17,6 @@ import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log; import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -36,6 +35,7 @@ import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.view.menu.ActionMenuItemView; import androidx.appcompat.view.menu.ActionMenuItemView;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.collection.SparseArrayCompat;
import androidx.documentfile.provider.DocumentFile; import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
@ -211,8 +211,7 @@ public class DownloadDialog extends DialogFragment
setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(context)); setStyle(STYLE_NO_TITLE, ThemeHelper.getDialogTheme(context));
Icepick.restoreInstanceState(this, savedInstanceState); Icepick.restoreInstanceState(this, savedInstanceState);
final SparseArray<SecondaryStreamHelper<AudioStream>> secondaryStreams = final var secondaryStreams = new SparseArrayCompat<SecondaryStreamHelper<AudioStream>>(4);
new SparseArray<>(4);
final List<VideoStream> videoStreams = wrappedVideoStreams.getStreamsList(); final List<VideoStream> videoStreams = wrappedVideoStreams.getStreamsList();
for (int i = 0; i < videoStreams.size(); i++) { for (int i = 0; i < videoStreams.size(); i++) {
@ -236,10 +235,9 @@ public class DownloadDialog extends DialogFragment
} }
} }
this.videoStreamsAdapter = new StreamItemAdapter<>(context, wrappedVideoStreams, this.videoStreamsAdapter = new StreamItemAdapter<>(wrappedVideoStreams, secondaryStreams);
secondaryStreams); this.audioStreamsAdapter = new StreamItemAdapter<>(wrappedAudioStreams);
this.audioStreamsAdapter = new StreamItemAdapter<>(context, wrappedAudioStreams); this.subtitleStreamsAdapter = new StreamItemAdapter<>(wrappedSubtitleStreams);
this.subtitleStreamsAdapter = new StreamItemAdapter<>(context, wrappedSubtitleStreams);
final Intent intent = new Intent(context, DownloadManagerService.class); final Intent intent = new Intent(context, DownloadManagerService.class);
context.startService(intent); context.startService(intent);

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.util; package org.schabi.newpipe.util;
import android.content.Context; import android.content.Context;
import android.util.SparseArray;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -11,6 +10,8 @@ import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.collection.SparseArrayCompat;
import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.DownloaderImpl;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
@ -39,10 +40,10 @@ import us.shandian.giga.util.Utility;
* @param <U> the secondary stream type's class extending {@link Stream} * @param <U> the secondary stream type's class extending {@link Stream}
*/ */
public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseAdapter { public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseAdapter {
private final Context context; @NonNull
private final StreamSizeWrapper<T> streamsWrapper; private final StreamSizeWrapper<T> streamsWrapper;
private final SparseArray<SecondaryStreamHelper<U>> secondaryStreams; @NonNull
private final SparseArrayCompat<SecondaryStreamHelper<U>> secondaryStreams;
/** /**
* Indicates that at least one of the primary streams is an instance of {@link VideoStream}, * Indicates that at least one of the primary streams is an instance of {@link VideoStream},
@ -51,9 +52,10 @@ public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseA
*/ */
private final boolean hasAnyVideoOnlyStreamWithNoSecondaryStream; private final boolean hasAnyVideoOnlyStreamWithNoSecondaryStream;
public StreamItemAdapter(final Context context, final StreamSizeWrapper<T> streamsWrapper, public StreamItemAdapter(
final SparseArray<SecondaryStreamHelper<U>> secondaryStreams) { @NonNull final StreamSizeWrapper<T> streamsWrapper,
this.context = context; @NonNull final SparseArrayCompat<SecondaryStreamHelper<U>> secondaryStreams
) {
this.streamsWrapper = streamsWrapper; this.streamsWrapper = streamsWrapper;
this.secondaryStreams = secondaryStreams; this.secondaryStreams = secondaryStreams;
@ -61,15 +63,15 @@ public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseA
checkHasAnyVideoOnlyStreamWithNoSecondaryStream(); checkHasAnyVideoOnlyStreamWithNoSecondaryStream();
} }
public StreamItemAdapter(final Context context, final StreamSizeWrapper<T> streamsWrapper) { public StreamItemAdapter(final StreamSizeWrapper<T> streamsWrapper) {
this(context, streamsWrapper, null); this(streamsWrapper, new SparseArrayCompat<>(0));
} }
public List<T> getAll() { public List<T> getAll() {
return streamsWrapper.getStreamsList(); return streamsWrapper.getStreamsList();
} }
public SparseArray<SecondaryStreamHelper<U>> getAllSecondary() { public SparseArrayCompat<SecondaryStreamHelper<U>> getAllSecondary() {
return secondaryStreams; return secondaryStreams;
} }
@ -106,6 +108,7 @@ public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseA
final View view, final View view,
final ViewGroup parent, final ViewGroup parent,
final boolean isDropdownItem) { final boolean isDropdownItem) {
final var context = parent.getContext();
View convertView = view; View convertView = view;
if (convertView == null) { if (convertView == null) {
convertView = LayoutInflater.from(context).inflate( convertView = LayoutInflater.from(context).inflate(
@ -129,7 +132,7 @@ public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseA
if (hasAnyVideoOnlyStreamWithNoSecondaryStream) { if (hasAnyVideoOnlyStreamWithNoSecondaryStream) {
if (videoStream.isVideoOnly()) { 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 // 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) // dropdown view so it doesn't look out of place (missing margin)
// compared to those that don't. // compared to those that don't.
@ -163,8 +166,7 @@ public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseA
} }
if (streamsWrapper.getSizeInBytes(position) > 0) { if (streamsWrapper.getSizeInBytes(position) > 0) {
final SecondaryStreamHelper<U> secondary = secondaryStreams == null ? null final var secondary = secondaryStreams.get(position);
: secondaryStreams.get(position);
if (secondary != null) { if (secondary != null) {
final long size = secondary.getSizeInBytes() final long size = secondary.getSizeInBytes()
+ streamsWrapper.getSizeInBytes(position); + streamsWrapper.getSizeInBytes(position);
@ -196,14 +198,6 @@ public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseA
return convertView; 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;
}
/** /**
* @return if there are any video-only streams with no secondary stream associated with them. * @return if there are any video-only streams with no secondary stream associated with them.
* @see #hasAnyVideoOnlyStreamWithNoSecondaryStream * @see #hasAnyVideoOnlyStreamWithNoSecondaryStream
@ -213,7 +207,7 @@ public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseA
final T stream = streamsWrapper.getStreamsList().get(i); final T stream = streamsWrapper.getStreamsList().get(i);
if (stream instanceof VideoStream) { if (stream instanceof VideoStream) {
final boolean videoOnly = ((VideoStream) stream).isVideoOnly(); final boolean videoOnly = ((VideoStream) stream).isVideoOnly();
if (videoOnly && !hasSecondaryStream(i)) { if (videoOnly && secondaryStreams.get(i) == null) {
return true; return true;
} }
} }
@ -228,16 +222,15 @@ public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseA
* @param <T> the stream type's class extending {@link Stream} * @param <T> the stream type's class extending {@link Stream}
*/ */
public static class StreamSizeWrapper<T extends Stream> implements Serializable { public static class StreamSizeWrapper<T extends Stream> implements Serializable {
private static final StreamSizeWrapper<Stream> EMPTY = new StreamSizeWrapper<>( private static final StreamSizeWrapper<Stream> EMPTY =
Collections.emptyList(), null); new StreamSizeWrapper<>(Collections.emptyList(), null);
private final List<T> streamsList; private final List<T> streamsList;
private final long[] streamSizes; private final long[] streamSizes;
private final String unknownSize; private final String unknownSize;
public StreamSizeWrapper(final List<T> sL, final Context context) { public StreamSizeWrapper(@NonNull final List<T> streamList,
this.streamsList = sL != null @Nullable final Context context) {
? sL this.streamsList = streamList;
: Collections.emptyList();
this.streamSizes = new long[streamsList.size()]; this.streamSizes = new long[streamsList.size()];
this.unknownSize = context == null this.unknownSize = context == null
? "--.-" : context.getString(R.string.unknown_content); ? "--.-" : context.getString(R.string.unknown_content);
@ -297,10 +290,6 @@ public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseA
return formatSize(getSizeInBytes(streamIndex)); return formatSize(getSizeInBytes(streamIndex));
} }
public String getFormattedSize(final T stream) {
return formatSize(getSizeInBytes(stream));
}
private String formatSize(final long size) { private String formatSize(final long size) {
if (size > -1) { if (size > -1) {
return Utility.formatBytes(size); return Utility.formatBytes(size);
@ -308,10 +297,6 @@ public class StreamItemAdapter<T extends Stream, U extends Stream> extends BaseA
return unknownSize; return unknownSize;
} }
public void setSize(final int streamIndex, final long sizeInBytes) {
streamSizes[streamIndex] = sizeInBytes;
}
public void setSize(final T stream, final long sizeInBytes) { public void setSize(final T stream, final long sizeInBytes) {
streamSizes[streamsList.indexOf(stream)] = sizeInBytes; streamSizes[streamsList.indexOf(stream)] = sizeInBytes;
} }