diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java
index 5e8977821..a64f2952c 100644
--- a/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java
+++ b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java
@@ -70,7 +70,9 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem {
@Ignore
public PlaylistRemoteEntity(final PlaylistInfo info) {
this(info.getServiceId(), info.getName(), info.getUrl(),
- ImageStrategy.choosePreferredImage(info.getThumbnails()),
+ // use uploader avatar when no thumbnail is available
+ ImageStrategy.choosePreferredImage(info.getThumbnails().isEmpty()
+ ? info.getUploaderAvatars() : info.getThumbnails()),
info.getUploaderName(), info.getStreamCount());
}
@@ -84,6 +86,8 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem {
&& getStreamCount() == info.getStreamCount()
&& TextUtils.equals(getName(), info.getName())
&& TextUtils.equals(getUrl(), info.getUrl())
+ // we want to update the local playlist data even when either the remote thumbnail
+ // URL changes, or the preferred image quality setting is changed by the user
&& TextUtils.equals(getThumbnailUrl(),
ImageStrategy.choosePreferredImage(info.getThumbnails()))
&& TextUtils.equals(getUploader(), info.getUploaderName());
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/BaseDescriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/BaseDescriptionFragment.java
index 2bd5906bc..ffd10d827 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/BaseDescriptionFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/BaseDescriptionFragment.java
@@ -224,6 +224,7 @@ public abstract class BaseDescriptionFragment extends BaseFragment {
case LOW:
urls.append(getString(R.string.image_quality_low));
break;
+ default: // unreachable, Image.ResolutionLevel.UNKNOWN is already filtered out
case MEDIUM:
urls.append(getString(R.string.image_quality_medium));
break;
diff --git a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java
index fc057de41..7524e5413 100644
--- a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java
+++ b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java
@@ -269,8 +269,8 @@ public final class ShareUtils {
*
*
* For Android 10+ users, a content preview is shown, which includes the title of the shared
- * content and an image preview the content, if its URL is not null or empty and its
- * corresponding image is in the image cache.
+ * content and an image preview the content, if the preferred image chosen by {@link
+ * ImageStrategy#choosePreferredImage(List)} is in the image cache.
*
*
* @param context the context to use
diff --git a/app/src/main/java/org/schabi/newpipe/util/image/ImageStrategy.java b/app/src/main/java/org/schabi/newpipe/util/image/ImageStrategy.java
index 89e63af3f..1583c0b09 100644
--- a/app/src/main/java/org/schabi/newpipe/util/image/ImageStrategy.java
+++ b/app/src/main/java/org/schabi/newpipe/util/image/ImageStrategy.java
@@ -38,19 +38,35 @@ public final class ImageStrategy {
// images whose size is completely unknown will be in their own subgroups, so
// any one of them will do, hence returning the same value for all of them
return 0;
-
} else {
return image.getWidth() * image.getWidth() / widthOverHeight;
}
-
} else if (image.getWidth() == WIDTH_UNKNOWN) {
return image.getHeight() * image.getHeight() * widthOverHeight;
-
} else {
return image.getHeight() * image.getWidth();
}
}
+ /**
+ * Chooses an image amongst the provided list based on the user preference previously set with
+ * {@link #setPreferredImageQuality(PreferredImageQuality)}. {@code null} will be returned in
+ * case the list is empty or the user preference is to not show images.
+ *
+ * These properties will be preferred, from most to least important:
+ *
+ * - The image's {@link Image#getEstimatedResolutionLevel()} is not unknown and is close
+ * to {@link #preferredImageQuality}
+ * - At least one of the image's width or height are known
+ * - The highest resolution image is finally chosen if the user's preference is {@link
+ * PreferredImageQuality#HIGH}, otherwise the chosen image is the one that has the closest
+ * height to {@link #BEST_LOW_H} or {@link #BEST_MEDIUM_H}
+ *
+ *
+ * @param images the images from which to choose
+ * @return the chosen preferred image, or {@link null} if the list is empty or the user disabled
+ * images
+ */
@Nullable
public static String choosePreferredImage(@NonNull final List images) {
if (preferredImageQuality == PreferredImageQuality.NONE) {
diff --git a/app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java b/app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java
index ccf7c3737..fb0c97fe1 100644
--- a/app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java
@@ -9,6 +9,7 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.BitmapCompat;
@@ -49,7 +50,7 @@ public final class PicassoHelper {
picassoCache = new LruCache(10 * 1024 * 1024);
picassoDownloaderClient = new OkHttpClient.Builder()
.cache(new okhttp3.Cache(new File(context.getExternalCacheDir(), "picasso"),
- 50 * 1024 * 1024))
+ 50L * 1024L * 1024L))
// this should already be the default timeout in OkHttp3, but just to be sure...
.callTimeout(15, TimeUnit.SECONDS)
.build();
@@ -90,50 +91,50 @@ public final class PicassoHelper {
}
- public static RequestCreator loadAvatar(final List images) {
+ public static RequestCreator loadAvatar(@NonNull final List images) {
return loadImageDefault(images, R.drawable.placeholder_person);
}
- public static RequestCreator loadAvatar(final String url) {
+ public static RequestCreator loadAvatar(@Nullable final String url) {
return loadImageDefault(url, R.drawable.placeholder_person);
}
- public static RequestCreator loadThumbnail(final List images) {
+ public static RequestCreator loadThumbnail(@NonNull final List images) {
return loadImageDefault(images, R.drawable.placeholder_thumbnail_video);
}
- public static RequestCreator loadThumbnail(final String url) {
+ public static RequestCreator loadThumbnail(@Nullable final String url) {
return loadImageDefault(url, R.drawable.placeholder_thumbnail_video);
}
- public static RequestCreator loadDetailsThumbnail(final List images) {
+ public static RequestCreator loadDetailsThumbnail(@NonNull final List images) {
return loadImageDefault(choosePreferredImage(images),
R.drawable.placeholder_thumbnail_video, false);
}
- public static RequestCreator loadBanner(final List images) {
+ public static RequestCreator loadBanner(@NonNull final List images) {
return loadImageDefault(images, R.drawable.placeholder_channel_banner);
}
- public static RequestCreator loadPlaylistThumbnail(final List images) {
+ public static RequestCreator loadPlaylistThumbnail(@NonNull final List images) {
return loadImageDefault(images, R.drawable.placeholder_thumbnail_playlist);
}
- public static RequestCreator loadPlaylistThumbnail(final String url) {
+ public static RequestCreator loadPlaylistThumbnail(@Nullable final String url) {
return loadImageDefault(url, R.drawable.placeholder_thumbnail_playlist);
}
- public static RequestCreator loadSeekbarThumbnailPreview(final String url) {
+ public static RequestCreator loadSeekbarThumbnailPreview(@Nullable final String url) {
return picassoInstance.load(url);
}
- public static RequestCreator loadNotificationIcon(final String url) {
+ public static RequestCreator loadNotificationIcon(@Nullable final String url) {
return loadImageDefault(url, R.drawable.ic_newpipe_triangle_white);
}
public static RequestCreator loadScaledDownThumbnail(final Context context,
- final List images) {
+ @NonNull final List images) {
// scale down the notification thumbnail for performance
return PicassoHelper.loadThumbnail(images)
.transform(new Transformation() {
@@ -182,18 +183,18 @@ public final class PicassoHelper {
}
@Nullable
- public static Bitmap getImageFromCacheIfPresent(final String imageUrl) {
+ 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(final List images,
+ private static RequestCreator loadImageDefault(@NonNull final List images,
final int placeholderResId) {
return loadImageDefault(choosePreferredImage(images), placeholderResId);
}
- private static RequestCreator loadImageDefault(final String url,
+ private static RequestCreator loadImageDefault(@Nullable final String url,
final int placeholderResId) {
return loadImageDefault(url, placeholderResId, true);
}