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

Use SparseArrayCompat for thumbnails.

This commit is contained in:
Isira Seneviratne 2022-08-22 09:25:08 +05:30
parent aad5e26f31
commit 8db90ba449
3 changed files with 57 additions and 85 deletions

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,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);