Merge pull request #8895 from Isira-Seneviratne/SparseArrayCompat
Use SparseArrayCompat.
This commit is contained in:
commit
231e677b16
|
@ -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(
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 List<Frameset> framesets,
|
|
||||||
final UUID updateRequestIdentifier) {
|
final UUID updateRequestIdentifier) {
|
||||||
|
|
||||||
Log.d(TAG, "Clearing seekbarPreviewData");
|
Log.d(TAG, "Clearing seekbarPreviewData");
|
||||||
|
synchronized (seekbarPreviewData) {
|
||||||
seekbarPreviewData.clear();
|
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)) {
|
||||||
|
synchronized (seekbarPreviewData) {
|
||||||
seekbarPreviewData.putAll(generatedDataForUrl);
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue