1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2024-12-23 16:40:32 +00:00

Addressed code review comments

This commit is contained in:
Isira Seneviratne 2024-07-22 09:41:07 +05:30
parent da83646303
commit 4ec7532126
6 changed files with 60 additions and 35 deletions

View File

@ -1473,6 +1473,7 @@ public final class VideoDetailFragment
CoilUtils.dispose(binding.detailThumbnailImageView); CoilUtils.dispose(binding.detailThumbnailImageView);
CoilUtils.dispose(binding.detailSubChannelThumbnailView); CoilUtils.dispose(binding.detailSubChannelThumbnailView);
CoilUtils.dispose(binding.overlayThumbnail); CoilUtils.dispose(binding.overlayThumbnail);
CoilUtils.dispose(binding.detailUploaderThumbnailView);
binding.detailThumbnailImageView.setImageBitmap(null); binding.detailThumbnailImageView.setImageBitmap(null);
binding.detailSubChannelThumbnailView.setImageBitmap(null); binding.detailSubChannelThumbnailView.setImageBitmap(null);

View File

@ -76,8 +76,7 @@ class NotificationHelper(val context: Context) {
summaryBuilder.setLargeIcon(avatarIcon) summaryBuilder.setLargeIcon(avatarIcon)
// Show individual stream notifications, set channel icon only if there is actually // Show individual stream notifications, set channel icon only if there is actually one
// one
showStreamNotifications(newStreams, data.serviceId, avatarIcon) showStreamNotifications(newStreams, data.serviceId, avatarIcon)
// Show summary notification // Show summary notification
manager.notify(data.pseudoId, summaryBuilder.build()) manager.notify(data.pseudoId, summaryBuilder.build())

View File

@ -192,6 +192,8 @@ public final class Player implements PlaybackListener, Listener {
private MediaItemTag currentMetadata; private MediaItemTag currentMetadata;
@Nullable @Nullable
private Bitmap currentThumbnail; private Bitmap currentThumbnail;
@Nullable
private coil.request.Disposable thumbnailDisposable;
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Player // Player
@ -772,6 +774,11 @@ public final class Player implements PlaybackListener, Listener {
+ thumbnails.size() + "]"); + thumbnails.size() + "]");
} }
// Cancel any ongoing image loading
if (thumbnailDisposable != null) {
thumbnailDisposable.dispose();
}
// Unset currentThumbnail, since it is now outdated. This ensures it is not used in media // 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. // session metadata while the new thumbnail is being loaded by Coil.
onThumbnailLoaded(null); onThumbnailLoaded(null);
@ -780,7 +787,7 @@ public final class Player implements PlaybackListener, Listener {
} }
// scale down the notification thumbnail for performance // scale down the notification thumbnail for performance
final var target = new Target() { final var thumbnailTarget = new Target() {
@Override @Override
public void onError(@Nullable final Drawable error) { public void onError(@Nullable final Drawable error) {
Log.e(TAG, "Thumbnail - onError() called"); Log.e(TAG, "Thumbnail - onError() called");
@ -805,7 +812,8 @@ public final class Player implements PlaybackListener, Listener {
result.getIntrinsicHeight(), null)); result.getIntrinsicHeight(), null));
} }
}; };
CoilHelper.INSTANCE.loadScaledDownThumbnail(context, thumbnails, target); thumbnailDisposable = CoilHelper.INSTANCE
.loadScaledDownThumbnail(context, thumbnails, thumbnailTarget);
} }
private void onThumbnailLoaded(@Nullable final Bitmap bitmap) { private void onThumbnailLoaded(@Nullable final Bitmap bitmap) {

View File

@ -42,12 +42,8 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
ImageStrategy.setPreferredImageQuality(PreferredImageQuality ImageStrategy.setPreferredImageQuality(PreferredImageQuality
.fromPreferenceKey(requireContext(), (String) newValue)); .fromPreferenceKey(requireContext(), (String) newValue));
final var loader = Coil.imageLoader(preference.getContext()); final var loader = Coil.imageLoader(preference.getContext());
if (loader.getMemoryCache() != null) { loader.getMemoryCache().clear();
loader.getMemoryCache().clear(); loader.getDiskCache().clear();
}
if (loader.getDiskCache() != null) {
loader.getDiskCache().clear();
}
Toast.makeText(preference.getContext(), 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(); .show();

View File

@ -10,6 +10,7 @@ import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.text.TextUtils; import android.text.TextUtils;
@ -24,13 +25,16 @@ import androidx.core.content.FileProvider;
import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.Image; 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.ImageStrategy;
import java.io.File; import java.nio.file.Files;
import java.io.FileOutputStream; import java.util.Collections;
import java.util.List; import java.util.List;
import coil.Coil;
import coil.disk.DiskCache;
import coil.memory.MemoryCache;
public final class ShareUtils { public final class ShareUtils {
private static final String TAG = ShareUtils.class.getSimpleName(); private static final String TAG = ShareUtils.class.getSimpleName();
@ -335,7 +339,13 @@ public final class ShareUtils {
} }
/** /**
* Generate a {@link ClipData} with the image of the content shared. * 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 MemoryCache} or {@link DiskCache}
* used by the Coil library 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> * <p>
* In order to display the image in the content preview of the Android share sheet, an URI of * In order to display the image in the content preview of the Android share sheet, an URI of
@ -351,11 +361,6 @@ public final class ShareUtils {
* </p> * </p>
* *
* <p> * <p>
* This method will call {@link CoilHelper#loadBitmapBlocking(Context, String)} to get the
* thumbnail of the content.
* </p>
*
* <p>
* Using the result of this method when sharing has only an effect on the system share sheet (if * Using the result of this method when sharing has only an effect on the system share sheet (if
* OEMs didn't change Android system standard behavior) on Android API 29 and higher. * OEMs didn't change Android system standard behavior) on Android API 29 and higher.
* </p> * </p>
@ -369,33 +374,46 @@ public final class ShareUtils {
@NonNull final Context context, @NonNull final Context context,
@NonNull final String thumbnailUrl) { @NonNull final String thumbnailUrl) {
try { try {
final var bitmap = CoilHelper.INSTANCE.loadBitmapBlocking(context, thumbnailUrl);
if (bitmap == null) {
return null;
}
// Save the image in memory to the application's cache because we need a URI to the // Save the image in memory to the application's cache because we need a URI to the
// image to generate a ClipData which will show the share sheet, and so an image file // image to generate a ClipData which will show the share sheet, and so an image file
final Context applicationContext = context.getApplicationContext(); final Context applicationContext = context.getApplicationContext();
final String appFolder = applicationContext.getCacheDir().getAbsolutePath(); final var loader = Coil.imageLoader(context);
final File thumbnailPreviewFile = new File(appFolder final var value = loader.getMemoryCache()
+ "/android_share_sheet_image_preview.jpg"); .get(new MemoryCache.Key(thumbnailUrl, Collections.emptyMap()));
// Any existing file will be overwritten with FileOutputStream final Bitmap cachedBitmap;
final FileOutputStream fileOutputStream = new FileOutputStream(thumbnailPreviewFile); if (value != null) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileOutputStream); cachedBitmap = value.getBitmap();
fileOutputStream.close(); } else {
try (var snapshot = loader.getDiskCache().openSnapshot(thumbnailUrl)) {
if (snapshot != null) {
cachedBitmap = BitmapFactory.decodeFile(snapshot.getData().toString());
} else {
cachedBitmap = null;
}
}
}
if (cachedBitmap == null) {
return null;
}
final var path = applicationContext.getCacheDir().toPath()
.resolve("android_share_sheet_image_preview.jpg");
// Any existing file will be overwritten
try (var outputStream = Files.newOutputStream(path)) {
cachedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream);
}
final ClipData clipData = ClipData.newUri(applicationContext.getContentResolver(), "", final ClipData clipData = ClipData.newUri(applicationContext.getContentResolver(), "",
FileProvider.getUriForFile(applicationContext, FileProvider.getUriForFile(applicationContext,
BuildConfig.APPLICATION_ID + ".provider", BuildConfig.APPLICATION_ID + ".provider",
thumbnailPreviewFile)); path.toFile()));
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "ClipData successfully generated for Android share sheet: " + clipData); Log.d(TAG, "ClipData successfully generated for Android share sheet: " + clipData);
} }
return clipData; return clipData;
} catch (final Exception e) { } catch (final Exception e) {
Log.w(TAG, "Error when setting preview image for share sheet", e); Log.w(TAG, "Error when setting preview image for share sheet", e);
return null; return null;

View File

@ -8,6 +8,7 @@ import androidx.annotation.DrawableRes
import androidx.core.graphics.drawable.toBitmapOrNull import androidx.core.graphics.drawable.toBitmapOrNull
import coil.executeBlocking import coil.executeBlocking
import coil.imageLoader import coil.imageLoader
import coil.request.Disposable
import coil.request.ImageRequest import coil.request.ImageRequest
import coil.size.Size import coil.size.Size
import coil.target.Target import coil.target.Target
@ -47,7 +48,7 @@ object CoilHelper {
loadImageDefault(target, url, R.drawable.placeholder_thumbnail_video) loadImageDefault(target, url, R.drawable.placeholder_thumbnail_video)
} }
fun loadScaledDownThumbnail(context: Context, images: List<Image>, target: Target) { fun loadScaledDownThumbnail(context: Context, images: List<Image>, target: Target): Disposable {
val url = ImageStrategy.choosePreferredImage(images) val url = ImageStrategy.choosePreferredImage(images)
val request = getImageRequest(context, url, R.drawable.placeholder_thumbnail_video) val request = getImageRequest(context, url, R.drawable.placeholder_thumbnail_video)
.target(target) .target(target)
@ -79,7 +80,7 @@ object CoilHelper {
}) })
.build() .build()
context.imageLoader.enqueue(request) return context.imageLoader.enqueue(request)
} }
fun loadDetailsThumbnail(target: ImageView, images: List<Image>) { fun loadDetailsThumbnail(target: ImageView, images: List<Image>) {
@ -133,6 +134,8 @@ object CoilHelper {
return ImageRequest.Builder(context) return ImageRequest.Builder(context)
.data(takenUrl) .data(takenUrl)
.error(placeholderResId) .error(placeholderResId)
.memoryCacheKey(takenUrl)
.diskCacheKey(takenUrl)
.apply { .apply {
if (takenUrl != null || showPlaceholderWhileLoading) { if (takenUrl != null || showPlaceholderWhileLoading) {
placeholder(placeholderResId) placeholder(placeholderResId)