mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2026-04-22 14:51:23 +00:00
Clean up Picasso leftovers
This commit is contained in:
@@ -23,9 +23,9 @@ import org.schabi.newpipe.util.Localization;
|
||||
import org.schabi.newpipe.util.ServiceHelper;
|
||||
import org.schabi.newpipe.util.StateSaver;
|
||||
import org.schabi.newpipe.util.image.ImageStrategy;
|
||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||
import org.schabi.newpipe.util.image.PreferredImageQuality;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.SocketException;
|
||||
@@ -113,12 +113,9 @@ public class App extends Application implements ImageLoaderFactory {
|
||||
|
||||
// Initialize image loader
|
||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
PicassoHelper.init(this);
|
||||
ImageStrategy.setPreferredImageQuality(PreferredImageQuality.fromPreferenceKey(this,
|
||||
prefs.getString(getString(R.string.image_quality_key),
|
||||
getString(R.string.image_quality_default))));
|
||||
PicassoHelper.setIndicatorsEnabled(MainActivity.DEBUG
|
||||
&& prefs.getBoolean(getString(R.string.show_image_indicators_key), false));
|
||||
|
||||
configureRxJavaErrorHandler();
|
||||
}
|
||||
@@ -131,13 +128,12 @@ public class App extends Application implements ImageLoaderFactory {
|
||||
.maxSizeBytes(10 * 1024 * 1024)
|
||||
.build())
|
||||
.diskCache(() -> new DiskCache.Builder()
|
||||
.directory(new File(getExternalCacheDir(), "coil"))
|
||||
.maxSizeBytes(50 * 1024 * 1024)
|
||||
.build())
|
||||
.allowRgb565(true);
|
||||
|
||||
final var prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
if (MainActivity.DEBUG
|
||||
&& prefs.getBoolean(getString(R.string.show_image_indicators_key), false)) {
|
||||
if (MainActivity.DEBUG) {
|
||||
builder.logger(new DebugLogger());
|
||||
}
|
||||
|
||||
|
||||
@@ -167,8 +167,8 @@ class AboutActivity : AppCompatActivity() {
|
||||
"https://square.github.io/okhttp/", StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"Picasso", "2013", "Square, Inc.",
|
||||
"https://square.github.io/picasso/", StandardLicenses.APACHE2
|
||||
"Coil", "2023", "Coil Contributors",
|
||||
"https://coil-kt.github.io/coil/", StandardLicenses.APACHE2
|
||||
),
|
||||
SoftwareComponent(
|
||||
"PrettyTime", "2012 - 2020", "Lincoln Baxter, III",
|
||||
|
||||
@@ -160,8 +160,6 @@ public final class VideoDetailFragment
|
||||
private static final String DESCRIPTION_TAB_TAG = "DESCRIPTION TAB";
|
||||
private static final String EMPTY_TAB_TAG = "EMPTY TAB";
|
||||
|
||||
private static final String PICASSO_VIDEO_DETAILS_TAG = "PICASSO_VIDEO_DETAILS_TAG";
|
||||
|
||||
// tabs
|
||||
private boolean showComments;
|
||||
private boolean showRelatedItems;
|
||||
|
||||
@@ -54,12 +54,12 @@ import org.schabi.newpipe.util.ThemeHelper;
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||
import org.schabi.newpipe.util.image.CoilHelper;
|
||||
import org.schabi.newpipe.util.image.ImageStrategy;
|
||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import coil.util.CoilUtils;
|
||||
import icepick.State;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
@@ -74,7 +74,6 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
||||
implements StateSaver.WriteRead {
|
||||
|
||||
private static final int BUTTON_DEBOUNCE_INTERVAL = 100;
|
||||
private static final String PICASSO_CHANNEL_TAG = "PICASSO_CHANNEL_TAG";
|
||||
|
||||
@State
|
||||
protected int serviceId = Constants.NO_SERVICE_ID;
|
||||
@@ -577,7 +576,9 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
|
||||
@Override
|
||||
public void showLoading() {
|
||||
super.showLoading();
|
||||
PicassoHelper.cancelTag(PICASSO_CHANNEL_TAG);
|
||||
CoilUtils.dispose(binding.channelAvatarView);
|
||||
CoilUtils.dispose(binding.channelBannerImage);
|
||||
CoilUtils.dispose(binding.subChannelAvatarView);
|
||||
animate(binding.channelSubscribeButton, false, 100);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@ import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.PlayButtonHelper;
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||
import org.schabi.newpipe.util.image.CoilHelper;
|
||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||
import org.schabi.newpipe.util.text.TextEllipsizer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -63,6 +62,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import coil.util.CoilUtils;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Flowable;
|
||||
import io.reactivex.rxjava3.core.Single;
|
||||
@@ -72,8 +72,6 @@ import io.reactivex.rxjava3.disposables.Disposable;
|
||||
public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, PlaylistInfo>
|
||||
implements PlaylistControlViewHolder {
|
||||
|
||||
private static final String PICASSO_PLAYLIST_TAG = "PICASSO_PLAYLIST_TAG";
|
||||
|
||||
private CompositeDisposable disposables;
|
||||
private Subscription bookmarkReactor;
|
||||
private AtomicBoolean isBookmarkButtonReady;
|
||||
@@ -277,7 +275,7 @@ public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, Playl
|
||||
animate(headerBinding.getRoot(), false, 200);
|
||||
animateHideRecyclerViewAllowingScrolling(itemsList);
|
||||
|
||||
PicassoHelper.cancelTag(PICASSO_PLAYLIST_TAG);
|
||||
CoilUtils.dispose(headerBinding.uploaderAvatarView);
|
||||
animate(headerBinding.uploaderLayout, false, 200);
|
||||
}
|
||||
|
||||
|
||||
13
app/src/main/java/org/schabi/newpipe/ktx/Bitmap.kt
Normal file
13
app/src/main/java/org/schabi/newpipe/ktx/Bitmap.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package org.schabi.newpipe.ktx
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Rect
|
||||
import androidx.core.graphics.BitmapCompat
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun Bitmap.scale(
|
||||
width: Int,
|
||||
height: Int,
|
||||
srcRect: Rect? = null,
|
||||
scaleInLinearSpace: Boolean = true,
|
||||
) = BitmapCompat.createScaledBitmap(this, width, height, srcRect, scaleInLinearSpace)
|
||||
@@ -60,6 +60,7 @@ import android.view.LayoutInflater;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.graphics.drawable.DrawableKt;
|
||||
import androidx.core.math.MathUtils;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
@@ -77,8 +78,6 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
|
||||
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
||||
import com.google.android.exoplayer2.video.VideoSize;
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.squareup.picasso.Target;
|
||||
|
||||
import org.schabi.newpipe.MainActivity;
|
||||
import org.schabi.newpipe.R;
|
||||
@@ -120,12 +119,13 @@ import org.schabi.newpipe.util.ListHelper;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.SerializedCache;
|
||||
import org.schabi.newpipe.util.StreamTypeUtil;
|
||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||
import org.schabi.newpipe.util.image.CoilHelper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import coil.target.Target;
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
@@ -174,7 +174,6 @@ public final class Player implements PlaybackListener, Listener {
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static final int RENDERER_UNAVAILABLE = -1;
|
||||
private static final String PICASSO_PLAYER_THUMBNAIL_TAG = "PICASSO_PLAYER_THUMBNAIL_TAG";
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playback
|
||||
@@ -246,12 +245,6 @@ public final class Player implements PlaybackListener, Listener {
|
||||
@NonNull
|
||||
private final CompositeDisposable databaseUpdateDisposable = new CompositeDisposable();
|
||||
|
||||
// This is the only listener we need for thumbnail loading, since there is always at most only
|
||||
// one thumbnail being loaded at a time. This field is also here to maintain a strong reference,
|
||||
// which would otherwise be garbage collected since Picasso holds weak references to targets.
|
||||
@NonNull
|
||||
private final Target currentThumbnailTarget;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@@ -295,8 +288,6 @@ public final class Player implements PlaybackListener, Listener {
|
||||
videoResolver = new VideoPlaybackResolver(context, dataSource, getQualityResolver());
|
||||
audioResolver = new AudioPlaybackResolver(context, dataSource);
|
||||
|
||||
currentThumbnailTarget = getCurrentThumbnailTarget();
|
||||
|
||||
// The UIs added here should always be present. They will be initialized when the player
|
||||
// reaches the initialization step. Make sure the media session ui is before the
|
||||
// notification ui in the UIs list, since the notification depends on the media session in
|
||||
@@ -602,7 +593,6 @@ public final class Player implements PlaybackListener, Listener {
|
||||
|
||||
databaseUpdateDisposable.clear();
|
||||
progressUpdateDisposable.set(null);
|
||||
cancelLoadingCurrentThumbnail();
|
||||
|
||||
UIs.destroyAll(Object.class); // destroy every UI: obviously every UI extends Object
|
||||
}
|
||||
@@ -776,45 +766,12 @@ public final class Player implements PlaybackListener, Listener {
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
//region Thumbnail loading
|
||||
|
||||
private Target getCurrentThumbnailTarget() {
|
||||
// a Picasso target is just a listener for thumbnail loading events
|
||||
return new Target() {
|
||||
@Override
|
||||
public void onBitmapLoaded(final Bitmap bitmap, final Picasso.LoadedFrom from) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Thumbnail - onBitmapLoaded() called with: bitmap = [" + bitmap
|
||||
+ " -> " + bitmap.getWidth() + "x" + bitmap.getHeight() + "], from = ["
|
||||
+ from + "]");
|
||||
}
|
||||
// there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too.
|
||||
onThumbnailLoaded(bitmap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBitmapFailed(final Exception e, final Drawable errorDrawable) {
|
||||
Log.e(TAG, "Thumbnail - onBitmapFailed() called", e);
|
||||
// there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too.
|
||||
onThumbnailLoaded(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareLoad(final Drawable placeHolderDrawable) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Thumbnail - onPrepareLoad() called");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void loadCurrentThumbnail(final List<Image> thumbnails) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Thumbnail - loadCurrentThumbnail() called with thumbnails = ["
|
||||
+ thumbnails.size() + "]");
|
||||
}
|
||||
|
||||
// first cancel any previous loading
|
||||
cancelLoadingCurrentThumbnail();
|
||||
|
||||
// Unset currentThumbnail, since it is now outdated. This ensures it is not used in media
|
||||
// session metadata while the new thumbnail is being loaded by Coil.
|
||||
onThumbnailLoaded(null);
|
||||
@@ -823,20 +780,38 @@ public final class Player implements PlaybackListener, Listener {
|
||||
}
|
||||
|
||||
// scale down the notification thumbnail for performance
|
||||
PicassoHelper.loadScaledDownThumbnail(context, thumbnails)
|
||||
.tag(PICASSO_PLAYER_THUMBNAIL_TAG)
|
||||
.into(currentThumbnailTarget);
|
||||
}
|
||||
final var target = new Target() {
|
||||
@Override
|
||||
public void onError(@Nullable final Drawable error) {
|
||||
Log.e(TAG, "Thumbnail - onError() called");
|
||||
// there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too.
|
||||
onThumbnailLoaded(null);
|
||||
}
|
||||
|
||||
private void cancelLoadingCurrentThumbnail() {
|
||||
// cancel the Picasso job associated with the player thumbnail, if any
|
||||
PicassoHelper.cancelTag(PICASSO_PLAYER_THUMBNAIL_TAG);
|
||||
@Override
|
||||
public void onStart(@Nullable final Drawable placeholder) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Thumbnail - onStart() called");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(@NonNull final Drawable result) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Thumbnail - onSuccess() called with: drawable = [" + result + "]");
|
||||
}
|
||||
// there is a new thumbnail, so e.g. the end screen thumbnail needs to change, too.
|
||||
onThumbnailLoaded(DrawableKt.toBitmapOrNull(result, result.getIntrinsicWidth(),
|
||||
result.getIntrinsicHeight(), null));
|
||||
}
|
||||
};
|
||||
CoilHelper.INSTANCE.loadScaledDownThumbnail(context, thumbnails, target);
|
||||
}
|
||||
|
||||
private void onThumbnailLoaded(@Nullable final Bitmap bitmap) {
|
||||
// Avoid useless thumbnail updates, if the thumbnail has not actually changed. Based on the
|
||||
// thumbnail loading code, this if would be skipped only when both bitmaps are `null`, since
|
||||
// onThumbnailLoaded won't be called twice with the same nonnull bitmap by Picasso's target.
|
||||
// onThumbnailLoaded won't be called twice with the same nonnull bitmap by Coil's target.
|
||||
if (currentThumbnail != bitmap) {
|
||||
currentThumbnail = bitmap;
|
||||
UIs.call(playerUi -> playerUi.onThumbnailLoaded(bitmap));
|
||||
|
||||
@@ -49,7 +49,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
|
||||
loader.getDiskCache().clear();
|
||||
}
|
||||
Toast.makeText(preference.getContext(),
|
||||
R.string.thumbnail_cache_wipe_complete_notice, Toast.LENGTH_SHORT)
|
||||
R.string.thumbnail_cache_wipe_complete_notice, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -10,7 +10,6 @@ import org.schabi.newpipe.error.ErrorInfo;
|
||||
import org.schabi.newpipe.error.ErrorUtil;
|
||||
import org.schabi.newpipe.error.UserAction;
|
||||
import org.schabi.newpipe.local.feed.notifications.NotificationWorker;
|
||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -25,8 +24,6 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
|
||||
findPreference(getString(R.string.allow_heap_dumping_key));
|
||||
final Preference showMemoryLeaksPreference =
|
||||
findPreference(getString(R.string.show_memory_leaks_key));
|
||||
final Preference showImageIndicatorsPreference =
|
||||
findPreference(getString(R.string.show_image_indicators_key));
|
||||
final Preference checkNewStreamsPreference =
|
||||
findPreference(getString(R.string.check_new_streams_key));
|
||||
final Preference crashTheAppPreference =
|
||||
@@ -38,7 +35,6 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
|
||||
|
||||
assert allowHeapDumpingPreference != null;
|
||||
assert showMemoryLeaksPreference != null;
|
||||
assert showImageIndicatorsPreference != null;
|
||||
assert checkNewStreamsPreference != null;
|
||||
assert crashTheAppPreference != null;
|
||||
assert showErrorSnackbarPreference != null;
|
||||
@@ -61,11 +57,6 @@ public class DebugSettingsFragment extends BasePreferenceFragment {
|
||||
showMemoryLeaksPreference.setSummary(R.string.leak_canary_not_available);
|
||||
}
|
||||
|
||||
showImageIndicatorsPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
PicassoHelper.setIndicatorsEnabled((Boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
|
||||
checkNewStreamsPreference.setOnPreferenceClickListener(preference -> {
|
||||
NotificationWorker.runNow(preference.getContext());
|
||||
return true;
|
||||
|
||||
@@ -24,8 +24,8 @@ import androidx.core.content.FileProvider;
|
||||
import org.schabi.newpipe.BuildConfig;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.Image;
|
||||
import org.schabi.newpipe.util.image.CoilHelper;
|
||||
import org.schabi.newpipe.util.image.ImageStrategy;
|
||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -278,7 +278,7 @@ public final class ShareUtils {
|
||||
* @param content the content to share
|
||||
* @param images a set of possible {@link Image}s of the subject, among which to choose with
|
||||
* {@link ImageStrategy#choosePreferredImage(List)} since that's likely to
|
||||
* provide an image that is in Picasso's cache
|
||||
* provide an image that is in Coil's cache
|
||||
*/
|
||||
public static void shareText(@NonNull final Context context,
|
||||
@NonNull final String title,
|
||||
@@ -335,15 +335,7 @@ public final class ShareUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a {@link ClipData} with the image of the content shared, if it's in the app cache.
|
||||
*
|
||||
* <p>
|
||||
* In order not to worry about network issues (timeouts, DNS issues, low connection speed, ...)
|
||||
* when sharing a content, only images in the {@link com.squareup.picasso.LruCache LruCache}
|
||||
* used by the Picasso library inside {@link PicassoHelper} are used as preview images. If the
|
||||
* thumbnail image is not in the cache, no {@link ClipData} will be generated and {@code null}
|
||||
* will be returned.
|
||||
* </p>
|
||||
* Generate a {@link ClipData} with the image of the content shared.
|
||||
*
|
||||
* <p>
|
||||
* In order to display the image in the content preview of the Android share sheet, an URI of
|
||||
@@ -359,9 +351,8 @@ public final class ShareUtils {
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This method will call {@link PicassoHelper#getImageFromCacheIfPresent(String)} to get the
|
||||
* thumbnail of the content in the {@link com.squareup.picasso.LruCache LruCache} used by
|
||||
* the Picasso library inside {@link PicassoHelper}.
|
||||
* This method will call {@link CoilHelper#loadBitmap(Context, String)} to get the
|
||||
* thumbnail of the content.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
@@ -378,7 +369,7 @@ public final class ShareUtils {
|
||||
@NonNull final Context context,
|
||||
@NonNull final String thumbnailUrl) {
|
||||
try {
|
||||
final Bitmap bitmap = PicassoHelper.getImageFromCacheIfPresent(thumbnailUrl);
|
||||
final var bitmap = CoilHelper.INSTANCE.loadBitmap(context, thumbnailUrl);
|
||||
if (bitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2,16 +2,25 @@ package org.schabi.newpipe.util.image
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.util.Log
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.graphics.drawable.toBitmapOrNull
|
||||
import coil.executeBlocking
|
||||
import coil.imageLoader
|
||||
import coil.request.ImageRequest
|
||||
import coil.size.Size
|
||||
import coil.target.Target
|
||||
import coil.transform.Transformation
|
||||
import org.schabi.newpipe.MainActivity
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.extractor.Image
|
||||
import org.schabi.newpipe.ktx.scale
|
||||
import kotlin.math.min
|
||||
|
||||
object CoilHelper {
|
||||
private const val TAG = "CoilHelper"
|
||||
|
||||
fun loadBitmap(context: Context, url: String): Bitmap? {
|
||||
val request = ImageRequest.Builder(context)
|
||||
.data(url)
|
||||
@@ -35,6 +44,44 @@ object CoilHelper {
|
||||
loadImageDefault(target, url, R.drawable.placeholder_thumbnail_video)
|
||||
}
|
||||
|
||||
fun loadScaledDownThumbnail(context: Context, images: List<Image>, target: Target) {
|
||||
val url = ImageStrategy.choosePreferredImage(images)
|
||||
val request = getImageRequest(context, url, R.drawable.placeholder_thumbnail_video)
|
||||
.target(target)
|
||||
.transformations(object : Transformation {
|
||||
override val cacheKey = "COIL_PLAYER_THUMBNAIL_TRANSFORMATION_KEY"
|
||||
|
||||
override suspend fun transform(input: Bitmap, size: Size): Bitmap {
|
||||
if (MainActivity.DEBUG) {
|
||||
Log.d(TAG, "Thumbnail - transform() called")
|
||||
}
|
||||
|
||||
val notificationThumbnailWidth = min(
|
||||
context.resources.getDimension(R.dimen.player_notification_thumbnail_width),
|
||||
input.width.toFloat()
|
||||
).toInt()
|
||||
|
||||
var newHeight = input.height / (input.width / notificationThumbnailWidth)
|
||||
val result = input.scale(notificationThumbnailWidth, newHeight)
|
||||
|
||||
if (result == input || !result.isMutable) {
|
||||
// create a new mutable bitmap to prevent strange crashes on some
|
||||
// devices (see #4638)
|
||||
newHeight = input.height / (input.width / (notificationThumbnailWidth - 1))
|
||||
val copied = input.scale(notificationThumbnailWidth, newHeight)
|
||||
input.recycle()
|
||||
return copied
|
||||
} else {
|
||||
input.recycle()
|
||||
return result
|
||||
}
|
||||
}
|
||||
})
|
||||
.build()
|
||||
|
||||
context.imageLoader.enqueue(request)
|
||||
}
|
||||
|
||||
fun loadDetailsThumbnail(target: ImageView, images: List<Image>) {
|
||||
val url = ImageStrategy.choosePreferredImage(images)
|
||||
loadImageDefault(target, url, R.drawable.placeholder_thumbnail_video, false)
|
||||
@@ -67,6 +114,7 @@ object CoilHelper {
|
||||
showPlaceholder: Boolean = true
|
||||
) {
|
||||
val request = getImageRequest(target.context, url, placeholderResId, showPlaceholder)
|
||||
.target(target)
|
||||
.build()
|
||||
target.context.imageLoader.enqueue(request)
|
||||
}
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
package org.schabi.newpipe.util.image;
|
||||
|
||||
import static org.schabi.newpipe.MainActivity.DEBUG;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
import static org.schabi.newpipe.util.image.ImageStrategy.choosePreferredImage;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.graphics.BitmapCompat;
|
||||
|
||||
import com.squareup.picasso.Cache;
|
||||
import com.squareup.picasso.LruCache;
|
||||
import com.squareup.picasso.OkHttp3Downloader;
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.squareup.picasso.RequestCreator;
|
||||
import com.squareup.picasso.Transformation;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.Image;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
public final class PicassoHelper {
|
||||
private static final String TAG = PicassoHelper.class.getSimpleName();
|
||||
private static final String PLAYER_THUMBNAIL_TRANSFORMATION_KEY =
|
||||
"PICASSO_PLAYER_THUMBNAIL_TRANSFORMATION_KEY";
|
||||
|
||||
private PicassoHelper() {
|
||||
}
|
||||
|
||||
private static Cache picassoCache;
|
||||
private static OkHttpClient picassoDownloaderClient;
|
||||
|
||||
// suppress because terminate() is called in App.onTerminate(), preventing leaks
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static Picasso picassoInstance;
|
||||
|
||||
|
||||
public static void init(final Context context) {
|
||||
picassoCache = new LruCache(10 * 1024 * 1024);
|
||||
picassoDownloaderClient = new OkHttpClient.Builder()
|
||||
.cache(new okhttp3.Cache(new File(context.getExternalCacheDir(), "picasso"),
|
||||
50L * 1024L * 1024L))
|
||||
// this should already be the default timeout in OkHttp3, but just to be sure...
|
||||
.callTimeout(15, TimeUnit.SECONDS)
|
||||
.build();
|
||||
|
||||
picassoInstance = new Picasso.Builder(context)
|
||||
.memoryCache(picassoCache) // memory cache
|
||||
.downloader(new OkHttp3Downloader(picassoDownloaderClient)) // disk cache
|
||||
.defaultBitmapConfig(Bitmap.Config.RGB_565)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static void cancelTag(final Object tag) {
|
||||
picassoInstance.cancelTag(tag);
|
||||
}
|
||||
|
||||
public static void setIndicatorsEnabled(final boolean enabled) {
|
||||
picassoInstance.setIndicatorsEnabled(enabled); // useful for debugging
|
||||
}
|
||||
|
||||
public static RequestCreator loadThumbnail(@NonNull final List<Image> images) {
|
||||
return loadImageDefault(images, R.drawable.placeholder_thumbnail_video);
|
||||
}
|
||||
|
||||
public static RequestCreator loadScaledDownThumbnail(final Context context,
|
||||
@NonNull final List<Image> images) {
|
||||
// scale down the notification thumbnail for performance
|
||||
return loadThumbnail(images)
|
||||
.transform(new Transformation() {
|
||||
@Override
|
||||
public Bitmap transform(final Bitmap source) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Thumbnail - transform() called");
|
||||
}
|
||||
|
||||
final float notificationThumbnailWidth = Math.min(
|
||||
context.getResources()
|
||||
.getDimension(R.dimen.player_notification_thumbnail_width),
|
||||
source.getWidth());
|
||||
|
||||
final Bitmap result = BitmapCompat.createScaledBitmap(
|
||||
source,
|
||||
(int) notificationThumbnailWidth,
|
||||
(int) (source.getHeight()
|
||||
/ (source.getWidth() / notificationThumbnailWidth)),
|
||||
null,
|
||||
true);
|
||||
|
||||
if (result == source || !result.isMutable()) {
|
||||
// create a new mutable bitmap to prevent strange crashes on some
|
||||
// devices (see #4638)
|
||||
final Bitmap copied = BitmapCompat.createScaledBitmap(
|
||||
source,
|
||||
(int) notificationThumbnailWidth - 1,
|
||||
(int) (source.getHeight() / (source.getWidth()
|
||||
/ (notificationThumbnailWidth - 1))),
|
||||
null,
|
||||
true);
|
||||
source.recycle();
|
||||
return copied;
|
||||
} else {
|
||||
source.recycle();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String key() {
|
||||
return PLAYER_THUMBNAIL_TRANSFORMATION_KEY;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Bitmap getImageFromCacheIfPresent(@NonNull final String imageUrl) {
|
||||
// URLs in the internal cache finish with \n so we need to add \n to image URLs
|
||||
return picassoCache.get(imageUrl + "\n");
|
||||
}
|
||||
|
||||
|
||||
private static RequestCreator loadImageDefault(@NonNull final List<Image> images,
|
||||
@DrawableRes final int placeholderResId) {
|
||||
return loadImageDefault(choosePreferredImage(images), placeholderResId);
|
||||
}
|
||||
|
||||
private static RequestCreator loadImageDefault(@Nullable final String url,
|
||||
@DrawableRes final int placeholderResId) {
|
||||
return loadImageDefault(url, placeholderResId, true);
|
||||
}
|
||||
|
||||
private static RequestCreator loadImageDefault(@Nullable final String url,
|
||||
@DrawableRes final int placeholderResId,
|
||||
final boolean showPlaceholderWhileLoading) {
|
||||
// if the URL was chosen with `choosePreferredImage` it will be null, but check again
|
||||
// `shouldLoadImages` in case the URL was chosen with `imageListToDbUrl` (which is the case
|
||||
// for URLs stored in the database)
|
||||
if (isNullOrEmpty(url) || !ImageStrategy.shouldLoadImages()) {
|
||||
return picassoInstance
|
||||
.load((String) null)
|
||||
.placeholder(placeholderResId) // show placeholder when no image should load
|
||||
.error(placeholderResId);
|
||||
} else {
|
||||
final RequestCreator requestCreator = picassoInstance
|
||||
.load(url)
|
||||
.error(placeholderResId);
|
||||
if (showPlaceholderWhileLoading) {
|
||||
requestCreator.placeholder(placeholderResId);
|
||||
}
|
||||
return requestCreator;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user