Removed butter knife
This commit is contained in:
parent
d5f63b91ac
commit
63e3276098
194 changed files with 5984 additions and 7491 deletions
|
@ -1,141 +0,0 @@
|
|||
package code.name.monkey.retromusic.glide;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
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;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.App;
|
||||
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;
|
||||
|
||||
public class ArtistGlideRequest {
|
||||
|
||||
private static final int DEFAULT_ANIMATION = android.R.anim.fade_in;
|
||||
private static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.ALL;
|
||||
private static final int DEFAULT_ERROR_IMAGE = R.drawable.default_artist_art;
|
||||
|
||||
private static DrawableTypeRequest createBaseRequest(RequestManager requestManager, Artist artist,
|
||||
boolean noCustomImage, boolean forceDownload) {
|
||||
boolean hasCustomImage = CustomArtistImageUtil.getInstance(App.Companion.getInstance())
|
||||
.hasCustomArtistImage(artist);
|
||||
if (noCustomImage || !hasCustomImage) {
|
||||
return requestManager.load(new ArtistImage(artist.getName(), forceDownload));
|
||||
} else {
|
||||
return requestManager.load(CustomArtistImageUtil.getFile(artist));
|
||||
}
|
||||
}
|
||||
|
||||
private static Key createSignature(Artist artist) {
|
||||
return ArtistSignatureUtil.getInstance(App.Companion.getInstance())
|
||||
.getArtistSignature(artist.getName());
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
final RequestManager requestManager;
|
||||
final Artist artist;
|
||||
boolean noCustomImage;
|
||||
boolean forceDownload;
|
||||
|
||||
private Builder(@NonNull RequestManager requestManager, Artist artist) {
|
||||
this.requestManager = requestManager;
|
||||
this.artist = artist;
|
||||
}
|
||||
|
||||
public static Builder from(@NonNull RequestManager requestManager, Artist artist) {
|
||||
return new Builder(requestManager, artist);
|
||||
}
|
||||
|
||||
public PaletteBuilder generatePalette(Context context) {
|
||||
return new PaletteBuilder(this, context);
|
||||
}
|
||||
|
||||
public BitmapBuilder asBitmap() {
|
||||
return new BitmapBuilder(this);
|
||||
}
|
||||
|
||||
public Builder noCustomImage(boolean noCustomImage) {
|
||||
this.noCustomImage = noCustomImage;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder forceDownload(boolean forceDownload) {
|
||||
this.forceDownload = forceDownload;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DrawableRequestBuilder<GlideDrawable> build() {
|
||||
//noinspection unchecked
|
||||
return createBaseRequest(requestManager, artist, noCustomImage, forceDownload)
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.priority(Priority.LOW)
|
||||
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
|
||||
.signature(createSignature(artist));
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.priority(Priority.LOW)
|
||||
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
|
||||
.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)
|
||||
.signature(createSignature(builder.artist));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package code.name.monkey.retromusic.glide
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
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.*
|
||||
import com.bumptech.glide.load.Key
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.target.Target
|
||||
|
||||
object ArtistGlideRequest {
|
||||
|
||||
private const val DEFAULT_ANIMATION = android.R.anim.fade_in
|
||||
private val DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.ALL
|
||||
private const val DEFAULT_ERROR_IMAGE = R.drawable.default_artist_art
|
||||
|
||||
private fun createBaseRequest(requestManager: RequestManager, artist: Artist, noCustomImage: Boolean, forceDownload: Boolean): DrawableTypeRequest<*> {
|
||||
val hasCustomImage = CustomArtistImageUtil.getInstance(App.instance)
|
||||
.hasCustomArtistImage(artist)
|
||||
return if (noCustomImage || !hasCustomImage) {
|
||||
requestManager.load(ArtistImage(artist.name, forceDownload))
|
||||
} else {
|
||||
requestManager.load(CustomArtistImageUtil.getFile(artist))
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSignature(artist: Artist): Key {
|
||||
return ArtistSignatureUtil.getInstance(App.instance)
|
||||
.getArtistSignature(artist.name)
|
||||
}
|
||||
|
||||
class Builder private constructor(internal val requestManager: RequestManager, internal val artist: Artist) {
|
||||
internal var noCustomImage: Boolean = false
|
||||
internal var forceDownload: Boolean = false
|
||||
|
||||
fun generatePalette(context: Context): PaletteBuilder {
|
||||
return PaletteBuilder(this, context)
|
||||
}
|
||||
|
||||
fun asBitmap(): BitmapBuilder {
|
||||
return BitmapBuilder(this)
|
||||
}
|
||||
|
||||
fun noCustomImage(noCustomImage: Boolean): Builder {
|
||||
this.noCustomImage = noCustomImage
|
||||
return this
|
||||
}
|
||||
|
||||
fun forceDownload(forceDownload: Boolean): Builder {
|
||||
this.forceDownload = forceDownload
|
||||
return this
|
||||
}
|
||||
|
||||
fun build(): DrawableRequestBuilder<out Any> {
|
||||
return createBaseRequest(requestManager, artist, noCustomImage, forceDownload)
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.priority(Priority.LOW)
|
||||
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
|
||||
.signature(createSignature(artist))
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun from(requestManager: RequestManager, artist: Artist): Builder {
|
||||
return Builder(requestManager, artist)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BitmapBuilder internal constructor(private val builder: Builder) {
|
||||
|
||||
fun build(): BitmapRequestBuilder<*, Bitmap> {
|
||||
|
||||
return createBaseRequest(builder.requestManager, builder.artist, builder.noCustomImage,
|
||||
builder.forceDownload)
|
||||
.asBitmap()
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.priority(Priority.LOW)
|
||||
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
|
||||
.signature(createSignature(builder.artist))
|
||||
}
|
||||
}
|
||||
|
||||
class PaletteBuilder internal constructor(private val builder: Builder, internal val context: Context) {
|
||||
|
||||
fun build(): BitmapRequestBuilder<*, BitmapPaletteWrapper> {
|
||||
|
||||
return createBaseRequest(builder.requestManager, builder.artist, builder.noCustomImage,
|
||||
builder.forceDownload)
|
||||
.asBitmap()
|
||||
.transcode(BitmapPaletteTranscoder(context), BitmapPaletteWrapper::class.java)
|
||||
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.priority(Priority.LOW)
|
||||
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
|
||||
.signature(createSignature(builder.artist))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
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.Allocation;
|
||||
import android.renderscript.Element;
|
||||
import android.renderscript.RSRuntimeException;
|
||||
import android.renderscript.RenderScript;
|
||||
import android.renderscript.ScriptIntrinsicBlur;
|
||||
import androidx.annotation.FloatRange;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
|
||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
|
||||
import code.name.monkey.retromusic.helper.StackBlur;
|
||||
|
||||
import code.name.monkey.retromusic.BuildConfig;
|
||||
import code.name.monkey.retromusic.util.ImageUtil;
|
||||
|
||||
|
||||
public class BlurTransformation extends BitmapTransformation {
|
||||
static final float DEFAULT_BLUR_RADIUS = 5f;
|
||||
|
||||
private Context context;
|
||||
private float blurRadius;
|
||||
private int sampling;
|
||||
|
||||
private BlurTransformation(Builder builder) {
|
||||
super(builder.context);
|
||||
init(builder);
|
||||
}
|
||||
|
||||
private BlurTransformation(Builder builder, BitmapPool bitmapPool) {
|
||||
super(bitmapPool);
|
||||
init(builder);
|
||||
}
|
||||
|
||||
private void init(Builder builder) {
|
||||
this.context = builder.context;
|
||||
this.blurRadius = builder.blurRadius;
|
||||
this.sampling = builder.sampling;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
|
||||
int sampling;
|
||||
if (this.sampling == 0) {
|
||||
sampling = ImageUtil.calculateInSampleSize(toTransform.getWidth(), toTransform.getHeight(), 100);
|
||||
} else {
|
||||
sampling = this.sampling;
|
||||
}
|
||||
|
||||
int width = toTransform.getWidth();
|
||||
int height = toTransform.getHeight();
|
||||
int scaledWidth = width / sampling;
|
||||
int scaledHeight = height / sampling;
|
||||
|
||||
Bitmap out = pool.get(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
|
||||
if (out == null) {
|
||||
out = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
|
||||
Canvas canvas = new Canvas(out);
|
||||
canvas.scale(1 / (float) sampling, 1 / (float) sampling);
|
||||
Paint paint = new Paint();
|
||||
paint.setFlags(Paint.FILTER_BITMAP_FLAG);
|
||||
canvas.drawBitmap(toTransform, 0, 0, paint);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 17) {
|
||||
try {
|
||||
final RenderScript rs = RenderScript.create(context.getApplicationContext());
|
||||
final Allocation input = Allocation.createFromBitmap(rs, out, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
|
||||
final Allocation output = Allocation.createTyped(rs, input.getType());
|
||||
final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
|
||||
|
||||
script.setRadius(blurRadius);
|
||||
script.setInput(input);
|
||||
script.forEach(output);
|
||||
|
||||
output.copyTo(out);
|
||||
|
||||
rs.destroy();
|
||||
|
||||
return out;
|
||||
|
||||
} catch (RSRuntimeException e) {
|
||||
// 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
|
||||
public String getId() {
|
||||
return "BlurTransformation(radius=" + blurRadius + ", sampling=" + sampling + ")";
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Context context;
|
||||
private BitmapPool bitmapPool;
|
||||
private float blurRadius = DEFAULT_BLUR_RADIUS;
|
||||
private int sampling;
|
||||
|
||||
public Builder(@NonNull Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param blurRadius The radius to use. Must be between 0 and 25. Default is 5.
|
||||
* @return the same Builder
|
||||
*/
|
||||
public Builder blurRadius(@FloatRange(from = 0.0f, to = 25.0f) float blurRadius) {
|
||||
this.blurRadius = blurRadius;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sampling The inSampleSize to use. Must be a power of 2, or 1 for no down sampling or 0 for auto detect sampling. Default is 0.
|
||||
* @return the same Builder
|
||||
*/
|
||||
public Builder sampling(int sampling) {
|
||||
this.sampling = sampling;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bitmapPool The BitmapPool to use.
|
||||
* @return the same Builder
|
||||
*/
|
||||
public Builder bitmapPool(BitmapPool bitmapPool) {
|
||||
this.bitmapPool = bitmapPool;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlurTransformation build() {
|
||||
if (bitmapPool != null) {
|
||||
return new BlurTransformation(this, bitmapPool);
|
||||
}
|
||||
return new BlurTransformation(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
package code.name.monkey.retromusic.glide
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.os.Build
|
||||
import android.renderscript.Allocation
|
||||
import android.renderscript.Element
|
||||
import android.renderscript.RSRuntimeException
|
||||
import android.renderscript.RenderScript
|
||||
import android.renderscript.ScriptIntrinsicBlur
|
||||
import androidx.annotation.FloatRange
|
||||
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
|
||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
||||
import code.name.monkey.retromusic.helper.StackBlur
|
||||
|
||||
import code.name.monkey.retromusic.BuildConfig
|
||||
import code.name.monkey.retromusic.util.ImageUtil
|
||||
|
||||
|
||||
class BlurTransformation : BitmapTransformation {
|
||||
|
||||
private var context: Context? = null
|
||||
private var blurRadius: Float = 0.toFloat()
|
||||
private var sampling: Int = 0
|
||||
|
||||
private constructor(builder: Builder) : super(builder.context) {
|
||||
init(builder)
|
||||
}
|
||||
|
||||
private constructor(builder: Builder, bitmapPool: BitmapPool) : super(bitmapPool) {
|
||||
init(builder)
|
||||
}
|
||||
|
||||
private fun init(builder: Builder) {
|
||||
this.context = builder.context
|
||||
this.blurRadius = builder.blurRadius
|
||||
this.sampling = builder.sampling
|
||||
}
|
||||
|
||||
|
||||
override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap? {
|
||||
val sampling: Int
|
||||
if (this.sampling == 0) {
|
||||
sampling = ImageUtil.calculateInSampleSize(toTransform.width, toTransform.height, 100)
|
||||
} else {
|
||||
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!!)
|
||||
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))
|
||||
|
||||
script.setRadius(blurRadius)
|
||||
script.setInput(input)
|
||||
script.forEach(output)
|
||||
|
||||
output.copyTo(out)
|
||||
|
||||
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 StackBlur.blur(out, blurRadius)
|
||||
}
|
||||
|
||||
override fun getId(): String {
|
||||
return "BlurTransformation(radius=$blurRadius, sampling=$sampling)"
|
||||
}
|
||||
|
||||
class Builder(internal val context: Context) {
|
||||
private var bitmapPool: BitmapPool? = null
|
||||
internal var blurRadius = DEFAULT_BLUR_RADIUS
|
||||
internal var sampling: Int = 0
|
||||
|
||||
/**
|
||||
* @param blurRadius The radius to use. Must be between 0 and 25. Default is 5.
|
||||
* @return the same Builder
|
||||
*/
|
||||
fun blurRadius(@FloatRange(from = 0.0, to = 25.0) blurRadius: Float): Builder {
|
||||
this.blurRadius = blurRadius
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sampling The inSampleSize to use. Must be a power of 2, or 1 for no down sampling or 0 for auto detect sampling. Default is 0.
|
||||
* @return the same Builder
|
||||
*/
|
||||
fun sampling(sampling: Int): Builder {
|
||||
this.sampling = sampling
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bitmapPool The BitmapPool to use.
|
||||
* @return the same Builder
|
||||
*/
|
||||
fun bitmapPool(bitmapPool: BitmapPool): Builder {
|
||||
this.bitmapPool = bitmapPool
|
||||
return this
|
||||
}
|
||||
|
||||
fun build(): BlurTransformation {
|
||||
return if (bitmapPool != null) {
|
||||
BlurTransformation(this, bitmapPool!!)
|
||||
} else BlurTransformation(this)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal const val DEFAULT_BLUR_RADIUS = 5f
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package code.name.monkey.retromusic.glide;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.GlideBuilder;
|
||||
import com.bumptech.glide.module.GlideModule;
|
||||
import code.name.monkey.retromusic.glide.artistimage.ArtistImage;
|
||||
import code.name.monkey.retromusic.glide.artistimage.ArtistImageLoader;
|
||||
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover;
|
||||
import code.name.monkey.retromusic.glide.audiocover.AudioFileCoverLoader;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
|
||||
public class RetroMusicGlideModule implements GlideModule {
|
||||
@Override
|
||||
public void applyOptions(Context context, GlideBuilder builder) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerComponents(Context context, Glide glide) {
|
||||
glide.register(AudioFileCover.class, InputStream.class, new AudioFileCoverLoader.Factory());
|
||||
glide.register(ArtistImage.class, InputStream.class, new ArtistImageLoader.Factory(context));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package code.name.monkey.retromusic.glide
|
||||
|
||||
import android.content.Context
|
||||
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.GlideBuilder
|
||||
import com.bumptech.glide.module.GlideModule
|
||||
import code.name.monkey.retromusic.glide.artistimage.ArtistImage
|
||||
import code.name.monkey.retromusic.glide.artistimage.ArtistImageLoader
|
||||
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover
|
||||
import code.name.monkey.retromusic.glide.audiocover.AudioFileCoverLoader
|
||||
|
||||
import java.io.InputStream
|
||||
|
||||
|
||||
class RetroMusicGlideModule : GlideModule {
|
||||
override fun applyOptions(context: Context, builder: GlideBuilder) {
|
||||
|
||||
}
|
||||
|
||||
override fun registerComponents(context: Context, glide: Glide) {
|
||||
glide.register(AudioFileCover::class.java, InputStream::class.java, AudioFileCoverLoader.Factory())
|
||||
glide.register(ArtistImage::class.java, InputStream::class.java, ArtistImageLoader.Factory(context))
|
||||
}
|
||||
}
|
|
@ -1,124 +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 SongGlideRequest {
|
||||
|
||||
static final DiskCacheStrategy DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.NONE;
|
||||
static final int DEFAULT_ANIMATION = android.R.anim.fade_in;
|
||||
static final int DEFAULT_ERROR_IMAGE = R.drawable.default_album_art;
|
||||
|
||||
static DrawableTypeRequest createBaseRequest(RequestManager requestManager, Song song,
|
||||
boolean ignoreMediaStore) {
|
||||
if (ignoreMediaStore) {
|
||||
return requestManager.load(new AudioFileCover(song.getData()));
|
||||
} else {
|
||||
return requestManager.loadFromMediaStore(MusicUtil.getMediaStoreAlbumCoverUri(song.getAlbumId()));
|
||||
}
|
||||
}
|
||||
|
||||
static Key createSignature(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;
|
||||
}
|
||||
|
||||
public static Builder from(@NonNull RequestManager requestManager, Song song) {
|
||||
return new Builder(requestManager, song);
|
||||
}
|
||||
|
||||
public PaletteBuilder generatePalette(Context context) {
|
||||
return new PaletteBuilder(this, context);
|
||||
}
|
||||
|
||||
public BitmapBuilder asBitmap() {
|
||||
return new BitmapBuilder(this);
|
||||
}
|
||||
|
||||
public Builder checkIgnoreMediaStore(Context context) {
|
||||
return ignoreMediaStore(PreferenceUtil.getInstance().ignoreMediaStoreArtwork());
|
||||
}
|
||||
|
||||
Builder ignoreMediaStore(boolean ignoreMediaStore) {
|
||||
this.ignoreMediaStore = ignoreMediaStore;
|
||||
return this;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package code.name.monkey.retromusic.glide
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
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.signature.MediaStoreSignature
|
||||
|
||||
|
||||
object SongGlideRequest {
|
||||
|
||||
internal val DEFAULT_DISK_CACHE_STRATEGY = DiskCacheStrategy.NONE
|
||||
internal const val DEFAULT_ANIMATION = android.R.anim.fade_in
|
||||
internal const val DEFAULT_ERROR_IMAGE = R.drawable.default_album_art
|
||||
|
||||
internal fun createBaseRequest(requestManager: RequestManager, song: Song,
|
||||
ignoreMediaStore: Boolean): DrawableTypeRequest<*> {
|
||||
return if (ignoreMediaStore) {
|
||||
requestManager.load(AudioFileCover(song.data!!))
|
||||
} else {
|
||||
requestManager.loadFromMediaStore(MusicUtil.getMediaStoreAlbumCoverUri(song.albumId))
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createSignature(song: Song): Key {
|
||||
return MediaStoreSignature("", song.dateModified, 0)
|
||||
}
|
||||
|
||||
class Builder private constructor(internal val requestManager: RequestManager, internal val song: Song) {
|
||||
internal var ignoreMediaStore: Boolean = false
|
||||
|
||||
fun generatePalette(context: Context): PaletteBuilder {
|
||||
return PaletteBuilder(this, context)
|
||||
}
|
||||
|
||||
fun asBitmap(): BitmapBuilder {
|
||||
return BitmapBuilder(this)
|
||||
}
|
||||
|
||||
fun checkIgnoreMediaStore(context: Context): Builder {
|
||||
return ignoreMediaStore(PreferenceUtil.getInstance().ignoreMediaStoreArtwork())
|
||||
}
|
||||
|
||||
fun ignoreMediaStore(ignoreMediaStore: Boolean): Builder {
|
||||
this.ignoreMediaStore = ignoreMediaStore
|
||||
return this
|
||||
}
|
||||
|
||||
fun build(): DrawableRequestBuilder<out Any> {
|
||||
return createBaseRequest(requestManager, song, ignoreMediaStore)
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.signature(createSignature(song))
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun from(requestManager: RequestManager, song: Song): Builder {
|
||||
return Builder(requestManager, song)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BitmapBuilder internal constructor(private val builder: Builder) {
|
||||
|
||||
fun build(): BitmapRequestBuilder<*, Bitmap> {
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
class PaletteBuilder internal constructor(private val builder: Builder, internal val context: Context) {
|
||||
|
||||
fun build(): BitmapRequestBuilder<*, BitmapPaletteWrapper> {
|
||||
|
||||
return createBaseRequest(builder.requestManager, builder.song, builder.ignoreMediaStore)
|
||||
.asBitmap()
|
||||
.transcode(BitmapPaletteTranscoder(context), BitmapPaletteWrapper::class.java)
|
||||
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
|
||||
.error(DEFAULT_ERROR_IMAGE)
|
||||
.animate(DEFAULT_ANIMATION)
|
||||
.signature(createSignature(builder.song))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package code.name.monkey.retromusic.glide.artistimage;
|
||||
|
||||
|
||||
public class ArtistImage {
|
||||
public final String artistName;
|
||||
public final boolean skipOkHttpCache;
|
||||
|
||||
public ArtistImage(String artistName, boolean skipOkHttpCache) {
|
||||
this.artistName = artistName;
|
||||
this.skipOkHttpCache = skipOkHttpCache;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package code.name.monkey.retromusic.glide.artistimage
|
||||
|
||||
|
||||
class ArtistImage(val artistName: String, val skipOkHttpCache: Boolean)
|
|
@ -1,83 +0,0 @@
|
|||
package code.name.monkey.retromusic.glide.artistimage;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.bumptech.glide.Priority;
|
||||
import com.bumptech.glide.load.data.DataFetcher;
|
||||
import com.bumptech.glide.load.model.GlideUrl;
|
||||
import com.bumptech.glide.load.model.ModelLoader;
|
||||
import code.name.monkey.retromusic.rest.LastFMRestClient;
|
||||
import code.name.monkey.retromusic.rest.model.LastFmArtist;
|
||||
|
||||
import code.name.monkey.retromusic.util.LastFMUtil;
|
||||
import code.name.monkey.retromusic.util.MusicUtil;
|
||||
import code.name.monkey.retromusic.util.RetroUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import retrofit2.Response;
|
||||
|
||||
|
||||
public class ArtistImageFetcher implements DataFetcher<InputStream> {
|
||||
public static final String TAG = ArtistImageFetcher.class.getSimpleName();
|
||||
private final LastFMRestClient lastFMRestClient;
|
||||
private final ArtistImage model;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private Context context;
|
||||
private ModelLoader<GlideUrl, InputStream> urlLoader;
|
||||
private volatile boolean isCancelled;
|
||||
private DataFetcher<InputStream> urlFetcher;
|
||||
|
||||
public ArtistImageFetcher(Context context, LastFMRestClient lastFMRestClient, ArtistImage model, ModelLoader<GlideUrl, InputStream> urlLoader, int width, int height) {
|
||||
this.context = context;
|
||||
this.lastFMRestClient = lastFMRestClient;
|
||||
this.model = model;
|
||||
this.urlLoader = urlLoader;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
// makes sure we never ever return null here
|
||||
return String.valueOf(model.artistName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream loadData(Priority priority) throws Exception {
|
||||
if (!MusicUtil.isArtistNameUnknown(model.artistName) && RetroUtil.isAllowedToDownloadMetadata(context)) {
|
||||
Response<LastFmArtist> response = lastFMRestClient.getApiService().getArtistInfo(model.artistName, null, model.skipOkHttpCache ? "no-cache" : null).execute();
|
||||
|
||||
if (!response.isSuccessful()) {
|
||||
throw new IOException("Request failed with code: " + response.code());
|
||||
}
|
||||
|
||||
LastFmArtist lastFmArtist = response.body();
|
||||
|
||||
if (isCancelled) return null;
|
||||
|
||||
GlideUrl url = new GlideUrl(LastFMUtil.getLargestArtistImageUrl(lastFmArtist.getArtist().getImage()));
|
||||
urlFetcher = urlLoader.getResourceFetcher(url, width, height);
|
||||
|
||||
return urlFetcher.loadData(priority);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
if (urlFetcher != null) {
|
||||
urlFetcher.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
isCancelled = true;
|
||||
if (urlFetcher != null) {
|
||||
urlFetcher.cancel();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package code.name.monkey.retromusic.glide.artistimage
|
||||
|
||||
import android.content.Context
|
||||
|
||||
import com.bumptech.glide.Priority
|
||||
import com.bumptech.glide.load.data.DataFetcher
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
import com.bumptech.glide.load.model.ModelLoader
|
||||
import code.name.monkey.retromusic.rest.LastFMRestClient
|
||||
import code.name.monkey.retromusic.rest.model.LastFmArtist
|
||||
|
||||
import code.name.monkey.retromusic.util.LastFMUtil
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
import retrofit2.Response
|
||||
|
||||
|
||||
class ArtistImageFetcher(private val context: Context, private val lastFMRestClient: LastFMRestClient, private val model: ArtistImage, private val urlLoader: ModelLoader<GlideUrl, InputStream>, private val width: Int, private val height: Int) : DataFetcher<InputStream> {
|
||||
@Volatile
|
||||
private var isCancelled: Boolean = false
|
||||
private var urlFetcher: DataFetcher<InputStream>? = null
|
||||
|
||||
override fun getId(): String {
|
||||
// makes sure we never ever return null here
|
||||
return model.artistName
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
override fun loadData(priority: Priority): InputStream? {
|
||||
if (!MusicUtil.isArtistNameUnknown(model.artistName) && RetroUtil.isAllowedToDownloadMetadata(context)) {
|
||||
val response = lastFMRestClient.apiService.getArtistInfo(model.artistName, null, if (model.skipOkHttpCache) "no-cache" else null).execute()
|
||||
|
||||
if (!response.isSuccessful) {
|
||||
throw IOException("Request failed with code: " + response.code())
|
||||
}
|
||||
|
||||
val lastFmArtist = response.body()
|
||||
|
||||
if (isCancelled) return null
|
||||
|
||||
val url = GlideUrl(LastFMUtil.getLargestArtistImageUrl(lastFmArtist!!.artist.image))
|
||||
urlFetcher = urlLoader.getResourceFetcher(url, width, height)
|
||||
|
||||
return urlFetcher!!.loadData(priority)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun cleanup() {
|
||||
if (urlFetcher != null) {
|
||||
urlFetcher!!.cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
isCancelled = true
|
||||
if (urlFetcher != null) {
|
||||
urlFetcher!!.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG = ArtistImageFetcher::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
package code.name.monkey.retromusic.glide.artistimage;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
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.model.ModelLoader;
|
||||
import com.bumptech.glide.load.model.ModelLoaderFactory;
|
||||
import com.bumptech.glide.load.model.stream.StreamModelLoader;
|
||||
import code.name.monkey.retromusic.rest.LastFMRestClient;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
|
||||
|
||||
public class ArtistImageLoader implements StreamModelLoader<ArtistImage> {
|
||||
// we need these very low values to make sure our artist image loading calls doesn't block the image loading queue
|
||||
private static final int TIMEOUT = 500;
|
||||
|
||||
private Context context;
|
||||
private LastFMRestClient lastFMClient;
|
||||
private ModelLoader<GlideUrl, InputStream> urlLoader;
|
||||
|
||||
public ArtistImageLoader(Context context, LastFMRestClient lastFMRestClient, ModelLoader<GlideUrl, InputStream> urlLoader) {
|
||||
this.context = context;
|
||||
this.lastFMClient = lastFMRestClient;
|
||||
this.urlLoader = urlLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFetcher<InputStream> getResourceFetcher(ArtistImage model, int width, int height) {
|
||||
return new ArtistImageFetcher(context, lastFMClient, model, urlLoader, width, height);
|
||||
}
|
||||
|
||||
public static class Factory implements ModelLoaderFactory<ArtistImage, InputStream> {
|
||||
private LastFMRestClient lastFMClient;
|
||||
private OkHttpUrlLoader.Factory okHttpFactory;
|
||||
|
||||
public Factory(Context context) {
|
||||
okHttpFactory = new OkHttpUrlLoader.Factory(new OkHttpClient.Builder()
|
||||
.connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.readTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.build());
|
||||
lastFMClient = new LastFMRestClient(LastFMRestClient.createDefaultOkHttpClientBuilder(context)
|
||||
.connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.readTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelLoader<ArtistImage, InputStream> build(Context context, GenericLoaderFactory factories) {
|
||||
return new ArtistImageLoader(context, lastFMClient, okHttpFactory.build(context, factories));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teardown() {
|
||||
okHttpFactory.teardown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package code.name.monkey.retromusic.glide.artistimage
|
||||
|
||||
import android.content.Context
|
||||
|
||||
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.model.ModelLoader
|
||||
import com.bumptech.glide.load.model.ModelLoaderFactory
|
||||
import com.bumptech.glide.load.model.stream.StreamModelLoader
|
||||
import code.name.monkey.retromusic.rest.LastFMRestClient
|
||||
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
|
||||
class ArtistImageLoader(private val context: Context, private val lastFMClient: LastFMRestClient, private val urlLoader: ModelLoader<GlideUrl, InputStream>) : StreamModelLoader<ArtistImage> {
|
||||
|
||||
override fun getResourceFetcher(model: ArtistImage, width: Int, height: Int): DataFetcher<InputStream> {
|
||||
return ArtistImageFetcher(context, lastFMClient, model, urlLoader, width, height)
|
||||
}
|
||||
|
||||
class Factory(context: Context) : ModelLoaderFactory<ArtistImage, InputStream> {
|
||||
private val lastFMClient: LastFMRestClient = LastFMRestClient(LastFMRestClient.createDefaultOkHttpClientBuilder(context)
|
||||
.connectTimeout(TIMEOUT.toLong(), TimeUnit.MILLISECONDS)
|
||||
.readTimeout(TIMEOUT.toLong(), TimeUnit.MILLISECONDS)
|
||||
.writeTimeout(TIMEOUT.toLong(), TimeUnit.MILLISECONDS)
|
||||
.build())
|
||||
private val okHttpFactory: OkHttpUrlLoader.Factory = OkHttpUrlLoader.Factory(OkHttpClient.Builder()
|
||||
.connectTimeout(TIMEOUT.toLong(), TimeUnit.MILLISECONDS)
|
||||
.readTimeout(TIMEOUT.toLong(), TimeUnit.MILLISECONDS)
|
||||
.writeTimeout(TIMEOUT.toLong(), TimeUnit.MILLISECONDS)
|
||||
.build())
|
||||
|
||||
override fun build(context: Context, factories: GenericLoaderFactory): ModelLoader<ArtistImage, InputStream> {
|
||||
return ArtistImageLoader(context, lastFMClient, okHttpFactory.build(context, factories))
|
||||
}
|
||||
|
||||
override fun teardown() {
|
||||
okHttpFactory.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 = 500
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package code.name.monkey.retromusic.glide.audiocover;
|
||||
|
||||
|
||||
public class AudioFileCover {
|
||||
public final String filePath;
|
||||
|
||||
public AudioFileCover(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package code.name.monkey.retromusic.glide.audiocover
|
||||
|
||||
|
||||
class AudioFileCover(val filePath: String)
|
|
@ -1,95 +0,0 @@
|
|||
package code.name.monkey.retromusic.glide.audiocover;
|
||||
|
||||
import android.media.MediaMetadataRetriever;
|
||||
|
||||
import com.bumptech.glide.Priority;
|
||||
import com.bumptech.glide.load.data.DataFetcher;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
public class AudioFileCoverFetcher implements DataFetcher<InputStream> {
|
||||
private static final String[] FALLBACKS = {"cover.jpg", "album.jpg", "folder.jpg", "cover.png", "album.png", "folder.png"};
|
||||
private final AudioFileCover model;
|
||||
private FileInputStream 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(Priority priority) throws Exception {
|
||||
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
||||
try {
|
||||
retriever.setDataSource(model.filePath);
|
||||
byte[] picture = retriever.getEmbeddedPicture();
|
||||
if (picture != null) {
|
||||
return new ByteArrayInputStream(picture);
|
||||
} else {
|
||||
return fallback(model.filePath);
|
||||
}
|
||||
} finally {
|
||||
retriever.release();
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream fallback(String path) throws FileNotFoundException {
|
||||
// Method 1: use embedded high resolution album art if there is any
|
||||
try {
|
||||
MP3File mp3File = new MP3File(path);
|
||||
if (mp3File.hasID3v2Tag()) {
|
||||
Artwork art = mp3File.getTag().getFirstArtwork();
|
||||
if (art != null) {
|
||||
byte[] imageData = art.getBinaryData();
|
||||
return new ByteArrayInputStream(imageData);
|
||||
}
|
||||
}
|
||||
// If there are any exceptions, we ignore them and continue to the other fallback method
|
||||
} catch (ReadOnlyFileException | InvalidAudioFrameException | TagException | IOException ignored) {
|
||||
}
|
||||
|
||||
// Method 2: look for album art in external files
|
||||
File parent = new File(path).getParentFile();
|
||||
for (String fallback : FALLBACKS) {
|
||||
File cover = new File(parent, fallback);
|
||||
if (cover.exists()) {
|
||||
return stream = new FileInputStream(cover);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
// already cleaned up in loadData and ByteArrayInputStream will be GC'd
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException ignore) {
|
||||
// can't do much about it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
// cannot cancel
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package code.name.monkey.retromusic.glide.audiocover
|
||||
|
||||
import android.media.MediaMetadataRetriever
|
||||
import com.bumptech.glide.Priority
|
||||
import com.bumptech.glide.load.data.DataFetcher
|
||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
|
||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException
|
||||
import org.jaudiotagger.audio.mp3.MP3File
|
||||
import org.jaudiotagger.tag.TagException
|
||||
import java.io.*
|
||||
|
||||
|
||||
class AudioFileCoverFetcher(private val model: AudioFileCover) : DataFetcher<InputStream> {
|
||||
private var stream: FileInputStream? = null
|
||||
|
||||
override fun getId(): String {
|
||||
// makes sure we never ever return null here
|
||||
return model.filePath
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
override fun loadData(priority: Priority): InputStream? {
|
||||
val retriever = MediaMetadataRetriever()
|
||||
try {
|
||||
retriever.setDataSource(model.filePath)
|
||||
val picture = retriever.embeddedPicture
|
||||
return if (picture != null) {
|
||||
ByteArrayInputStream(picture)
|
||||
} else {
|
||||
fallback(model.filePath)
|
||||
}
|
||||
} finally {
|
||||
retriever.release()
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(FileNotFoundException::class)
|
||||
private fun fallback(path: String): InputStream? {
|
||||
// Method 1: use embedded high resolution album art if there is any
|
||||
try {
|
||||
val mp3File = MP3File(path)
|
||||
if (mp3File.hasID3v2Tag()) {
|
||||
val art = mp3File.tag.firstArtwork
|
||||
if (art != null) {
|
||||
val imageData = art.binaryData
|
||||
return ByteArrayInputStream(imageData)
|
||||
}
|
||||
}
|
||||
// If there are any exceptions, we ignore them and continue to the other fallback method
|
||||
} catch (ignored: ReadOnlyFileException) {
|
||||
} catch (ignored: InvalidAudioFrameException) {
|
||||
} catch (ignored: TagException) {
|
||||
} catch (ignored: IOException) {
|
||||
}
|
||||
|
||||
// Method 2: look for album art in external files
|
||||
val parent = File(path).parentFile
|
||||
for (fallback in FALLBACKS) {
|
||||
val cover = File(parent, fallback)
|
||||
if (cover.exists()) {
|
||||
stream = FileInputStream(cover)
|
||||
return stream
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun cleanup() {
|
||||
// already cleaned up in loadData and ByteArrayInputStream will be GC'd
|
||||
if (stream != null) {
|
||||
try {
|
||||
stream!!.close()
|
||||
} catch (ignore: IOException) {
|
||||
// can't do much about it
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
// cannot cancel
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val FALLBACKS = arrayOf("cover.jpg", "album.jpg", "folder.jpg", "cover.png", "album.png", "folder.png")
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
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 com.bumptech.glide.load.model.ModelLoader;
|
||||
import com.bumptech.glide.load.model.ModelLoaderFactory;
|
||||
import com.bumptech.glide.load.model.stream.StreamModelLoader;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
|
||||
public class AudioFileCoverLoader implements StreamModelLoader<AudioFileCover> {
|
||||
|
||||
@Override
|
||||
public DataFetcher<InputStream> getResourceFetcher(AudioFileCover model, int width, int height) {
|
||||
return new AudioFileCoverFetcher(model);
|
||||
}
|
||||
|
||||
public static class Factory implements ModelLoaderFactory<AudioFileCover, InputStream> {
|
||||
@Override
|
||||
public ModelLoader<AudioFileCover, InputStream> build(Context context, GenericLoaderFactory factories) {
|
||||
return new AudioFileCoverLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teardown() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
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 com.bumptech.glide.load.model.ModelLoader
|
||||
import com.bumptech.glide.load.model.ModelLoaderFactory
|
||||
import com.bumptech.glide.load.model.stream.StreamModelLoader
|
||||
|
||||
import java.io.InputStream
|
||||
|
||||
|
||||
class AudioFileCoverLoader : StreamModelLoader<AudioFileCover> {
|
||||
|
||||
override fun getResourceFetcher(model: AudioFileCover, width: Int, height: Int): DataFetcher<InputStream> {
|
||||
return AudioFileCoverFetcher(model)
|
||||
}
|
||||
|
||||
class Factory : ModelLoaderFactory<AudioFileCover, InputStream> {
|
||||
override fun build(context: Context, factories: GenericLoaderFactory): ModelLoader<AudioFileCover, InputStream> {
|
||||
return AudioFileCoverLoader()
|
||||
}
|
||||
|
||||
override fun teardown() {}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package code.name.monkey.retromusic.glide.palette;
|
||||
|
||||
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) {
|
||||
this.bitmapPaletteWrapper = bitmapPaletteWrapper;
|
||||
this.bitmapPool = bitmapPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BitmapPaletteWrapper get() {
|
||||
return bitmapPaletteWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return Util.getBitmapByteSize(bitmapPaletteWrapper.getBitmap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle() {
|
||||
if (!bitmapPool.put(bitmapPaletteWrapper.getBitmap())) {
|
||||
bitmapPaletteWrapper.getBitmap().recycle();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package code.name.monkey.retromusic.glide.palette
|
||||
|
||||
import com.bumptech.glide.load.engine.Resource
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
|
||||
import com.bumptech.glide.util.Util
|
||||
|
||||
|
||||
class BitmapPaletteResource(private val bitmapPaletteWrapper: BitmapPaletteWrapper, private val bitmapPool: BitmapPool) : Resource<BitmapPaletteWrapper> {
|
||||
|
||||
override fun get(): BitmapPaletteWrapper {
|
||||
return bitmapPaletteWrapper
|
||||
}
|
||||
|
||||
override fun getSize(): Int {
|
||||
return Util.getBitmapByteSize(bitmapPaletteWrapper.bitmap)
|
||||
}
|
||||
|
||||
override fun recycle() {
|
||||
if (!bitmapPool.put(bitmapPaletteWrapper.bitmap)) {
|
||||
bitmapPaletteWrapper.bitmap.recycle()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package code.name.monkey.retromusic.glide.palette;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
|
||||
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil;
|
||||
|
||||
public class BitmapPaletteTranscoder implements ResourceTranscoder<Bitmap, BitmapPaletteWrapper> {
|
||||
private final BitmapPool bitmapPool;
|
||||
|
||||
public BitmapPaletteTranscoder(Context context) {
|
||||
this(Glide.get(context).getBitmapPool());
|
||||
}
|
||||
|
||||
public BitmapPaletteTranscoder(BitmapPool bitmapPool) {
|
||||
this.bitmapPool = bitmapPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource<BitmapPaletteWrapper> transcode(Resource<Bitmap> bitmapResource) {
|
||||
Bitmap bitmap = bitmapResource.get();
|
||||
BitmapPaletteWrapper bitmapPaletteWrapper = new BitmapPaletteWrapper(bitmap, RetroColorUtil.generatePalette(bitmap));
|
||||
return new BitmapPaletteResource(bitmapPaletteWrapper, bitmapPool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "BitmapPaletteTranscoder.code.name.monkey.retromusic.glide.palette";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package code.name.monkey.retromusic.glide.palette
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.Resource
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
|
||||
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
|
||||
class BitmapPaletteTranscoder(private val bitmapPool: BitmapPool) : ResourceTranscoder<Bitmap, BitmapPaletteWrapper> {
|
||||
|
||||
constructor(context: Context) : this(Glide.get(context).bitmapPool) {}
|
||||
|
||||
override fun transcode(bitmapResource: Resource<Bitmap>): Resource<BitmapPaletteWrapper> {
|
||||
val bitmap = bitmapResource.get()
|
||||
val bitmapPaletteWrapper = BitmapPaletteWrapper(bitmap, RetroColorUtil.generatePalette(bitmap)!!)
|
||||
return BitmapPaletteResource(bitmapPaletteWrapper, bitmapPool)
|
||||
}
|
||||
|
||||
override fun getId(): String {
|
||||
return "BitmapPaletteTranscoder.code.name.monkey.retromusic.glide.palette"
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package code.name.monkey.retromusic.glide.palette;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import androidx.palette.graphics.Palette;
|
||||
|
||||
public class BitmapPaletteWrapper {
|
||||
private final Bitmap mBitmap;
|
||||
private final Palette mPalette;
|
||||
|
||||
public BitmapPaletteWrapper(Bitmap bitmap, Palette palette) {
|
||||
mBitmap = bitmap;
|
||||
mPalette = palette;
|
||||
}
|
||||
|
||||
public Bitmap getBitmap() {
|
||||
return mBitmap;
|
||||
}
|
||||
|
||||
public Palette getPalette() {
|
||||
return mPalette;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package code.name.monkey.retromusic.glide.palette
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.palette.graphics.Palette
|
||||
|
||||
class BitmapPaletteWrapper(val bitmap: Bitmap, val palette: Palette)
|
Loading…
Add table
Add a link
Reference in a new issue