1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2024-12-23 08:30:44 +00:00

Merge pull request #8895 from Isira-Seneviratne/SparseArrayCompat

Use SparseArrayCompat.
This commit is contained in:
Stypox 2022-12-28 20:37:05 +01:00 committed by GitHub
commit 231e677b16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 88 additions and 141 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

@ -33,6 +33,7 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.TooltipCompat; import androidx.appcompat.widget.TooltipCompat;
import androidx.collection.SparseArrayCompat;
import androidx.core.text.HtmlCompat; import androidx.core.text.HtmlCompat;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
@ -70,9 +71,7 @@ import org.schabi.newpipe.util.ServiceHelper;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -141,7 +140,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
@State @State
boolean wasSearchFocused = false; boolean wasSearchFocused = false;
@Nullable private Map<Integer, String> menuItemToFilterName = null; private final SparseArrayCompat<String> menuItemToFilterName = new SparseArrayCompat<>();
private StreamingService service; private StreamingService service;
private Page nextPage; private Page nextPage;
private boolean showLocalSuggestions = true; private boolean showLocalSuggestions = true;
@ -426,8 +425,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
supportActionBar.setDisplayHomeAsUpEnabled(true); supportActionBar.setDisplayHomeAsUpEnabled(true);
} }
menuItemToFilterName = new HashMap<>();
int itemId = 0; int itemId = 0;
boolean isFirstItem = true; boolean isFirstItem = true;
final Context c = getContext(); final Context c = getContext();
@ -468,11 +465,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
@Override @Override
public boolean onOptionsItemSelected(@NonNull final MenuItem item) { public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
if (menuItemToFilterName != null) { final var filter = Collections.singletonList(menuItemToFilterName.get(item.getItemId()));
final List<String> cf = new ArrayList<>(1); changeContentFilter(item, filter);
cf.add(menuItemToFilterName.get(item.getItemId()));
changeContentFilter(item, cf);
}
return true; return true;
} }

View File

@ -1,6 +1,7 @@
package org.schabi.newpipe.player.seekbarpreview; package org.schabi.newpipe.player.seekbarpreview;
import static org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper.SeekbarPreviewThumbnailType; import static org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper.SeekbarPreviewThumbnailType;
import static org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper.getSeekbarPreviewThumbnailType;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@ -8,6 +9,7 @@ import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.collection.SparseArrayCompat;
import com.google.common.base.Stopwatch; import com.google.common.base.Stopwatch;
@ -15,12 +17,9 @@ import org.schabi.newpipe.extractor.stream.Frameset;
import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.PicassoHelper;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -34,18 +33,15 @@ public class SeekbarPreviewThumbnailHolder {
// Key = Position of the picture in milliseconds // Key = Position of the picture in milliseconds
// Supplier = Supplies the bitmap for that position // Supplier = Supplies the bitmap for that position
private final Map<Integer, Supplier<Bitmap>> seekbarPreviewData = new ConcurrentHashMap<>(); private final SparseArrayCompat<Supplier<Bitmap>> seekbarPreviewData =
new SparseArrayCompat<>();
// This ensures that if the reset is still undergoing // This ensures that if the reset is still undergoing
// and another reset starts, only the last reset is processed // and another reset starts, only the last reset is processed
private UUID currentUpdateRequestIdentifier = UUID.randomUUID(); private UUID currentUpdateRequestIdentifier = UUID.randomUUID();
public synchronized void resetFrom( public void resetFrom(@NonNull final Context context, final List<Frameset> framesets) {
@NonNull final Context context, final int seekbarPreviewType = getSeekbarPreviewThumbnailType(context);
final List<Frameset> framesets) {
final int seekbarPreviewType =
SeekbarPreviewThumbnailHelper.getSeekbarPreviewThumbnailType(context);
final UUID updateRequestIdentifier = UUID.randomUUID(); final UUID updateRequestIdentifier = UUID.randomUUID();
this.currentUpdateRequestIdentifier = updateRequestIdentifier; this.currentUpdateRequestIdentifier = updateRequestIdentifier;
@ -63,13 +59,12 @@ public class SeekbarPreviewThumbnailHolder {
executorService.shutdown(); executorService.shutdown();
} }
private void resetFromAsync( private void resetFromAsync(final int seekbarPreviewType, final List<Frameset> framesets,
final int seekbarPreviewType, final UUID updateRequestIdentifier) {
final List<Frameset> framesets,
final UUID updateRequestIdentifier) {
Log.d(TAG, "Clearing seekbarPreviewData"); Log.d(TAG, "Clearing seekbarPreviewData");
seekbarPreviewData.clear(); synchronized (seekbarPreviewData) {
seekbarPreviewData.clear();
}
if (seekbarPreviewType == SeekbarPreviewThumbnailType.NONE) { if (seekbarPreviewType == SeekbarPreviewThumbnailType.NONE) {
Log.d(TAG, "Not processing seekbarPreviewData due to settings"); Log.d(TAG, "Not processing seekbarPreviewData due to settings");
@ -94,10 +89,8 @@ public class SeekbarPreviewThumbnailHolder {
generateDataFrom(frameset, updateRequestIdentifier); generateDataFrom(frameset, updateRequestIdentifier);
} }
private Frameset getFrameSetForType( private Frameset getFrameSetForType(final List<Frameset> framesets,
final List<Frameset> framesets, final int seekbarPreviewType) {
final int seekbarPreviewType) {
if (seekbarPreviewType == SeekbarPreviewThumbnailType.HIGH_QUALITY) { if (seekbarPreviewType == SeekbarPreviewThumbnailType.HIGH_QUALITY) {
Log.d(TAG, "Strategy for seekbarPreviewData: high quality"); Log.d(TAG, "Strategy for seekbarPreviewData: high quality");
return framesets.stream() return framesets.stream()
@ -111,17 +104,14 @@ public class SeekbarPreviewThumbnailHolder {
} }
} }
private void generateDataFrom( private void generateDataFrom(final Frameset frameset, final UUID updateRequestIdentifier) {
final Frameset frameset,
final UUID updateRequestIdentifier) {
Log.d(TAG, "Starting generation of seekbarPreviewData"); Log.d(TAG, "Starting generation of seekbarPreviewData");
final Stopwatch sw = Log.isLoggable(TAG, Log.DEBUG) ? Stopwatch.createStarted() : null; final Stopwatch sw = Log.isLoggable(TAG, Log.DEBUG) ? Stopwatch.createStarted() : null;
int currentPosMs = 0; int currentPosMs = 0;
int pos = 1; int pos = 1;
final int frameCountPerUrl = frameset.getFramesPerPageX() * frameset.getFramesPerPageY(); final int urlFrameCount = frameset.getFramesPerPageX() * frameset.getFramesPerPageY();
// Process each url in the frameset // Process each url in the frameset
for (final String url : frameset.getUrls()) { for (final String url : frameset.getUrls()) {
@ -130,11 +120,11 @@ public class SeekbarPreviewThumbnailHolder {
// The data is not added directly to "seekbarPreviewData" due to // The data is not added directly to "seekbarPreviewData" due to
// concurrency and checks for "updateRequestIdentifier" // concurrency and checks for "updateRequestIdentifier"
final Map<Integer, Supplier<Bitmap>> generatedDataForUrl = new HashMap<>(); final var generatedDataForUrl = new SparseArrayCompat<Supplier<Bitmap>>(urlFrameCount);
// The bitmap consists of several images, which we process here // The bitmap consists of several images, which we process here
// foreach frame in the returned bitmap // foreach frame in the returned bitmap
for (int i = 0; i < frameCountPerUrl; i++) { for (int i = 0; i < urlFrameCount; i++) {
// Frames outside the video length are skipped // Frames outside the video length are skipped
if (pos > frameset.getTotalCount()) { if (pos > frameset.getTotalCount()) {
break; break;
@ -161,7 +151,9 @@ public class SeekbarPreviewThumbnailHolder {
// Check if we are still the latest request // Check if we are still the latest request
// If not abort method execution // If not abort method execution
if (isRequestIdentifierCurrent(updateRequestIdentifier)) { if (isRequestIdentifierCurrent(updateRequestIdentifier)) {
seekbarPreviewData.putAll(generatedDataForUrl); synchronized (seekbarPreviewData) {
seekbarPreviewData.putAll(generatedDataForUrl);
}
} else { } else {
Log.d(TAG, "Aborted of generation of seekbarPreviewData"); Log.d(TAG, "Aborted of generation of seekbarPreviewData");
break; break;
@ -169,7 +161,7 @@ public class SeekbarPreviewThumbnailHolder {
} }
if (sw != null) { if (sw != null) {
Log.d(TAG, "Generation of seekbarPreviewData took " + sw.stop().toString()); Log.d(TAG, "Generation of seekbarPreviewData took " + sw.stop());
} }
} }
@ -189,17 +181,14 @@ public class SeekbarPreviewThumbnailHolder {
final Bitmap bitmap = PicassoHelper.loadSeekbarThumbnailPreview(url).get(); final Bitmap bitmap = PicassoHelper.loadSeekbarThumbnailPreview(url).get();
if (sw != null) { if (sw != null) {
Log.d(TAG, Log.d(TAG, "Download of bitmap for seekbarPreview from '" + url + "' took "
"Download of bitmap for seekbarPreview from '" + url + sw.stop());
+ "' took " + sw.stop().toString());
} }
return bitmap; return bitmap;
} catch (final Exception ex) { } catch (final Exception ex) {
Log.w(TAG, Log.w(TAG, "Failed to get bitmap for seekbarPreview from url='" + url
"Failed to get bitmap for seekbarPreview from url='" + url + "' in time", ex);
+ "' in time",
ex);
return null; return null;
} }
} }
@ -208,32 +197,20 @@ public class SeekbarPreviewThumbnailHolder {
return this.currentUpdateRequestIdentifier.equals(requestIdentifier); return this.currentUpdateRequestIdentifier.equals(requestIdentifier);
} }
public Optional<Bitmap> getBitmapAt(final int positionInMs) { public Optional<Bitmap> getBitmapAt(final int positionInMs) {
// Check if the BitmapData is empty // Get the frame supplier closest to the requested position
if (seekbarPreviewData.isEmpty()) { Supplier<Bitmap> closestFrame = () -> null;
return Optional.empty(); synchronized (seekbarPreviewData) {
int min = Integer.MAX_VALUE;
for (int i = 0; i < seekbarPreviewData.size(); i++) {
final int pos = Math.abs(seekbarPreviewData.keyAt(i) - positionInMs);
if (pos < min) {
closestFrame = seekbarPreviewData.valueAt(i);
min = pos;
}
}
} }
// Get the closest frame to the requested position return Optional.ofNullable(closestFrame.get());
final int closestIndexPosition =
seekbarPreviewData.keySet().stream()
.min(Comparator.comparingInt(i -> Math.abs(i - positionInMs)))
.orElse(-1);
// this should never happen, because
// it indicates that "seekbarPreviewData" is empty which was already checked
if (closestIndexPosition == -1) {
return Optional.empty();
}
try {
// Get the bitmap for the position (executes the supplier)
return Optional.ofNullable(seekbarPreviewData.get(closestIndexPosition).get());
} catch (final Exception ex) {
// If there is an error, log it and return Optional.empty
Log.w(TAG, "Unable to get seekbar preview", ex);
return Optional.empty();
}
} }
} }

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;
} }

View File

@ -1,5 +1,8 @@
package us.shandian.giga.service; package us.shandian.giga.service;
import static org.schabi.newpipe.BuildConfig.APPLICATION_ID;
import static org.schabi.newpipe.BuildConfig.DEBUG;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
@ -22,12 +25,12 @@ import android.os.IBinder;
import android.os.Message; import android.os.Message;
import android.os.Parcelable; import android.os.Parcelable;
import android.util.Log; import android.util.Log;
import android.util.SparseArray;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.collection.SparseArrayCompat;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationCompat.Builder; import androidx.core.app.NotificationCompat.Builder;
import androidx.core.app.ServiceCompat; import androidx.core.app.ServiceCompat;
@ -37,24 +40,21 @@ import androidx.preference.PreferenceManager;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.download.DownloadActivity; import org.schabi.newpipe.download.DownloadActivity;
import org.schabi.newpipe.player.helper.LockManager; import org.schabi.newpipe.player.helper.LockManager;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import us.shandian.giga.get.DownloadMission;
import us.shandian.giga.get.MissionRecoveryInfo;
import org.schabi.newpipe.streams.io.StoredDirectoryHelper; import org.schabi.newpipe.streams.io.StoredDirectoryHelper;
import org.schabi.newpipe.streams.io.StoredFileHelper; import org.schabi.newpipe.streams.io.StoredFileHelper;
import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.PendingIntentCompat; import org.schabi.newpipe.util.PendingIntentCompat;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import us.shandian.giga.get.DownloadMission;
import us.shandian.giga.get.MissionRecoveryInfo;
import us.shandian.giga.postprocessing.Postprocessing; import us.shandian.giga.postprocessing.Postprocessing;
import us.shandian.giga.service.DownloadManager.NetworkState; import us.shandian.giga.service.DownloadManager.NetworkState;
import static org.schabi.newpipe.BuildConfig.APPLICATION_ID;
import static org.schabi.newpipe.BuildConfig.DEBUG;
public class DownloadManagerService extends Service { public class DownloadManagerService extends Service {
private static final String TAG = "DownloadManagerService"; private static final String TAG = "DownloadManagerService";
@ -95,7 +95,7 @@ public class DownloadManagerService extends Service {
private Builder downloadDoneNotification = null; private Builder downloadDoneNotification = null;
private StringBuilder downloadDoneList = null; private StringBuilder downloadDoneList = null;
private final ArrayList<Callback> mEchoObservers = new ArrayList<>(1); private final List<Callback> mEchoObservers = new ArrayList<>(1);
private ConnectivityManager mConnectivityManager; private ConnectivityManager mConnectivityManager;
private ConnectivityManager.NetworkCallback mNetworkStateListenerL = null; private ConnectivityManager.NetworkCallback mNetworkStateListenerL = null;
@ -108,7 +108,8 @@ public class DownloadManagerService extends Service {
private int downloadFailedNotificationID = DOWNLOADS_NOTIFICATION_ID + 1; private int downloadFailedNotificationID = DOWNLOADS_NOTIFICATION_ID + 1;
private Builder downloadFailedNotification = null; private Builder downloadFailedNotification = null;
private final SparseArray<DownloadMission> mFailedDownloads = new SparseArray<>(5); private final SparseArrayCompat<DownloadMission> mFailedDownloads =
new SparseArrayCompat<>(5);
private Bitmap icLauncher; private Bitmap icLauncher;
private Bitmap icDownloadDone; private Bitmap icDownloadDone;
@ -277,7 +278,7 @@ public class DownloadManagerService extends Service {
} }
if (msg.what != MESSAGE_ERROR) if (msg.what != MESSAGE_ERROR)
mFailedDownloads.delete(mFailedDownloads.indexOfValue(mission)); mFailedDownloads.remove(mFailedDownloads.indexOfValue(mission));
for (Callback observer : mEchoObservers) for (Callback observer : mEchoObservers)
observer.handleMessage(msg); observer.handleMessage(msg);
@ -461,7 +462,7 @@ public class DownloadManagerService extends Service {
} }
public void notifyFailedDownload(DownloadMission mission) { public void notifyFailedDownload(DownloadMission mission) {
if (!mDownloadNotificationEnable || mFailedDownloads.indexOfValue(mission) >= 0) return; if (!mDownloadNotificationEnable || mFailedDownloads.containsValue(mission)) return;
int id = downloadFailedNotificationID++; int id = downloadFailedNotificationID++;
mFailedDownloads.put(id, mission); mFailedDownloads.put(id, mission);