converted to Glide 4

This commit is contained in:
h4h13 2018-12-13 02:29:07 +05:30
parent 96aa205405
commit f4c56c8484
45 changed files with 853 additions and 945 deletions

View file

@ -1,112 +0,0 @@
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))
}
}
}

View file

@ -1,37 +1,30 @@
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 android.renderscript.*
import androidx.annotation.FloatRange
import code.name.monkey.retromusic.BuildConfig
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 code.name.monkey.retromusic.helper.StackBlur
import code.name.monkey.retromusic.BuildConfig
import code.name.monkey.retromusic.util.ImageUtil
import java.security.MessageDigest
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) {
private constructor(builder: Builder) : super() {
init(builder)
}
private constructor(builder: Builder, bitmapPool: BitmapPool) : super(bitmapPool) {
private constructor(builder: Builder, bitmapPool: BitmapPool) : super() {
init(builder)
}
@ -41,7 +34,6 @@ class BlurTransformation : BitmapTransformation {
this.sampling = builder.sampling
}
override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap? {
val sampling: Int
if (this.sampling == 0) {
@ -93,14 +85,22 @@ class BlurTransformation : BitmapTransformation {
return StackBlur.blur(out, blurRadius)
}
override fun getId(): String {
return "BlurTransformation(radius=$blurRadius, sampling=$sampling)"
override fun equals(o: Any?): Boolean {
return o is BlurTransformation
}
class Builder(internal val context: Context) {
private var bitmapPool: BitmapPool? = null
internal var blurRadius = DEFAULT_BLUR_RADIUS
internal var sampling: Int = 0
override fun hashCode(): Int {
return ID.hashCode()
}
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
messageDigest.update("BlurTransformation(radius=$blurRadius, sampling=$sampling)".toByteArray(CHARSET))
}
class Builder(val context: Context) {
var bitmapPool: BitmapPool? = null
var blurRadius = DEFAULT_BLUR_RADIUS
var sampling: Int = 0
/**
* @param blurRadius The radius to use. Must be between 0 and 25. Default is 5.
@ -137,6 +137,8 @@ class BlurTransformation : BitmapTransformation {
}
companion object {
internal const val DEFAULT_BLUR_RADIUS = 5f
val DEFAULT_BLUR_RADIUS = 5f
private val ID = "com.poupa.vinylmusicplayer.glide.BlurTransformation"
}
}
}

View file

@ -0,0 +1,96 @@
package code.name.monkey.retromusic.glide;
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.RequestOptions;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.signature.MediaStoreSignature;
import androidx.annotation.NonNull;
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.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;
import code.name.monkey.retromusic.util.MusicUtil;
import code.name.monkey.retromusic.util.PreferenceUtil;
@GlideExtension
public final class RetroGlideExtension {
private RetroGlideExtension() {
}
@GlideType(BitmapPaletteWrapper.class)
public static void asBitmapPalette(RequestBuilder<BitmapPaletteWrapper> requestBuilder) {
}
@GlideOption
public static RequestOptions artistOptions(@NonNull RequestOptions requestOptions, Artist artist) {
return requestOptions
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.error(R.drawable.default_artist_art)
.placeholder(R.drawable.default_artist_art)
.priority(Priority.LOW)
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.signature(createSignature(artist));
}
@GlideOption
public static RequestOptions songOptions(@NonNull RequestOptions requestOptions, Song song) {
return requestOptions
.diskCacheStrategy(DiskCacheStrategy.NONE)
.error(R.drawable.default_album_art)
.placeholder(R.drawable.default_album_art)
.signature(createSignature(song));
}
public static Key createSignature(Artist artist) {
return ArtistSignatureUtil.getInstance().getArtistSignature(artist.getName());
}
public static Key createSignature(Song song) {
return new MediaStoreSignature("", song.getDateModified(), 0);
}
public static Object getArtistModel(Artist artist) {
return getArtistModel(artist, CustomArtistImageUtil.Companion.getInstance(App.Companion.getContext()).hasCustomArtistImage(artist), false);
}
public static Object getArtistModel(Artist artist, boolean forceDownload) {
return getArtistModel(artist, CustomArtistImageUtil.Companion.getInstance(App.Companion.getContext()).hasCustomArtistImage(artist), forceDownload);
}
public static Object getArtistModel(Artist artist, boolean hasCustomImage, boolean forceDownload) {
if (!hasCustomImage) {
return new ArtistImage(artist.getName(), forceDownload);
} else {
return CustomArtistImageUtil.getFile(artist);
}
}
public static Object getSongModel(Song song) {
return getSongModel(song, PreferenceUtil.getInstance().ignoreMediaStoreArtwork());
}
public static Object getSongModel(Song song, boolean ignoreMediaStore) {
if (ignoreMediaStore) {
return new AudioFileCover(song.getData());
} else {
return MusicUtil.getMediaStoreAlbumCoverUri(song.getAlbumId());
}
}
public static <TranscodeType> GenericTransitionOptions<TranscodeType> getDefaultTransition() {
return new GenericTransitionOptions<TranscodeType>().transition(android.R.anim.fade_in);
}
}

View file

@ -2,17 +2,14 @@ package code.name.monkey.retromusic.glide
import android.graphics.drawable.Drawable
import android.widget.ImageView
import com.bumptech.glide.request.animation.GlideAnimation
import code.name.monkey.appthemehelper.util.ATHUtil
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.PreferenceUtil
import code.name.monkey.retromusic.util.RetroColorUtil.getColor
import code.name.monkey.retromusic.util.RetroColorUtil.getDominantColor
import com.bumptech.glide.request.transition.Transition
abstract class RetroMusicColoredTarget(view: ImageView) : BitmapPaletteTarget(view) {
@ -23,13 +20,13 @@ abstract class RetroMusicColoredTarget(view: ImageView) : BitmapPaletteTarget(vi
protected val albumArtistFooterColor: Int
get() = ATHUtil.resolveColor(getView().context, R.attr.cardBackgroundColor)
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>?) {
glideAnimation: Transition<in BitmapPaletteWrapper>?) {
super.onResourceReady(resource, glideAnimation)
val defaultColor = defaultFooterColor

View file

@ -1,25 +1,29 @@
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 android.graphics.Bitmap
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 code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import com.bumptech.glide.Glide
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) {
@GlideModule
class RetroMusicGlideModule : AppGlideModule() {
override fun registerComponents(context: Context, glide: Glide,
registry: Registry) {
registry.append(AudioFileCover::class.java, InputStream::class.java, AudioFileCoverLoader.Factory())
registry.append(ArtistImage::class.java, InputStream::class.java, ArtistImageLoader.Factory(context))
registry.register(Bitmap::class.java, BitmapPaletteWrapper::class.java, BitmapPaletteTranscoder())
}
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))
override fun isManifestParsingEnabled(): Boolean {
return false
}
}

View file

@ -0,0 +1,64 @@
package code.name.monkey.retromusic.glide
import android.graphics.drawable.Drawable
import com.bumptech.glide.request.Request
import com.bumptech.glide.request.target.SizeReadyCallback
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.transition.Transition
import com.bumptech.glide.util.Util
open class RetroSimpleTarget<T> @JvmOverloads constructor(private val width: Int = Target.SIZE_ORIGINAL, private val height: Int = Target.SIZE_ORIGINAL) : Target<T> {
private var request: Request? = null
override fun getRequest(): Request? {
return request
}
override fun setRequest(request: Request?) {
this.request = request
}
override fun onLoadStarted(placeholder: Drawable?) {
}
override fun onLoadFailed(errorDrawable: Drawable?) {
}
override fun onResourceReady(resource: T, transition: Transition<in T>?) {
}
override fun onLoadCleared(placeholder: Drawable?) {
}
override fun getSize(cb: SizeReadyCallback) {
if (!Util.isValidDimensions(width, height)) {
throw IllegalArgumentException(
"Width and height must both be > 0 or Target#SIZE_ORIGINAL, but given" + " width: "
+ width + " and height: " + height + ", either provide dimensions in the constructor"
+ " or call override()")
}
cb.onSizeReady(width, height)
}
override fun removeCallback(cb: SizeReadyCallback) {
}
override fun onStart() {
}
override fun onStop() {
}
override fun onDestroy() {
}
}

View file

@ -1,102 +0,0 @@
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))
}
}
}

View file

@ -1,69 +1,98 @@
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 android.text.TextUtils
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 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 java.io.IOException
import okhttp3.OkHttpClient
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
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> {
class ArtistImageFetcher(private val context: Context, private val lastFMRestClient: LastFMRestClient, private val okHttp: OkHttpClient, private val model: ArtistImage, width: Int, height: Int) : DataFetcher<InputStream> {
@Volatile
private var isCancelled: Boolean = false
private var urlFetcher: DataFetcher<InputStream>? = null
private var call: Call<LastFmArtist>? = null
private var streamFetcher: OkHttpStreamFetcher? = null
override fun getId(): String {
// makes sure we never ever return null here
return model.artistName
override fun getDataClass(): Class<InputStream> {
return InputStream::class.java
}
@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())
override fun getDataSource(): DataSource {
return DataSource.REMOTE
}
override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
try {
if (!MusicUtil.isArtistNameUnknown(model.artistName) && RetroUtil.isAllowedToDownloadMetadata(context)) {
call = lastFMRestClient.apiService.getArtistInfo(model.artistName, null, if (model.skipOkHttpCache) "no-cache" else null)
call!!.enqueue(object : Callback<LastFmArtist> {
override fun onResponse(call: Call<LastFmArtist>, response: Response<LastFmArtist>) {
if (isCancelled) {
callback.onDataReady(null)
return
}
val lastFmArtist = response.body()
if (lastFmArtist == null || lastFmArtist.artist == null || lastFmArtist.artist.image == null) {
callback.onLoadFailed(Exception("No artist image url found"))
return
}
val url = LastFMUtil.getLargestArtistImageUrl(lastFmArtist.artist.image)
if (TextUtils.isEmpty(url) || TextUtils.isEmpty(url.trim { it <= ' ' })) {
callback.onLoadFailed(Exception("No artist image url found"))
return
}
streamFetcher = OkHttpStreamFetcher(okHttp, GlideUrl(url))
streamFetcher!!.loadData(priority, callback)
}
override fun onFailure(call: Call<LastFmArtist>, throwable: Throwable) {
callback.onLoadFailed(Exception(throwable))
}
})
}
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)
} catch (e: Exception) {
callback.onLoadFailed(e)
}
return null
}
override fun cleanup() {
if (urlFetcher != null) {
urlFetcher!!.cleanup()
if (streamFetcher != null) {
streamFetcher!!.cleanup()
}
}
override fun cancel() {
isCancelled = true
if (urlFetcher != null) {
urlFetcher!!.cancel()
if (call != null) {
call!!.cancel()
}
if (streamFetcher != null) {
streamFetcher!!.cancel()
}
}
companion object {
val TAG = ArtistImageFetcher::class.java.simpleName
val TAG: String = ArtistImageFetcher::class.java.simpleName
}
}
}

View file

@ -1,52 +1,49 @@
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 code.name.monkey.retromusic.rest.LastFMRestClient
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 code.name.monkey.retromusic.rest.LastFMRestClient
import com.bumptech.glide.load.model.MultiModelLoaderFactory
import com.bumptech.glide.signature.ObjectKey
import okhttp3.OkHttpClient
import java.io.InputStream
import java.util.concurrent.TimeUnit
import okhttp3.OkHttpClient
class ArtistImageLoader(private val context: Context, private val lastFMClient: LastFMRestClient, private val okhttp: OkHttpClient) : ModelLoader<ArtistImage, InputStream> {
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)
override fun buildLoadData(model: ArtistImage, width: Int, height: Int, options: Options): ModelLoader.LoadData<InputStream>? {
return ModelLoader.LoadData(ObjectKey(model.artistName), ArtistImageFetcher(context, lastFMClient, okhttp, model, width, height))
}
class Factory(context: Context) : ModelLoaderFactory<ArtistImage, InputStream> {
override fun handles(model: ArtistImage): Boolean {
return true
}
class Factory(private val 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()
private val okHttp: OkHttpClient = OkHttpClient.Builder()
.connectTimeout(TIMEOUT.toLong(), TimeUnit.MILLISECONDS)
.readTimeout(TIMEOUT.toLong(), TimeUnit.MILLISECONDS)
.writeTimeout(TIMEOUT.toLong(), TimeUnit.MILLISECONDS)
.build())
.build()
override fun build(context: Context, factories: GenericLoaderFactory): ModelLoader<ArtistImage, InputStream> {
return ArtistImageLoader(context, lastFMClient, okHttpFactory.build(context, factories))
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<ArtistImage, InputStream> {
return ArtistImageLoader(context, lastFMClient, 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 = 500
}
}
}

View file

@ -2,41 +2,48 @@ 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.jaudiotagger.audio.exceptions.InvalidAudioFrameException
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException
import org.jaudiotagger.audio.mp3.MP3File
import org.jaudiotagger.tag.TagException
import java.io.*
/**
* @author Karim Abou Zeid (kabouzeid)
*/
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? {
override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
val retriever = MediaMetadataRetriever()
val data: InputStream?
try {
retriever.setDataSource(model.filePath)
val picture = retriever.embeddedPicture
return if (picture != null) {
ByteArrayInputStream(picture)
if (picture != null) {
data = ByteArrayInputStream(picture)
} else {
fallback(model.filePath)
data = fallback(model.filePath)
}
callback.onDataReady(data)
} catch (e: FileNotFoundException) {
callback.onLoadFailed(e)
} finally {
retriever.release()
}
}
override fun getDataClass(): Class<InputStream> {
return InputStream::class.java
}
override fun getDataSource(): DataSource {
return DataSource.LOCAL
}
@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()) {
@ -47,10 +54,7 @@ class AudioFileCoverFetcher(private val model: AudioFileCover) : DataFetcher<Inp
}
}
// 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) {
} catch (ignored: Exception) {
}
// Method 2: look for album art in external files
@ -82,6 +86,7 @@ class AudioFileCoverFetcher(private val model: AudioFileCover) : DataFetcher<Inp
}
companion object {
private val FALLBACKS = arrayOf("cover.jpg", "album.jpg", "folder.jpg", "cover.png", "album.png", "folder.png")
}
}

View file

@ -1,24 +1,27 @@
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.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 com.bumptech.glide.load.model.MultiModelLoaderFactory
import com.bumptech.glide.signature.ObjectKey
import java.io.InputStream
class AudioFileCoverLoader : StreamModelLoader<AudioFileCover> {
override fun getResourceFetcher(model: AudioFileCover, width: Int, height: Int): DataFetcher<InputStream> {
return AudioFileCoverFetcher(model)
class AudioFileCoverLoader : ModelLoader<AudioFileCover, InputStream> {
override fun buildLoadData(model: AudioFileCover, width: Int, height: Int,
options: Options): LoadData<InputStream>? {
return LoadData(ObjectKey(model.filePath), AudioFileCoverFetcher(model))
}
override fun handles(model: AudioFileCover): Boolean {
return true
}
class Factory : ModelLoaderFactory<AudioFileCover, InputStream> {
override fun build(context: Context, factories: GenericLoaderFactory): ModelLoader<AudioFileCover, InputStream> {
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<AudioFileCover, InputStream> {
return AudioFileCoverLoader()
}

View file

@ -1,23 +1,24 @@
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> {
class BitmapPaletteResource(private val bitmapPaletteWrapper: BitmapPaletteWrapper) : Resource<BitmapPaletteWrapper> {
override fun get(): BitmapPaletteWrapper {
return bitmapPaletteWrapper
}
override fun getResourceClass(): Class<BitmapPaletteWrapper> {
return BitmapPaletteWrapper::class.java
}
override fun getSize(): Int {
return Util.getBitmapByteSize(bitmapPaletteWrapper.bitmap)
}
override fun recycle() {
if (!bitmapPool.put(bitmapPaletteWrapper.bitmap)) {
bitmapPaletteWrapper.bitmap.recycle()
}
bitmapPaletteWrapper.bitmap.recycle()
}
}
}

View file

@ -1,12 +1,14 @@
package code.name.monkey.retromusic.glide.palette
import android.widget.ImageView
import com.bumptech.glide.request.target.ImageViewTarget
open class BitmapPaletteTarget(view: ImageView) : ImageViewTarget<BitmapPaletteWrapper>(view) {
override fun setResource(bitmapPaletteWrapper: BitmapPaletteWrapper) {
view.setImageBitmap(bitmapPaletteWrapper.bitmap)
override fun setResource(bitmapPaletteWrapper: BitmapPaletteWrapper?) {
if (bitmapPaletteWrapper != null) {
view.setImageBitmap(bitmapPaletteWrapper.bitmap)
}
}
}
}

View file

@ -1,25 +1,16 @@
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
import com.bumptech.glide.load.Options
import com.bumptech.glide.load.engine.Resource
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder
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> {
class BitmapPaletteTranscoder : ResourceTranscoder<Bitmap, BitmapPaletteWrapper> {
override fun transcode(bitmapResource: Resource<Bitmap>, options: Options): 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"
return BitmapPaletteResource(bitmapPaletteWrapper)
}
}