Implement better image selection strategy
This commit is contained in:
parent
a6a7c2271a
commit
4164bf36f6
19 changed files with 161 additions and 89 deletions
|
|
@ -24,6 +24,7 @@ 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.ImageStrategy;
|
||||
import org.schabi.newpipe.util.image.PicassoHelper;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -251,7 +252,7 @@ public final class ShareUtils {
|
|||
// If loading of images has been disabled, don't try to generate a content preview
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||
&& !TextUtils.isEmpty(imagePreviewUrl)
|
||||
&& PicassoHelper.shouldLoadImages()) {
|
||||
&& ImageStrategy.shouldLoadImages()) {
|
||||
|
||||
final ClipData clipData = generateClipDataForImagePreview(context, imagePreviewUrl);
|
||||
if (clipData != null) {
|
||||
|
|
@ -276,14 +277,14 @@ public final class ShareUtils {
|
|||
* @param title the title of the content
|
||||
* @param content the content to share
|
||||
* @param images a set of possible {@link Image}s of the subject, among which to choose with
|
||||
* {@link PicassoHelper#choosePreferredImage(List)} since that's likely to
|
||||
* {@link ImageStrategy#choosePreferredImage(List)} since that's likely to
|
||||
* provide an image that is in Picasso's cache
|
||||
*/
|
||||
public static void shareText(@NonNull final Context context,
|
||||
@NonNull final String title,
|
||||
final String content,
|
||||
final List<Image> images) {
|
||||
shareText(context, title, content, PicassoHelper.choosePreferredImage(images));
|
||||
shareText(context, title, content, ImageStrategy.choosePreferredImage(images));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
package org.schabi.newpipe.util.image;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.schabi.newpipe.extractor.Image;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public final class ImageStrategy {
|
||||
|
||||
// the height thresholds also used by the extractor (TODO move them to the extractor)
|
||||
private static final int LOW_MEDIUM = 175;
|
||||
private static final int MEDIUM_HIGH = 720;
|
||||
|
||||
private static PreferredImageQuality preferredImageQuality = PreferredImageQuality.MEDIUM;
|
||||
|
||||
private ImageStrategy() {
|
||||
}
|
||||
|
||||
public static void setPreferredImageQuality(final PreferredImageQuality preferredImageQuality) {
|
||||
ImageStrategy.preferredImageQuality = preferredImageQuality;
|
||||
}
|
||||
|
||||
public static boolean shouldLoadImages() {
|
||||
return preferredImageQuality != PreferredImageQuality.NONE;
|
||||
}
|
||||
|
||||
|
||||
private static double estimatePixelCount(final Image image,
|
||||
final double widthOverHeight,
|
||||
final boolean unknownsLast) {
|
||||
if (image.getHeight() == Image.HEIGHT_UNKNOWN) {
|
||||
if (image.getWidth() == Image.WIDTH_UNKNOWN) {
|
||||
switch (image.getEstimatedResolutionLevel()) {
|
||||
case LOW:
|
||||
return unknownsLast
|
||||
? (LOW_MEDIUM - 1) * (LOW_MEDIUM - 1) * widthOverHeight
|
||||
: 0;
|
||||
case MEDIUM:
|
||||
return unknownsLast
|
||||
? (MEDIUM_HIGH - 1) * (MEDIUM_HIGH - 1) * widthOverHeight
|
||||
: LOW_MEDIUM * LOW_MEDIUM * widthOverHeight;
|
||||
case HIGH:
|
||||
return unknownsLast
|
||||
? 1e20 // less than 1e21 to prefer over fully unknown image sizes
|
||||
: MEDIUM_HIGH * MEDIUM_HIGH * widthOverHeight;
|
||||
default:
|
||||
case UNKNOWN:
|
||||
// images whose size is completely unknown will be avoided when possible
|
||||
return unknownsLast ? 1e21 : -1;
|
||||
}
|
||||
|
||||
} else {
|
||||
return image.getWidth() * image.getWidth() / widthOverHeight;
|
||||
}
|
||||
|
||||
} else if (image.getWidth() == Image.WIDTH_UNKNOWN) {
|
||||
return image.getHeight() * image.getHeight() * widthOverHeight;
|
||||
|
||||
} else {
|
||||
return image.getHeight() * image.getWidth();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String choosePreferredImage(@NonNull final List<Image> images) {
|
||||
if (preferredImageQuality == PreferredImageQuality.NONE) {
|
||||
return null; // do not load images
|
||||
}
|
||||
|
||||
final double widthOverHeight = images.stream()
|
||||
.filter(image -> image.getHeight() != Image.HEIGHT_UNKNOWN
|
||||
&& image.getWidth() != Image.WIDTH_UNKNOWN)
|
||||
.mapToDouble(image -> ((double) image.getWidth()) / image.getHeight())
|
||||
.findFirst()
|
||||
.orElse(1.0);
|
||||
|
||||
final Comparator<Image> comparator;
|
||||
switch (preferredImageQuality) {
|
||||
case LOW:
|
||||
comparator = Comparator.comparingDouble(
|
||||
image -> estimatePixelCount(image, widthOverHeight, true));
|
||||
break;
|
||||
default:
|
||||
case MEDIUM:
|
||||
comparator = Comparator.comparingDouble(image -> {
|
||||
final double pixelCount = estimatePixelCount(image, widthOverHeight, true);
|
||||
final double mediumHeight = (LOW_MEDIUM + MEDIUM_HIGH) / 2.0;
|
||||
return Math.abs(pixelCount - mediumHeight * mediumHeight * widthOverHeight);
|
||||
});
|
||||
break;
|
||||
case HIGH:
|
||||
comparator = Comparator.<Image>comparingDouble(
|
||||
image -> estimatePixelCount(image, widthOverHeight, false))
|
||||
.reversed();
|
||||
break;
|
||||
}
|
||||
|
||||
return images.stream()
|
||||
.min(comparator)
|
||||
.map(Image::getUrl)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static List<Image> urlToImageList(@Nullable final String url) {
|
||||
if (url == null) {
|
||||
return List.of();
|
||||
} else {
|
||||
return List.of(new Image(url, -1, -1, Image.ResolutionLevel.UNKNOWN));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +2,13 @@ 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.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.graphics.BitmapCompat;
|
||||
|
||||
|
|
@ -21,11 +21,9 @@ import com.squareup.picasso.Transformation;
|
|||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.Image;
|
||||
import org.schabi.newpipe.extractor.Image.ResolutionLevel;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
|
@ -46,7 +44,6 @@ public final class PicassoHelper {
|
|||
@SuppressLint("StaticFieldLeak")
|
||||
private static Picasso picassoInstance;
|
||||
|
||||
private static PreferredImageQuality preferredImageQuality = PreferredImageQuality.MEDIUM;
|
||||
|
||||
public static void init(final Context context) {
|
||||
picassoCache = new LruCache(10 * 1024 * 1024);
|
||||
|
|
@ -92,14 +89,6 @@ public final class PicassoHelper {
|
|||
picassoInstance.setIndicatorsEnabled(enabled); // useful for debugging
|
||||
}
|
||||
|
||||
public static void setPreferredImageQuality(final PreferredImageQuality preferredImageQuality) {
|
||||
PicassoHelper.preferredImageQuality = preferredImageQuality;
|
||||
}
|
||||
|
||||
public static boolean shouldLoadImages() {
|
||||
return preferredImageQuality != PreferredImageQuality.NONE;
|
||||
}
|
||||
|
||||
|
||||
public static RequestCreator loadAvatar(final List<Image> images) {
|
||||
return loadImageDefault(images, R.drawable.placeholder_person);
|
||||
|
|
@ -227,41 +216,4 @@ public final class PicassoHelper {
|
|||
return requestCreator;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String choosePreferredImage(final List<Image> images) {
|
||||
final Comparator<Image> comparator;
|
||||
switch (preferredImageQuality) {
|
||||
case NONE:
|
||||
return null;
|
||||
case HIGH:
|
||||
comparator = Comparator.comparingInt(Image::getHeight).reversed();
|
||||
break;
|
||||
default:
|
||||
case MEDIUM:
|
||||
comparator = Comparator.comparingInt(image -> Math.abs(image.getHeight() - 450));
|
||||
break;
|
||||
case LOW:
|
||||
comparator = Comparator.comparingInt(Image::getHeight);
|
||||
break;
|
||||
}
|
||||
|
||||
return images.stream()
|
||||
.filter(image -> image.getEstimatedResolutionLevel() != ResolutionLevel.UNKNOWN)
|
||||
.min(comparator)
|
||||
.map(Image::getUrl)
|
||||
.orElseGet(() -> images.stream()
|
||||
.findAny()
|
||||
.map(Image::getUrl)
|
||||
.orElse(null));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static List<Image> urlToImageList(@Nullable final String url) {
|
||||
if (url == null) {
|
||||
return List.of();
|
||||
} else {
|
||||
return List.of(new Image(url, -1, -1, ResolutionLevel.UNKNOWN));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue