V5 Push
Here's a list of changes/features: https://github.com/RetroMusicPlayer/RetroMusicPlayer/releases/tag/v5.0 Internal Changes: 1) Migrated to ViewBinding 2) Migrated to Glide V4 3) Migrated to kotlin version of Material Dialogs
This commit is contained in:
parent
fc42767031
commit
bce6dbfa27
421 changed files with 13285 additions and 5757 deletions
|
@ -1,130 +0,0 @@
|
|||
package code.name.monkey.retromusic.glide;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import androidx.annotation.NonNull;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover;
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder;
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
import code.name.monkey.retromusic.util.MusicUtil;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
import com.bumptech.glide.BitmapRequestBuilder;
|
||||
import com.bumptech.glide.DrawableRequestBuilder;
|
||||
import com.bumptech.glide.DrawableTypeRequest;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
import com.bumptech.glide.load.Key;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
|
||||
import com.bumptech.glide.signature.MediaStoreSignature;
|
||||
|
||||
public class AlbumGlideRequest {
|
||||
private static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.NONE;
|
||||
private static final int DEFAULT_ERROR_IMAGE = R.drawable.default_album_art;
|
||||
private static final int DEFAULT_ANIMATION = android.R.anim.fade_in;
|
||||
|
||||
@NonNull
|
||||
private static DrawableTypeRequest createBaseRequest(
|
||||
@NonNull RequestManager requestManager, @NonNull Song song, boolean ignoreMediaStore) {
|
||||
if (ignoreMediaStore) {
|
||||
return requestManager.load(new AudioFileCover(song.getData()));
|
||||
} else {
|
||||
return requestManager.loadFromMediaStore(
|
||||
MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(song.getAlbumId()));
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Key createSignature(@NonNull Song song) {
|
||||
return new MediaStoreSignature("", song.getDateModified(), 0);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
final RequestManager requestManager;
|
||||
final Song song;
|
||||
boolean ignoreMediaStore;
|
||||
|
||||
private Builder(@NonNull RequestManager requestManager, Song song) {
|
||||
this.requestManager = requestManager;
|
||||
this.song = song;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Builder from(@NonNull RequestManager requestManager, Song song) {
|
||||
return new Builder(requestManager, song);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public PaletteBuilder generatePalette(@NonNull Context context) {
|
||||
return new PaletteBuilder(this, context);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public BitmapBuilder asBitmap() {
|
||||
return new BitmapBuilder(this);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder checkIgnoreMediaStore() {
|
||||
return ignoreMediaStore(PreferenceUtil.INSTANCE.isIgnoreMediaStoreArtwork());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder ignoreMediaStore(boolean ignoreMediaStore) {
|
||||
this.ignoreMediaStore = ignoreMediaStore;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public DrawableRequestBuilder<GlideDrawable> build() {
|
||||
//noinspection unchecked
|
||||
return createBaseRequest(requestManager, song, ignoreMediaStore)
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.signature(createSignature(song));
|
||||
}
|
||||
}
|
||||
|
||||
public static class BitmapBuilder {
|
||||
private final Builder builder;
|
||||
|
||||
BitmapBuilder(Builder builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public BitmapRequestBuilder<?, Bitmap> build() {
|
||||
//noinspection unchecked
|
||||
return createBaseRequest(builder.requestManager, builder.song, builder.ignoreMediaStore)
|
||||
.asBitmap()
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.dontTransform()
|
||||
.signature(createSignature(builder.song));
|
||||
}
|
||||
}
|
||||
|
||||
public static class PaletteBuilder {
|
||||
private final Context context;
|
||||
private final Builder builder;
|
||||
|
||||
PaletteBuilder(Builder builder, Context context) {
|
||||
this.builder = builder;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public BitmapRequestBuilder<?, BitmapPaletteWrapper> build() {
|
||||
|
||||
//noinspection unchecked
|
||||
return createBaseRequest(builder.requestManager, builder.song, builder.ignoreMediaStore)
|
||||
.asBitmap()
|
||||
.transcode(new BitmapPaletteTranscoder(context), BitmapPaletteWrapper.class)
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.signature(createSignature(builder.song));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,171 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package code.name.monkey.retromusic.glide;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import code.name.monkey.appthemehelper.ThemeStore;
|
||||
import code.name.monkey.appthemehelper.util.TintHelper;
|
||||
import code.name.monkey.retromusic.App;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.glide.artistimage.ArtistImage;
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder;
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
|
||||
import code.name.monkey.retromusic.model.Artist;
|
||||
import code.name.monkey.retromusic.util.ArtistSignatureUtil;
|
||||
import code.name.monkey.retromusic.util.CustomArtistImageUtil;
|
||||
import com.bumptech.glide.BitmapRequestBuilder;
|
||||
import com.bumptech.glide.DrawableRequestBuilder;
|
||||
import com.bumptech.glide.DrawableTypeRequest;
|
||||
import com.bumptech.glide.Priority;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
import com.bumptech.glide.load.Key;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
|
||||
public class ArtistGlideRequest {
|
||||
private static final int DEFAULT_ANIMATION = android.R.anim.fade_in;
|
||||
|
||||
private static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.SOURCE;
|
||||
|
||||
private static final int DEFAULT_ERROR_IMAGE = R.drawable.default_artist_art;
|
||||
|
||||
@NonNull
|
||||
private static Key createSignature(@NonNull Artist artist) {
|
||||
return ArtistSignatureUtil.getInstance(App.Companion.getContext())
|
||||
.getArtistSignature(artist.getName());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static DrawableTypeRequest createBaseRequest(
|
||||
@NonNull RequestManager requestManager,
|
||||
@NonNull Artist artist,
|
||||
boolean noCustomImage,
|
||||
boolean forceDownload) {
|
||||
boolean hasCustomImage =
|
||||
CustomArtistImageUtil.Companion.getInstance(App.Companion.getContext())
|
||||
.hasCustomArtistImage(artist);
|
||||
if (noCustomImage || !hasCustomImage) {
|
||||
return requestManager.load(new ArtistImage(artist));
|
||||
} else {
|
||||
return requestManager.load(CustomArtistImageUtil.getFile(artist));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
final Artist artist;
|
||||
final RequestManager requestManager;
|
||||
private Drawable error;
|
||||
private boolean forceDownload;
|
||||
private boolean noCustomImage;
|
||||
|
||||
private Builder(@NonNull RequestManager requestManager, Artist artist) {
|
||||
this.requestManager = requestManager;
|
||||
this.artist = artist;
|
||||
error =
|
||||
TintHelper.createTintedDrawable(
|
||||
ContextCompat.getDrawable(App.Companion.getContext(), R.drawable.default_artist_art),
|
||||
ThemeStore.Companion.accentColor(App.Companion.getContext()));
|
||||
}
|
||||
|
||||
public static Builder from(@NonNull RequestManager requestManager, Artist artist) {
|
||||
return new Builder(requestManager, artist);
|
||||
}
|
||||
|
||||
public BitmapBuilder asBitmap() {
|
||||
return new BitmapBuilder(this);
|
||||
}
|
||||
|
||||
public DrawableRequestBuilder<GlideDrawable> build() {
|
||||
//noinspection unchecked
|
||||
return createBaseRequest(requestManager, artist, noCustomImage, forceDownload)
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.priority(Priority.LOW)
|
||||
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
|
||||
.dontTransform()
|
||||
.signature(createSignature(artist));
|
||||
}
|
||||
|
||||
public Builder forceDownload(boolean forceDownload) {
|
||||
this.forceDownload = forceDownload;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PaletteBuilder generatePalette(Context context) {
|
||||
return new PaletteBuilder(this, context);
|
||||
}
|
||||
|
||||
public Builder noCustomImage(boolean noCustomImage) {
|
||||
this.noCustomImage = noCustomImage;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class BitmapBuilder {
|
||||
|
||||
private final Builder builder;
|
||||
|
||||
BitmapBuilder(Builder builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public BitmapRequestBuilder<?, Bitmap> build() {
|
||||
//noinspection unchecked
|
||||
return createBaseRequest(
|
||||
builder.requestManager, builder.artist, builder.noCustomImage, builder.forceDownload)
|
||||
.asBitmap()
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.priority(Priority.LOW)
|
||||
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
|
||||
.dontTransform()
|
||||
.signature(createSignature(builder.artist));
|
||||
}
|
||||
}
|
||||
|
||||
public static class PaletteBuilder {
|
||||
|
||||
final Context context;
|
||||
|
||||
private final Builder builder;
|
||||
|
||||
PaletteBuilder(Builder builder, Context context) {
|
||||
this.builder = builder;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public BitmapRequestBuilder<?, BitmapPaletteWrapper> build() {
|
||||
//noinspection unchecked
|
||||
return createBaseRequest(
|
||||
builder.requestManager, builder.artist, builder.noCustomImage, builder.forceDownload)
|
||||
.asBitmap()
|
||||
.transcode(new BitmapPaletteTranscoder(context), BitmapPaletteWrapper.class)
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.priority(Priority.LOW)
|
||||
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
|
||||
.dontTransform()
|
||||
.signature(createSignature(builder.artist));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +1,9 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Hemanth Savarla.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
package code.name.monkey.retromusic.glide
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.os.Build
|
||||
import android.renderscript.*
|
||||
import androidx.annotation.FloatRange
|
||||
import code.name.monkey.retromusic.BuildConfig
|
||||
|
@ -26,12 +11,14 @@ import code.name.monkey.retromusic.helper.StackBlur
|
|||
import code.name.monkey.retromusic.util.ImageUtil
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
|
||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
||||
import java.security.MessageDigest
|
||||
|
||||
|
||||
class BlurTransformation : BitmapTransformation {
|
||||
|
||||
private var context: Context? = null
|
||||
private var blurRadius: Float = 0.toFloat()
|
||||
private var sampling: Int = 0
|
||||
private var blurRadius = 0f
|
||||
private var sampling = 0
|
||||
|
||||
private fun init(builder: Builder) {
|
||||
this.context = builder.context
|
||||
|
@ -39,18 +26,18 @@ class BlurTransformation : BitmapTransformation {
|
|||
this.sampling = builder.sampling
|
||||
}
|
||||
|
||||
private constructor(builder: Builder) : super(builder.context) {
|
||||
private constructor(builder: Builder) : super() {
|
||||
init(builder)
|
||||
}
|
||||
|
||||
private constructor(builder: Builder, bitmapPool: BitmapPool) : super(bitmapPool) {
|
||||
private constructor(builder: Builder, bitmapPool: BitmapPool) : super() {
|
||||
init(builder)
|
||||
}
|
||||
|
||||
class Builder(val context: Context) {
|
||||
private var bitmapPool: BitmapPool? = null
|
||||
var blurRadius = DEFAULT_BLUR_RADIUS
|
||||
var sampling: Int = 0
|
||||
var sampling = 0
|
||||
|
||||
/**
|
||||
* @param blurRadius The radius to use. Must be between 0 and 25. Default is 5.
|
||||
|
@ -74,7 +61,7 @@ class BlurTransformation : BitmapTransformation {
|
|||
* @param bitmapPool The BitmapPool to use.
|
||||
* @return the same Builder
|
||||
*/
|
||||
fun bitmapPool(bitmapPool: BitmapPool): Builder {
|
||||
fun bitmapPool(bitmapPool: BitmapPool?): Builder {
|
||||
this.bitmapPool = bitmapPool
|
||||
return this
|
||||
}
|
||||
|
@ -91,65 +78,60 @@ class BlurTransformation : BitmapTransformation {
|
|||
toTransform: Bitmap,
|
||||
outWidth: Int,
|
||||
outHeight: Int
|
||||
): Bitmap? {
|
||||
val sampling: Int
|
||||
if (this.sampling == 0) {
|
||||
sampling = ImageUtil.calculateInSampleSize(toTransform.width, toTransform.height, 100)
|
||||
): Bitmap {
|
||||
val sampling = if (this.sampling == 0) {
|
||||
ImageUtil.calculateInSampleSize(toTransform.width, toTransform.height, 100)
|
||||
} else {
|
||||
sampling = this.sampling
|
||||
this.sampling
|
||||
}
|
||||
|
||||
val width = toTransform.width
|
||||
val height = toTransform.height
|
||||
val scaledWidth = width / sampling
|
||||
val scaledHeight = height / sampling
|
||||
|
||||
var out: Bitmap? = pool.get(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888)
|
||||
if (out == null) {
|
||||
out = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888)
|
||||
}
|
||||
|
||||
val canvas = Canvas(out!!)
|
||||
val out = pool[scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888]
|
||||
val canvas = Canvas(out)
|
||||
canvas.scale(1 / sampling.toFloat(), 1 / sampling.toFloat())
|
||||
val paint = Paint()
|
||||
paint.flags = Paint.FILTER_BITMAP_FLAG
|
||||
canvas.drawBitmap(toTransform, 0f, 0f, paint)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 17) {
|
||||
try {
|
||||
val rs = RenderScript.create(context!!.applicationContext)
|
||||
val input = Allocation.createFromBitmap(
|
||||
rs,
|
||||
out,
|
||||
Allocation.MipmapControl.MIPMAP_NONE,
|
||||
Allocation.USAGE_SCRIPT
|
||||
)
|
||||
val output = Allocation.createTyped(rs, input.type)
|
||||
val script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs))
|
||||
try {
|
||||
val rs = RenderScript.create(context!!.applicationContext)
|
||||
val input = Allocation.createFromBitmap(
|
||||
rs,
|
||||
out,
|
||||
Allocation.MipmapControl.MIPMAP_NONE,
|
||||
Allocation.USAGE_SCRIPT
|
||||
)
|
||||
val output = Allocation.createTyped(rs, input.type)
|
||||
val script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs))
|
||||
|
||||
script.setRadius(blurRadius)
|
||||
script.setInput(input)
|
||||
script.forEach(output)
|
||||
script.setRadius(blurRadius)
|
||||
script.setInput(input)
|
||||
script.forEach(output)
|
||||
|
||||
output.copyTo(out)
|
||||
output.copyTo(out)
|
||||
|
||||
rs.destroy()
|
||||
rs.destroy()
|
||||
|
||||
return out
|
||||
} catch (e: RSRuntimeException) {
|
||||
// on some devices RenderScript.create() throws: android.support.v8.renderscript.RSRuntimeException: Error loading libRSSupport library
|
||||
if (BuildConfig.DEBUG) e.printStackTrace()
|
||||
}
|
||||
return out
|
||||
} catch (e: RSRuntimeException) {
|
||||
// on some devices RenderScript.create() throws: android.support.v8.renderscript.RSRuntimeException: Error loading libRSSupport library
|
||||
if (BuildConfig.DEBUG) e.printStackTrace()
|
||||
}
|
||||
|
||||
return StackBlur.blur(out, blurRadius)
|
||||
}
|
||||
|
||||
override fun getId(): String {
|
||||
return "BlurTransformation(radius=$blurRadius, sampling=$sampling)"
|
||||
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
|
||||
messageDigest.update(
|
||||
"BlurTransformation(radius=$blurRadius, sampling=$sampling)".toByteArray(
|
||||
CHARSET
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val DEFAULT_BLUR_RADIUS = 5f
|
||||
const val DEFAULT_BLUR_RADIUS = 5f
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
package code.name.monkey.retromusic.glide;
|
||||
|
||||
import static code.name.monkey.retromusic.Constants.USER_BANNER;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import androidx.annotation.NonNull;
|
||||
import code.name.monkey.retromusic.App;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import com.bumptech.glide.BitmapRequestBuilder;
|
||||
import com.bumptech.glide.BitmapTypeRequest;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
import com.bumptech.glide.load.Key;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.signature.MediaStoreSignature;
|
||||
import java.io.File;
|
||||
|
||||
public class ProfileBannerGlideRequest {
|
||||
private static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.NONE;
|
||||
private static final int DEFAULT_ERROR_IMAGE = R.drawable.material_design_default;
|
||||
private static final int DEFAULT_ANIMATION = android.R.anim.fade_in;
|
||||
|
||||
public static File getBannerModel() {
|
||||
File dir = App.Companion.getContext().getFilesDir();
|
||||
return new File(dir, USER_BANNER);
|
||||
}
|
||||
|
||||
private static BitmapTypeRequest<File> createBaseRequest(
|
||||
RequestManager requestManager, File profile) {
|
||||
return requestManager.load(profile).asBitmap();
|
||||
}
|
||||
|
||||
private static Key createSignature(File file) {
|
||||
return new MediaStoreSignature("", file.lastModified(), 0);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private RequestManager requestManager;
|
||||
private File profile;
|
||||
|
||||
private Builder(RequestManager requestManager, File profile) {
|
||||
this.requestManager = requestManager;
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
public static Builder from(@NonNull RequestManager requestManager, File profile) {
|
||||
return new Builder(requestManager, profile);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public BitmapRequestBuilder<File, Bitmap> build() {
|
||||
//noinspection unchecked
|
||||
return createBaseRequest(requestManager, profile)
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.placeholder(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.signature(createSignature(profile));
|
||||
}
|
||||
}
|
||||
|
||||
public static class BitmapBuilder {
|
||||
private final Builder builder;
|
||||
|
||||
BitmapBuilder(Builder builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public BitmapRequestBuilder<?, Bitmap> build() {
|
||||
//noinspection unchecked
|
||||
return createBaseRequest(builder.requestManager, builder.profile)
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.signature(createSignature(builder.profile));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
package code.name.monkey.retromusic.glide
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.retromusic.App.Companion.getContext
|
||||
import code.name.monkey.retromusic.Constants.USER_BANNER
|
||||
import code.name.monkey.retromusic.Constants.USER_PROFILE
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.glide.artistimage.ArtistImage
|
||||
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.ArtistSignatureUtil
|
||||
import code.name.monkey.retromusic.util.CustomArtistImageUtil.Companion.getFile
|
||||
import code.name.monkey.retromusic.util.CustomArtistImageUtil.Companion.getInstance
|
||||
import code.name.monkey.retromusic.util.MusicUtil.getMediaStoreAlbumCoverUri
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import com.bumptech.glide.GenericTransitionOptions
|
||||
import com.bumptech.glide.Priority
|
||||
import com.bumptech.glide.RequestBuilder
|
||||
import com.bumptech.glide.annotation.GlideExtension
|
||||
import com.bumptech.glide.annotation.GlideOption
|
||||
import com.bumptech.glide.annotation.GlideType
|
||||
import com.bumptech.glide.load.Key
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.BaseRequestOptions
|
||||
import com.bumptech.glide.request.target.Target.SIZE_ORIGINAL
|
||||
import com.bumptech.glide.signature.MediaStoreSignature
|
||||
import java.io.File
|
||||
|
||||
|
||||
@GlideExtension
|
||||
object RetroGlideExtension {
|
||||
|
||||
private const val DEFAULT_ERROR_ARTIST_IMAGE =
|
||||
R.drawable.default_artist_art
|
||||
private const val DEFAULT_ERROR_SONG_IMAGE: Int = R.drawable.default_audio_art
|
||||
private const val DEFAULT_ERROR_ALBUM_IMAGE = R.drawable.default_album_art
|
||||
private const val DEFAULT_ERROR_IMAGE_BANNER = R.drawable.material_design_default
|
||||
|
||||
private val DEFAULT_DISK_CACHE_STRATEGY_ARTIST = DiskCacheStrategy.RESOURCE
|
||||
private val DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.NONE
|
||||
|
||||
private const val DEFAULT_ANIMATION = android.R.anim.fade_in
|
||||
|
||||
@JvmStatic
|
||||
@GlideType(BitmapPaletteWrapper::class)
|
||||
fun asBitmapPalette(requestBuilder: RequestBuilder<BitmapPaletteWrapper>): RequestBuilder<BitmapPaletteWrapper> {
|
||||
return requestBuilder
|
||||
}
|
||||
|
||||
private fun getSongModel(song: Song, ignoreMediaStore: Boolean): Any {
|
||||
return if (ignoreMediaStore) {
|
||||
AudioFileCover(song.data)
|
||||
} else {
|
||||
getMediaStoreAlbumCoverUri(song.albumId)
|
||||
}
|
||||
}
|
||||
|
||||
fun getSongModel(song: Song): Any {
|
||||
return getSongModel(song, PreferenceUtil.isIgnoreMediaStoreArtwork)
|
||||
}
|
||||
|
||||
fun getArtistModel(artist: Artist): Any {
|
||||
return getArtistModel(
|
||||
artist,
|
||||
getInstance(getContext()).hasCustomArtistImage(artist),
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
fun getArtistModel(artist: Artist, forceDownload: Boolean): Any {
|
||||
return getArtistModel(
|
||||
artist,
|
||||
getInstance(getContext()).hasCustomArtistImage(artist),
|
||||
forceDownload
|
||||
)
|
||||
}
|
||||
|
||||
private fun getArtistModel(artist: Artist, hasCustomImage: Boolean, forceDownload: Boolean): Any {
|
||||
return if (!hasCustomImage) {
|
||||
ArtistImage(artist)
|
||||
} else {
|
||||
getFile(artist)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@GlideOption
|
||||
fun artistImageOptions(
|
||||
baseRequestOptions: BaseRequestOptions<*>,
|
||||
artist: Artist
|
||||
): BaseRequestOptions<*> {
|
||||
return baseRequestOptions
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY_ARTIST)
|
||||
.priority(Priority.LOW)
|
||||
.error(DEFAULT_ERROR_ARTIST_IMAGE)
|
||||
.override(SIZE_ORIGINAL, SIZE_ORIGINAL)
|
||||
.signature(createSignature(artist))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@GlideOption
|
||||
fun songCoverOptions(
|
||||
baseRequestOptions: BaseRequestOptions<*>,
|
||||
song: Song
|
||||
): BaseRequestOptions<*> {
|
||||
return baseRequestOptions.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_SONG_IMAGE)
|
||||
.signature(createSignature(song))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@GlideOption
|
||||
fun albumCoverOptions(
|
||||
baseRequestOptions: BaseRequestOptions<*>,
|
||||
song: Song
|
||||
): BaseRequestOptions<*> {
|
||||
return baseRequestOptions.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_ALBUM_IMAGE)
|
||||
.signature(createSignature(song))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@GlideOption
|
||||
fun userProfileOptions(
|
||||
baseRequestOptions: BaseRequestOptions<*>,
|
||||
file: File
|
||||
): BaseRequestOptions<*> {
|
||||
return baseRequestOptions.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(getErrorUserProfile())
|
||||
.signature(createSignature(file))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@GlideOption
|
||||
fun profileBannerOptions(
|
||||
baseRequestOptions: BaseRequestOptions<*>,
|
||||
file: File
|
||||
): BaseRequestOptions<*> {
|
||||
return baseRequestOptions.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.placeholder(DEFAULT_ERROR_IMAGE_BANNER)
|
||||
.error(DEFAULT_ERROR_IMAGE_BANNER)
|
||||
.signature(createSignature(file))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@GlideOption
|
||||
fun playlistOptions(
|
||||
baseRequestOptions: BaseRequestOptions<*>
|
||||
): BaseRequestOptions<*> {
|
||||
return baseRequestOptions.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_ALBUM_IMAGE)
|
||||
}
|
||||
|
||||
private fun createSignature(song: Song): Key {
|
||||
return MediaStoreSignature("", song.dateModified, 0)
|
||||
}
|
||||
|
||||
private fun createSignature(file: File): Key {
|
||||
return MediaStoreSignature("", file.lastModified(), 0)
|
||||
}
|
||||
|
||||
private fun createSignature(artist: Artist): Key {
|
||||
return ArtistSignatureUtil.getInstance(getContext())
|
||||
.getArtistSignature(artist.name)
|
||||
}
|
||||
|
||||
fun getUserModel(): File {
|
||||
val dir = getContext().filesDir
|
||||
return File(dir, USER_PROFILE)
|
||||
}
|
||||
|
||||
fun getBannerModel(): File {
|
||||
val dir = getContext().filesDir
|
||||
return File(dir, USER_BANNER)
|
||||
}
|
||||
|
||||
private fun getErrorUserProfile(): Drawable {
|
||||
return TintHelper.createTintedDrawable(
|
||||
getContext(),
|
||||
R.drawable.ic_account,
|
||||
accentColor(getContext())
|
||||
)
|
||||
}
|
||||
|
||||
fun <TranscodeType> getDefaultTransition(): GenericTransitionOptions<TranscodeType> {
|
||||
return GenericTransitionOptions<TranscodeType>().transition(DEFAULT_ANIMATION)
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ import code.name.monkey.retromusic.R
|
|||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTarget
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.request.animation.GlideAnimation
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
|
||||
abstract class RetroMusicColoredTarget(view: ImageView) : BitmapPaletteTarget(view) {
|
||||
|
||||
|
@ -31,21 +31,19 @@ abstract class RetroMusicColoredTarget(view: ImageView) : BitmapPaletteTarget(vi
|
|||
|
||||
abstract fun onColorReady(colors: MediaNotificationProcessor)
|
||||
|
||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(e, errorDrawable)
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
val colors = MediaNotificationProcessor(App.getContext(), errorDrawable)
|
||||
onColorReady(colors)
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper?,
|
||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>?
|
||||
resource: BitmapPaletteWrapper,
|
||||
transition: Transition<in BitmapPaletteWrapper>?
|
||||
) {
|
||||
super.onResourceReady(resource, glideAnimation)
|
||||
resource?.let { bitmapWrap ->
|
||||
MediaNotificationProcessor(App.getContext()).getPaletteAsync({
|
||||
onColorReady(it)
|
||||
}, bitmapWrap.bitmap)
|
||||
}
|
||||
super.onResourceReady(resource, transition)
|
||||
MediaNotificationProcessor(App.getContext()).getPaletteAsync({
|
||||
onColorReady(it)
|
||||
}, resource.bitmap)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,25 +15,38 @@
|
|||
package code.name.monkey.retromusic.glide
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import code.name.monkey.retromusic.glide.artistimage.ArtistImage
|
||||
import code.name.monkey.retromusic.glide.artistimage.Factory
|
||||
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover
|
||||
import code.name.monkey.retromusic.glide.audiocover.AudioFileCoverLoader
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.glide.playlistPreview.PlaylistPreview
|
||||
import code.name.monkey.retromusic.glide.playlistPreview.PlaylistPreviewLoader
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.GlideBuilder
|
||||
import com.bumptech.glide.module.GlideModule
|
||||
import com.bumptech.glide.Registry
|
||||
import com.bumptech.glide.annotation.GlideModule
|
||||
import com.bumptech.glide.module.AppGlideModule
|
||||
import java.io.InputStream
|
||||
|
||||
class RetroMusicGlideModule : GlideModule {
|
||||
override fun applyOptions(context: Context, builder: GlideBuilder) {
|
||||
}
|
||||
|
||||
override fun registerComponents(context: Context, glide: Glide) {
|
||||
glide.register(
|
||||
@GlideModule
|
||||
class RetroMusicGlideModule : AppGlideModule() {
|
||||
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
|
||||
registry.prepend(PlaylistPreview::class.java, Bitmap::class.java, PlaylistPreviewLoader.Factory(context))
|
||||
registry.prepend(
|
||||
AudioFileCover::class.java,
|
||||
InputStream::class.java,
|
||||
AudioFileCoverLoader.Factory()
|
||||
)
|
||||
glide.register(ArtistImage::class.java, InputStream::class.java, Factory(context))
|
||||
registry.prepend(ArtistImage::class.java, InputStream::class.java, Factory(context))
|
||||
registry.register(
|
||||
Bitmap::class.java,
|
||||
BitmapPaletteWrapper::class.java, BitmapPaletteTranscoder()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun isManifestParsingEnabled(): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ import code.name.monkey.retromusic.R
|
|||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTarget
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.util.ColorUtil
|
||||
import com.bumptech.glide.request.animation.GlideAnimation
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
|
||||
abstract class SingleColorTarget(view: ImageView) : BitmapPaletteTarget(view) {
|
||||
|
||||
|
@ -30,23 +30,21 @@ abstract class SingleColorTarget(view: ImageView) : BitmapPaletteTarget(view) {
|
|||
|
||||
abstract fun onColorReady(color: Int)
|
||||
|
||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(e, errorDrawable)
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
onColorReady(defaultFooterColor)
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper?,
|
||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>?
|
||||
resource: BitmapPaletteWrapper,
|
||||
transition: Transition<in BitmapPaletteWrapper>?
|
||||
) {
|
||||
super.onResourceReady(resource, glideAnimation)
|
||||
resource?.let {
|
||||
onColorReady(
|
||||
ColorUtil.getColor(
|
||||
it.palette,
|
||||
ATHUtil.resolveColor(view.context, R.attr.colorPrimary)
|
||||
)
|
||||
super.onResourceReady(resource, transition)
|
||||
onColorReady(
|
||||
ColorUtil.getColor(
|
||||
resource.palette,
|
||||
ATHUtil.resolveColor(view.context, R.attr.colorPrimary)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package code.name.monkey.retromusic.glide;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import androidx.annotation.NonNull;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover;
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder;
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
import code.name.monkey.retromusic.util.MusicUtil;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
import com.bumptech.glide.BitmapRequestBuilder;
|
||||
import com.bumptech.glide.DrawableRequestBuilder;
|
||||
import com.bumptech.glide.DrawableTypeRequest;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
import com.bumptech.glide.load.Key;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
|
||||
import com.bumptech.glide.signature.MediaStoreSignature;
|
||||
|
||||
/** Created by hemanths on 2019-09-15. */
|
||||
public class SongGlideRequest {
|
||||
|
||||
private static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.NONE;
|
||||
private static final int DEFAULT_ERROR_IMAGE = R.drawable.default_audio_art;
|
||||
private static final int DEFAULT_ANIMATION = android.R.anim.fade_in;
|
||||
|
||||
@NonNull
|
||||
private static DrawableTypeRequest createBaseRequest(
|
||||
@NonNull RequestManager requestManager, @NonNull Song song, boolean ignoreMediaStore) {
|
||||
if (ignoreMediaStore) {
|
||||
return requestManager.load(new AudioFileCover(song.getData()));
|
||||
} else {
|
||||
return requestManager.loadFromMediaStore(
|
||||
MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(song.getAlbumId()));
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Key createSignature(@NonNull Song song) {
|
||||
return new MediaStoreSignature("", song.getDateModified(), 0);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
final RequestManager requestManager;
|
||||
final Song song;
|
||||
boolean ignoreMediaStore;
|
||||
|
||||
private Builder(@NonNull RequestManager requestManager, Song song) {
|
||||
this.requestManager = requestManager;
|
||||
this.song = song;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Builder from(@NonNull RequestManager requestManager, Song song) {
|
||||
return new Builder(requestManager, song);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public PaletteBuilder generatePalette(@NonNull Context context) {
|
||||
return new PaletteBuilder(this, context);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public BitmapBuilder asBitmap() {
|
||||
return new BitmapBuilder(this);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder checkIgnoreMediaStore(@NonNull Context context) {
|
||||
return ignoreMediaStore(PreferenceUtil.INSTANCE.isIgnoreMediaStoreArtwork());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Builder ignoreMediaStore(boolean ignoreMediaStore) {
|
||||
this.ignoreMediaStore = ignoreMediaStore;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public DrawableRequestBuilder<GlideDrawable> build() {
|
||||
//noinspection unchecked
|
||||
return createBaseRequest(requestManager, song, ignoreMediaStore)
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.signature(createSignature(song));
|
||||
}
|
||||
}
|
||||
|
||||
public static class BitmapBuilder {
|
||||
private final Builder builder;
|
||||
|
||||
BitmapBuilder(Builder builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public BitmapRequestBuilder<?, Bitmap> build() {
|
||||
//noinspection unchecked
|
||||
return createBaseRequest(builder.requestManager, builder.song, builder.ignoreMediaStore)
|
||||
.asBitmap()
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.signature(createSignature(builder.song));
|
||||
}
|
||||
}
|
||||
|
||||
public static class PaletteBuilder {
|
||||
final Context context;
|
||||
private final Builder builder;
|
||||
|
||||
PaletteBuilder(Builder builder, Context context) {
|
||||
this.builder = builder;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public BitmapRequestBuilder<?, BitmapPaletteWrapper> build() {
|
||||
//noinspection unchecked
|
||||
return createBaseRequest(builder.requestManager, builder.song, builder.ignoreMediaStore)
|
||||
.asBitmap()
|
||||
.transcode(new BitmapPaletteTranscoder(context), BitmapPaletteWrapper.class)
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.signature(createSignature(builder.song));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
package code.name.monkey.retromusic.glide;
|
||||
|
||||
import static code.name.monkey.retromusic.Constants.USER_PROFILE;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import androidx.annotation.NonNull;
|
||||
import code.name.monkey.appthemehelper.ThemeStore;
|
||||
import code.name.monkey.appthemehelper.util.TintHelper;
|
||||
import code.name.monkey.retromusic.App;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import com.bumptech.glide.BitmapRequestBuilder;
|
||||
import com.bumptech.glide.BitmapTypeRequest;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
import com.bumptech.glide.load.Key;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.signature.MediaStoreSignature;
|
||||
import java.io.File;
|
||||
|
||||
public class UserProfileGlideRequest {
|
||||
private static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.NONE;
|
||||
private static final int DEFAULT_ERROR_IMAGE = R.drawable.ic_account;
|
||||
private static final int DEFAULT_ANIMATION = android.R.anim.fade_in;
|
||||
|
||||
public static File getUserModel() {
|
||||
File dir = App.Companion.getContext().getFilesDir();
|
||||
return new File(dir, USER_PROFILE);
|
||||
}
|
||||
|
||||
private static BitmapTypeRequest<File> createBaseRequest(
|
||||
RequestManager requestManager, File profile) {
|
||||
return requestManager.load(profile).asBitmap();
|
||||
}
|
||||
|
||||
private static Key createSignature(File file) {
|
||||
return new MediaStoreSignature("", file.lastModified(), 0);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private RequestManager requestManager;
|
||||
private File profile;
|
||||
private Drawable error;
|
||||
|
||||
private Builder(RequestManager requestManager, File profile) {
|
||||
this.requestManager = requestManager;
|
||||
this.profile = profile;
|
||||
error =
|
||||
TintHelper.createTintedDrawable(
|
||||
App.Companion.getContext(),
|
||||
R.drawable.ic_account,
|
||||
ThemeStore.Companion.accentColor(App.Companion.getContext()));
|
||||
}
|
||||
|
||||
public static Builder from(@NonNull RequestManager requestManager, File profile) {
|
||||
return new Builder(requestManager, profile);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public BitmapRequestBuilder<File, Bitmap> build() {
|
||||
return createBaseRequest(requestManager, profile)
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(error)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.signature(createSignature(profile));
|
||||
}
|
||||
}
|
||||
|
||||
public static class BitmapBuilder {
|
||||
private final Builder builder;
|
||||
|
||||
BitmapBuilder(Builder builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
public BitmapRequestBuilder<?, Bitmap> build() {
|
||||
return createBaseRequest(builder.requestManager, builder.profile)
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(builder.error)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.signature(createSignature(builder.profile));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package code.name.monkey.retromusic.glide.artistimage
|
||||
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
|
||||
class ArtistImage(val artist: Artist){
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other is ArtistImage){
|
||||
return other.artist == artist
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return artist.hashCode()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
package code.name.monkey.retromusic.glide.artistimage
|
||||
|
||||
import android.content.Context
|
||||
import code.name.monkey.retromusic.model.Data
|
||||
import code.name.monkey.retromusic.model.DeezerResponse
|
||||
import code.name.monkey.retromusic.network.DeezerService
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import com.bumptech.glide.Priority
|
||||
import com.bumptech.glide.integration.okhttp3.OkHttpStreamFetcher
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.data.DataFetcher
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
|
||||
class ArtistImageFetcher(
|
||||
private val context: Context,
|
||||
private val deezerService: DeezerService,
|
||||
val model: ArtistImage,
|
||||
private val okhttp: OkHttpClient
|
||||
) : DataFetcher<InputStream> {
|
||||
|
||||
private var streamFetcher: OkHttpStreamFetcher? = null
|
||||
private var response: Call<DeezerResponse>? = null
|
||||
private var isCancelled: Boolean = false
|
||||
|
||||
override fun getDataClass(): Class<InputStream> {
|
||||
return InputStream::class.java
|
||||
}
|
||||
|
||||
override fun getDataSource(): DataSource {
|
||||
return DataSource.REMOTE
|
||||
}
|
||||
|
||||
override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
|
||||
try {
|
||||
if (!MusicUtil.isArtistNameUnknown(model.artist.name) &&
|
||||
PreferenceUtil.isAllowedToDownloadMetadata()
|
||||
) {
|
||||
val artists = model.artist.name.split(",", "&")
|
||||
response = deezerService.getArtistImage(artists[0])
|
||||
response?.enqueue(object : Callback<DeezerResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<DeezerResponse>,
|
||||
response: Response<DeezerResponse>
|
||||
) {
|
||||
if (!response.isSuccessful) {
|
||||
throw IOException("Request failed with code: " + response.code())
|
||||
}
|
||||
|
||||
if (isCancelled) {
|
||||
callback.onDataReady(null)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
val deezerResponse = response.body()
|
||||
val imageUrl =
|
||||
deezerResponse?.data?.get(0)?.let { getHighestQuality(it) }
|
||||
// Fragile way to detect a place holder image returned from Deezer:
|
||||
// ex: "https://e-cdns-images.dzcdn.net/images/artist//250x250-000000-80-0-0.jpg"
|
||||
// the double slash implies no artist identified
|
||||
val placeHolder = imageUrl?.contains("/images/artist//") ?: false
|
||||
if (!placeHolder) {
|
||||
streamFetcher = OkHttpStreamFetcher(okhttp, GlideUrl(imageUrl))
|
||||
streamFetcher?.loadData(priority, callback)
|
||||
} else {
|
||||
callback.onDataReady(getFallbackAlbumImage())
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
callback.onDataReady(getFallbackAlbumImage())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<DeezerResponse>, t: Throwable) {
|
||||
callback.onDataReady(getFallbackAlbumImage())
|
||||
}
|
||||
})
|
||||
} else callback.onDataReady(null)
|
||||
} catch (e: Exception) {
|
||||
callback.onLoadFailed(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFallbackAlbumImage(): InputStream? {
|
||||
val imageUri = MusicUtil.getMediaStoreAlbumCoverUri(model.artist.safeGetFirstAlbum().id)
|
||||
return try {
|
||||
context.contentResolver.openInputStream(imageUri)
|
||||
} catch (e: FileNotFoundException){
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getHighestQuality(imageUrl: Data): String {
|
||||
return when {
|
||||
imageUrl.pictureXl.isNotEmpty() -> imageUrl.pictureXl
|
||||
imageUrl.pictureBig.isNotEmpty() -> imageUrl.pictureBig
|
||||
imageUrl.pictureMedium.isNotEmpty() -> imageUrl.pictureMedium
|
||||
imageUrl.pictureSmall.isNotEmpty() -> imageUrl.pictureSmall
|
||||
imageUrl.picture.isNotEmpty() -> imageUrl.picture
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
override fun cleanup() {
|
||||
streamFetcher?.cleanup()
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
isCancelled = true
|
||||
response?.cancel()
|
||||
streamFetcher?.cancel()
|
||||
}
|
||||
}
|
|
@ -15,115 +15,39 @@
|
|||
package code.name.monkey.retromusic.glide.artistimage
|
||||
|
||||
import android.content.Context
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.model.Data
|
||||
import code.name.monkey.retromusic.network.DeezerService
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import com.bumptech.glide.Priority
|
||||
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
|
||||
import com.bumptech.glide.load.data.DataFetcher
|
||||
import com.bumptech.glide.load.model.GenericLoaderFactory
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
import com.bumptech.glide.load.Options
|
||||
import com.bumptech.glide.load.model.ModelLoader
|
||||
import com.bumptech.glide.load.model.ModelLoader.LoadData
|
||||
import com.bumptech.glide.load.model.ModelLoaderFactory
|
||||
import com.bumptech.glide.load.model.stream.StreamModelLoader
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.TimeUnit
|
||||
import com.bumptech.glide.load.model.MultiModelLoaderFactory
|
||||
import com.bumptech.glide.signature.ObjectKey
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
|
||||
class ArtistImage(val artist: Artist)
|
||||
|
||||
class ArtistImageFetcher(
|
||||
private val context: Context,
|
||||
private val deezerService: DeezerService,
|
||||
val model: ArtistImage,
|
||||
val urlLoader: ModelLoader<GlideUrl, InputStream>,
|
||||
val width: Int,
|
||||
val height: Int
|
||||
) : DataFetcher<InputStream> {
|
||||
|
||||
private var urlFetcher: DataFetcher<InputStream>? = null
|
||||
private var isCancelled: Boolean = false
|
||||
|
||||
override fun cleanup() {
|
||||
urlFetcher?.cleanup()
|
||||
}
|
||||
|
||||
override fun getId(): String {
|
||||
return model.artist.name
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
isCancelled = true
|
||||
urlFetcher?.cancel()
|
||||
}
|
||||
|
||||
override fun loadData(priority: Priority?): InputStream? {
|
||||
if (!MusicUtil.isArtistNameUnknown(model.artist.name) &&
|
||||
PreferenceUtil.isAllowedToDownloadMetadata()
|
||||
) {
|
||||
val artists = model.artist.name.split(",")
|
||||
val response = deezerService.getArtistImage(artists[0]).execute()
|
||||
|
||||
if (!response.isSuccessful) {
|
||||
throw IOException("Request failed with code: " + response.code())
|
||||
}
|
||||
|
||||
if (isCancelled) return null
|
||||
|
||||
return try {
|
||||
val deezerResponse = response.body()
|
||||
val imageUrl = deezerResponse?.data?.get(0)?.let { getHighestQuality(it) }
|
||||
// Fragile way to detect a place holder image returned from Deezer:
|
||||
// ex: "https://e-cdns-images.dzcdn.net/images/artist//250x250-000000-80-0-0.jpg"
|
||||
// the double slash implies no artist identified
|
||||
val placeHolder = imageUrl?.contains("/images/artist//") ?: false
|
||||
if (!placeHolder) {
|
||||
val glideUrl = GlideUrl(imageUrl)
|
||||
urlFetcher = urlLoader.getResourceFetcher(glideUrl, width, height)
|
||||
urlFetcher?.loadData(priority)
|
||||
} else {
|
||||
getFallbackAlbumImage()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
getFallbackAlbumImage()
|
||||
}
|
||||
} else return null
|
||||
}
|
||||
|
||||
private fun getFallbackAlbumImage(): InputStream? {
|
||||
val imageUri = MusicUtil.getMediaStoreAlbumCoverUri(model.artist.safeGetFirstAlbum().id)
|
||||
return context.contentResolver.openInputStream(imageUri)
|
||||
}
|
||||
|
||||
private fun getHighestQuality(imageUrl: Data): String {
|
||||
return when {
|
||||
imageUrl.pictureXl.isNotEmpty() -> imageUrl.pictureXl
|
||||
imageUrl.pictureBig.isNotEmpty() -> imageUrl.pictureBig
|
||||
imageUrl.pictureMedium.isNotEmpty() -> imageUrl.pictureMedium
|
||||
imageUrl.pictureSmall.isNotEmpty() -> imageUrl.pictureSmall
|
||||
imageUrl.picture.isNotEmpty() -> imageUrl.picture
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
}
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class ArtistImageLoader(
|
||||
val context: Context,
|
||||
private val deezerService: DeezerService,
|
||||
private val urlLoader: ModelLoader<GlideUrl, InputStream>
|
||||
) : StreamModelLoader<ArtistImage> {
|
||||
private val okhttp: OkHttpClient
|
||||
) : ModelLoader<ArtistImage, InputStream> {
|
||||
|
||||
override fun getResourceFetcher(
|
||||
override fun buildLoadData(
|
||||
model: ArtistImage,
|
||||
width: Int,
|
||||
height: Int
|
||||
): DataFetcher<InputStream> {
|
||||
return ArtistImageFetcher(context, deezerService, model, urlLoader, width, height)
|
||||
height: Int,
|
||||
options: Options
|
||||
): LoadData<InputStream> {
|
||||
return LoadData(
|
||||
ObjectKey(model.artist.name),
|
||||
ArtistImageFetcher(context, deezerService, model, okhttp)
|
||||
)
|
||||
}
|
||||
|
||||
override fun handles(model: ArtistImage): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,16 +56,15 @@ class Factory(
|
|||
) : ModelLoaderFactory<ArtistImage, InputStream> {
|
||||
|
||||
private var deezerService: DeezerService
|
||||
private var okHttpFactory: OkHttpUrlLoader.Factory
|
||||
private var okHttp: OkHttpClient
|
||||
|
||||
init {
|
||||
okHttpFactory = OkHttpUrlLoader.Factory(
|
||||
okHttp =
|
||||
OkHttpClient.Builder()
|
||||
.connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.readTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
)
|
||||
deezerService = DeezerService.invoke(
|
||||
DeezerService.createDefaultOkHttpClient(context)
|
||||
.connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
|
@ -158,23 +81,18 @@ class Factory(
|
|||
return interceptor
|
||||
}
|
||||
|
||||
override fun build(
|
||||
context: Context?,
|
||||
factories: GenericLoaderFactory?
|
||||
): ModelLoader<ArtistImage, InputStream> {
|
||||
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<ArtistImage, InputStream> {
|
||||
return ArtistImageLoader(
|
||||
context!!,
|
||||
context,
|
||||
deezerService,
|
||||
okHttpFactory.build(context, factories)
|
||||
okHttp
|
||||
)
|
||||
}
|
||||
|
||||
override fun teardown() {
|
||||
okHttpFactory.teardown()
|
||||
}
|
||||
override fun teardown() {}
|
||||
|
||||
companion object {
|
||||
// we need these very low values to make sure our artist image loading calls doesn't block the image loading queue
|
||||
private const val TIMEOUT: Long = 700
|
||||
private const val TIMEOUT: Long = 500
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
package code.name.monkey.retromusic.glide.audiocover;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/** @author Karim Abou Zeid (kabouzeid) */
|
||||
public class AudioFileCover {
|
||||
public final String filePath;
|
||||
|
@ -21,4 +23,17 @@ public class AudioFileCover {
|
|||
public AudioFileCover(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return filePath.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object object) {
|
||||
if (object instanceof AudioFileCover){
|
||||
return ((AudioFileCover) object).filePath.equals(filePath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,15 @@
|
|||
package code.name.monkey.retromusic.glide.audiocover;
|
||||
|
||||
import android.media.MediaMetadataRetriever;
|
||||
|
||||
import com.bumptech.glide.Priority;
|
||||
import com.bumptech.glide.load.DataSource;
|
||||
import com.bumptech.glide.load.data.DataFetcher;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
|
@ -27,19 +33,11 @@ public class AudioFileCoverFetcher implements DataFetcher<InputStream> {
|
|||
private InputStream stream;
|
||||
|
||||
public AudioFileCoverFetcher(AudioFileCover model) {
|
||||
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
// makes sure we never ever return null here
|
||||
return String.valueOf(model.filePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream loadData(final Priority priority) throws Exception {
|
||||
|
||||
public void loadData(@NotNull Priority priority, @NotNull DataCallback<? super InputStream> callback) {
|
||||
final MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
||||
try {
|
||||
retriever.setDataSource(model.filePath);
|
||||
|
@ -49,11 +47,12 @@ public class AudioFileCoverFetcher implements DataFetcher<InputStream> {
|
|||
} else {
|
||||
stream = AudioFileCoverUtils.fallback(model.filePath);
|
||||
}
|
||||
callback.onDataReady(stream);
|
||||
} catch (FileNotFoundException e) {
|
||||
callback.onLoadFailed(e);
|
||||
} finally {
|
||||
retriever.release();
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -72,4 +71,16 @@ public class AudioFileCoverFetcher implements DataFetcher<InputStream> {
|
|||
public void cancel() {
|
||||
// cannot cancel
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Class<InputStream> getDataClass() {
|
||||
return InputStream.class;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DataSource getDataSource() {
|
||||
return DataSource.LOCAL;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,29 +14,40 @@
|
|||
|
||||
package code.name.monkey.retromusic.glide.audiocover;
|
||||
|
||||
import android.content.Context;
|
||||
import com.bumptech.glide.load.data.DataFetcher;
|
||||
import com.bumptech.glide.load.model.GenericLoaderFactory;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.bumptech.glide.load.Options;
|
||||
import com.bumptech.glide.load.model.ModelLoader;
|
||||
import com.bumptech.glide.load.model.ModelLoaderFactory;
|
||||
import com.bumptech.glide.load.model.stream.StreamModelLoader;
|
||||
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
|
||||
import com.bumptech.glide.signature.ObjectKey;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
public class AudioFileCoverLoader implements StreamModelLoader<AudioFileCover> {
|
||||
public class AudioFileCoverLoader implements ModelLoader<AudioFileCover, InputStream> {
|
||||
|
||||
@Override
|
||||
public DataFetcher<InputStream> getResourceFetcher(AudioFileCover model, int width, int height) {
|
||||
return new AudioFileCoverFetcher(model);
|
||||
public LoadData<InputStream> buildLoadData(@NonNull @NotNull AudioFileCover audioFileCover, int width, int height, @NonNull @NotNull Options options) {
|
||||
return new LoadData<>(new ObjectKey(audioFileCover.filePath), new AudioFileCoverFetcher(audioFileCover));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handles(@NonNull @NotNull AudioFileCover audioFileCover) {
|
||||
return audioFileCover.filePath != null;
|
||||
}
|
||||
|
||||
public static class Factory implements ModelLoaderFactory<AudioFileCover, InputStream> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ModelLoader<AudioFileCover, InputStream> build(
|
||||
Context context, GenericLoaderFactory factories) {
|
||||
public ModelLoader<AudioFileCover, InputStream> build(@NonNull @NotNull MultiModelLoaderFactory multiFactory) {
|
||||
return new AudioFileCoverLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teardown() {}
|
||||
public void teardown() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,17 +14,18 @@
|
|||
|
||||
package code.name.monkey.retromusic.glide.audiocover;
|
||||
|
||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||
import org.jaudiotagger.audio.mp3.MP3File;
|
||||
import org.jaudiotagger.tag.TagException;
|
||||
import org.jaudiotagger.tag.images.Artwork;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||
import org.jaudiotagger.audio.mp3.MP3File;
|
||||
import org.jaudiotagger.tag.TagException;
|
||||
import org.jaudiotagger.tag.images.Artwork;
|
||||
|
||||
public class AudioFileCoverUtils {
|
||||
|
||||
|
@ -44,10 +45,7 @@ public class AudioFileCoverUtils {
|
|||
}
|
||||
}
|
||||
// If there are any exceptions, we ignore them and continue to the other fallback method
|
||||
} catch (ReadOnlyFileException ignored) {
|
||||
} catch (InvalidAudioFrameException ignored) {
|
||||
} catch (TagException ignored) {
|
||||
} catch (IOException ignored) {
|
||||
} catch (ReadOnlyFileException | InvalidAudioFrameException | TagException | IOException ignored) {
|
||||
}
|
||||
|
||||
// Method 2: look for album art in external files
|
||||
|
|
|
@ -14,25 +14,31 @@
|
|||
|
||||
package code.name.monkey.retromusic.glide.palette;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
|
||||
import com.bumptech.glide.util.Util;
|
||||
|
||||
public class BitmapPaletteResource implements Resource<BitmapPaletteWrapper> {
|
||||
|
||||
private final BitmapPaletteWrapper bitmapPaletteWrapper;
|
||||
private final BitmapPool bitmapPool;
|
||||
|
||||
public BitmapPaletteResource(BitmapPaletteWrapper bitmapPaletteWrapper, BitmapPool bitmapPool) {
|
||||
public BitmapPaletteResource(BitmapPaletteWrapper bitmapPaletteWrapper) {
|
||||
this.bitmapPaletteWrapper = bitmapPaletteWrapper;
|
||||
this.bitmapPool = bitmapPool;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public BitmapPaletteWrapper get() {
|
||||
return bitmapPaletteWrapper;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<BitmapPaletteWrapper> getResourceClass() {
|
||||
return BitmapPaletteWrapper.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return Util.getBitmapByteSize(bitmapPaletteWrapper.getBitmap());
|
||||
|
@ -40,8 +46,6 @@ public class BitmapPaletteResource implements Resource<BitmapPaletteWrapper> {
|
|||
|
||||
@Override
|
||||
public void recycle() {
|
||||
if (!bitmapPool.put(bitmapPaletteWrapper.getBitmap())) {
|
||||
bitmapPaletteWrapper.getBitmap().recycle();
|
||||
}
|
||||
bitmapPaletteWrapper.getBitmap().recycle();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
package code.name.monkey.retromusic.glide.palette;
|
||||
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.bumptech.glide.request.target.ImageViewTarget;
|
||||
|
||||
public class BitmapPaletteTarget extends ImageViewTarget<BitmapPaletteWrapper> {
|
||||
|
@ -24,6 +25,8 @@ public class BitmapPaletteTarget extends ImageViewTarget<BitmapPaletteWrapper> {
|
|||
|
||||
@Override
|
||||
protected void setResource(BitmapPaletteWrapper bitmapPaletteWrapper) {
|
||||
view.setImageBitmap(bitmapPaletteWrapper.getBitmap());
|
||||
if (bitmapPaletteWrapper != null) {
|
||||
view.setImageBitmap(bitmapPaletteWrapper.getBitmap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,35 +14,23 @@
|
|||
|
||||
package code.name.monkey.retromusic.glide.palette;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil;
|
||||
import com.bumptech.glide.Glide;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.bumptech.glide.load.Options;
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
|
||||
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
|
||||
|
||||
public class BitmapPaletteTranscoder implements ResourceTranscoder<Bitmap, BitmapPaletteWrapper> {
|
||||
private final BitmapPool bitmapPool;
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil;
|
||||
|
||||
public BitmapPaletteTranscoder(Context context) {
|
||||
this(Glide.get(context).getBitmapPool());
|
||||
}
|
||||
public class BitmapPaletteTranscoder implements ResourceTranscoder<Bitmap, BitmapPaletteWrapper> {
|
||||
|
||||
public BitmapPaletteTranscoder(BitmapPool bitmapPool) {
|
||||
this.bitmapPool = bitmapPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource<BitmapPaletteWrapper> transcode(Resource<Bitmap> bitmapResource) {
|
||||
Bitmap bitmap = bitmapResource.get();
|
||||
@Override
|
||||
public Resource<BitmapPaletteWrapper> transcode(@NonNull Resource<Bitmap> toTranscode, @NonNull Options options) {
|
||||
Bitmap bitmap = toTranscode.get();
|
||||
BitmapPaletteWrapper bitmapPaletteWrapper =
|
||||
new BitmapPaletteWrapper(bitmap, RetroColorUtil.generatePalette(bitmap));
|
||||
return new BitmapPaletteResource(bitmapPaletteWrapper, bitmapPool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "BitmapPaletteTranscoder.com.kabouzeid.gramophone.glide.palette";
|
||||
new BitmapPaletteWrapper(bitmap, RetroColorUtil.generatePalette(bitmap));
|
||||
return new BitmapPaletteResource(bitmapPaletteWrapper);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
package code.name.monkey.retromusic.glide.palette;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import androidx.palette.graphics.Palette;
|
||||
|
||||
public class BitmapPaletteWrapper {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package code.name.monkey.retromusic.glide.playlistPreview
|
||||
|
||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
||||
import code.name.monkey.retromusic.db.toSongs
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
|
||||
class PlaylistPreview(val playlistWithSongs: PlaylistWithSongs) {
|
||||
|
||||
val playlistEntity: PlaylistEntity get() = playlistWithSongs.playlistEntity
|
||||
val songs: List<Song> get() = playlistWithSongs.songs.toSongs()
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other is PlaylistPreview) {
|
||||
if (other.playlistEntity.playListId != playlistEntity.playListId) {
|
||||
return false
|
||||
}
|
||||
if (other.songs.size != songs.size) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = playlistEntity.playListId.hashCode()
|
||||
result = 31 * result + playlistWithSongs.songs.size
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package code.name.monkey.retromusic.glide.playlistPreview
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import code.name.monkey.retromusic.util.AutoGeneratedPlaylistBitmap
|
||||
import com.bumptech.glide.Priority
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.data.DataFetcher
|
||||
import kotlinx.coroutines.*
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
class PlaylistPreviewFetcher(val context: Context, private val playlistPreview: PlaylistPreview) :
|
||||
DataFetcher<Bitmap>, CoroutineScope by GlideScope() {
|
||||
override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in Bitmap>) {
|
||||
launch {
|
||||
try {
|
||||
val bitmap =
|
||||
AutoGeneratedPlaylistBitmap.getBitmap(
|
||||
context,
|
||||
playlistPreview.songs.shuffled(),
|
||||
true,
|
||||
false
|
||||
)
|
||||
callback.onDataReady(bitmap)
|
||||
} catch (e: Exception) {
|
||||
callback.onLoadFailed(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun cleanup() {}
|
||||
|
||||
override fun cancel() {
|
||||
cancel(null)
|
||||
}
|
||||
|
||||
override fun getDataClass(): Class<Bitmap> {
|
||||
return Bitmap::class.java
|
||||
}
|
||||
|
||||
override fun getDataSource(): DataSource {
|
||||
return DataSource.LOCAL
|
||||
}
|
||||
}
|
||||
|
||||
private val glideDispatcher: CoroutineDispatcher by lazy {
|
||||
Executors.newFixedThreadPool(4).asCoroutineDispatcher()
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
internal fun GlideScope(): CoroutineScope = CoroutineScope(SupervisorJob() + glideDispatcher)
|
|
@ -0,0 +1,36 @@
|
|||
package code.name.monkey.retromusic.glide.playlistPreview
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import com.bumptech.glide.load.Options
|
||||
import com.bumptech.glide.load.model.ModelLoader
|
||||
import com.bumptech.glide.load.model.ModelLoader.LoadData
|
||||
import com.bumptech.glide.load.model.ModelLoaderFactory
|
||||
import com.bumptech.glide.load.model.MultiModelLoaderFactory
|
||||
import com.bumptech.glide.signature.ObjectKey
|
||||
|
||||
class PlaylistPreviewLoader(val context: Context) : ModelLoader<PlaylistPreview, Bitmap> {
|
||||
override fun buildLoadData(
|
||||
model: PlaylistPreview,
|
||||
width: Int,
|
||||
height: Int,
|
||||
options: Options
|
||||
): LoadData<Bitmap> {
|
||||
return LoadData(
|
||||
ObjectKey(model),
|
||||
PlaylistPreviewFetcher(context, model)
|
||||
)
|
||||
}
|
||||
|
||||
override fun handles(model: PlaylistPreview): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
class Factory(val context: Context) : ModelLoaderFactory<PlaylistPreview, Bitmap> {
|
||||
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<PlaylistPreview, Bitmap> {
|
||||
return PlaylistPreviewLoader(context)
|
||||
}
|
||||
|
||||
override fun teardown() {}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue