Refactor code

main
h4h13 2020-02-05 21:14:40 +05:30
parent 016c7f6218
commit 67f4b4ecf2
4 changed files with 125 additions and 108 deletions

View File

@ -11,7 +11,7 @@ import retrofit2.create
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Query import retrofit2.http.Query
import java.io.File import java.io.File
import java.util.* import java.util.Locale
private const val BASE_QUERY_ARTIST = "search/artist" private const val BASE_QUERY_ARTIST = "search/artist"
private const val BASE_URL = "https://api.deezer.com/" private const val BASE_URL = "https://api.deezer.com/"
@ -20,30 +20,30 @@ interface DeezerApiService {
@GET("$BASE_QUERY_ARTIST&limit=1") @GET("$BASE_QUERY_ARTIST&limit=1")
fun getArtistImage( fun getArtistImage(
@Query("q") artistName: String @Query("q") artistName: String
): Call<DeezerResponse> ): Call<DeezerResponse>
companion object { companion object {
operator fun invoke( operator fun invoke(
client: okhttp3.Call.Factory client: okhttp3.Call.Factory
): DeezerApiService { ): DeezerApiService {
return Retrofit.Builder() return Retrofit.Builder()
.baseUrl(BASE_URL) .baseUrl(BASE_URL)
.callFactory(client) .callFactory(client)
.addConverterFactory(GsonConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create())
.build() .build()
.create() .create()
} }
fun createDefaultOkHttpClient( fun createDefaultOkHttpClient(
context: Context context: Context
): OkHttpClient.Builder = ): OkHttpClient.Builder =
OkHttpClient.Builder() OkHttpClient.Builder()
.cache(createDefaultCache(context)) .cache(createDefaultCache(context))
.addInterceptor(createCacheControlInterceptor()) .addInterceptor(createCacheControlInterceptor())
private fun createDefaultCache( private fun createDefaultCache(
context: Context context: Context
): Cache? { ): Cache? {
val cacheDir = File(context.applicationContext.cacheDir.absolutePath, "/okhttp-deezer/") val cacheDir = File(context.applicationContext.cacheDir.absolutePath, "/okhttp-deezer/")
if (cacheDir.mkdir() or cacheDir.isDirectory) { if (cacheDir.mkdir() or cacheDir.isDirectory) {
@ -55,12 +55,13 @@ interface DeezerApiService {
private fun createCacheControlInterceptor(): Interceptor { private fun createCacheControlInterceptor(): Interceptor {
return Interceptor { chain -> return Interceptor { chain ->
val modifiedRequest = chain.request().newBuilder() val modifiedRequest = chain.request().newBuilder()
.addHeader("Cache-Control", .addHeader(
String.format( "Cache-Control",
Locale.getDefault(), String.format(
"max-age=31536000, max-stale=31536000" Locale.getDefault(),
) "max-age=31536000, max-stale=31536000"
).build() )
).build()
chain.proceed(modifiedRequest) chain.proceed(modifiedRequest)
} }
} }

View File

@ -3,29 +3,29 @@ package code.name.monkey.retromusic.deezer
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
data class Data( data class Data(
val id: String, val id: String,
val link: String, val link: String,
val name: String, val name: String,
@SerializedName("nb_album") @SerializedName("nb_album")
val nbAlbum: Int, val nbAlbum: Int,
@SerializedName("nb_fan") @SerializedName("nb_fan")
val nbFan: Int, val nbFan: Int,
val picture: String, val picture: String,
@SerializedName("picture_big") @SerializedName("picture_big")
val pictureBig: String, val pictureBig: String,
@SerializedName("picture_medium") @SerializedName("picture_medium")
val pictureMedium: String, val pictureMedium: String,
@SerializedName("picture_small") @SerializedName("picture_small")
val pictureSmall: String, val pictureSmall: String,
@SerializedName("picture_xl") @SerializedName("picture_xl")
val pictureXl: String, val pictureXl: String,
val radio: Boolean, val radio: Boolean,
val tracklist: String, val tracklist: String,
val type: String val type: String
) )
data class DeezerResponse( data class DeezerResponse(
val data: List<Data>, val data: List<Data>,
val next: String, val next: String,
val total: Int val total: Int
) )

View File

@ -16,9 +16,15 @@ package code.name.monkey.retromusic.glide;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import androidx.annotation.NonNull; 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.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.BitmapRequestBuilder;
import com.bumptech.glide.DrawableRequestBuilder; import com.bumptech.glide.DrawableRequestBuilder;
import com.bumptech.glide.DrawableTypeRequest; import com.bumptech.glide.DrawableTypeRequest;
@ -29,68 +35,32 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.drawable.GlideDrawable; import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.target.Target; import com.bumptech.glide.request.target.Target;
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;
public class ArtistGlideRequest { public class ArtistGlideRequest {
public 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;
public static DrawableTypeRequest createBaseRequest(RequestManager requestManager, 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.getName(), forceDownload));
} else {
return requestManager.load(CustomArtistImageUtil.getFile(artist));
}
}
public static Key createSignature(Artist artist) {
return ArtistSignatureUtil.getInstance(App.Companion.getContext()).getArtistSignature(artist.getName());
}
public static class Builder { public static class Builder {
final RequestManager requestManager;
final Artist artist; final Artist artist;
boolean noCustomImage;
boolean forceDownload; boolean forceDownload;
boolean noCustomImage;
final RequestManager requestManager;
public static Builder from(@NonNull RequestManager requestManager, Artist artist) {
return new Builder(requestManager, artist);
}
private Builder(@NonNull RequestManager requestManager, Artist artist) { private Builder(@NonNull RequestManager requestManager, Artist artist) {
this.requestManager = requestManager; this.requestManager = requestManager;
this.artist = artist; 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() { public BitmapBuilder asBitmap() {
return new BitmapBuilder(this); 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() { public DrawableRequestBuilder<GlideDrawable> build() {
//noinspection unchecked //noinspection unchecked
return createBaseRequest(requestManager, artist, noCustomImage, forceDownload) return createBaseRequest(requestManager, artist, noCustomImage, forceDownload)
@ -101,9 +71,24 @@ public class ArtistGlideRequest {
.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) .override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.signature(createSignature(artist)); .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 { public static class BitmapBuilder {
private final Builder builder; private final Builder builder;
public BitmapBuilder(Builder builder) { public BitmapBuilder(Builder builder) {
@ -112,7 +97,8 @@ public class ArtistGlideRequest {
public BitmapRequestBuilder<?, Bitmap> build() { public BitmapRequestBuilder<?, Bitmap> build() {
//noinspection unchecked //noinspection unchecked
return createBaseRequest(builder.requestManager, builder.artist, builder.noCustomImage, builder.forceDownload) return createBaseRequest(builder.requestManager, builder.artist, builder.noCustomImage,
builder.forceDownload)
.asBitmap() .asBitmap()
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY) .diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
.error(DEFAULT_ERROR_IMAGE) .error(DEFAULT_ERROR_IMAGE)
@ -124,7 +110,9 @@ public class ArtistGlideRequest {
} }
public static class PaletteBuilder { public static class PaletteBuilder {
final Context context; final Context context;
private final Builder builder; private final Builder builder;
public PaletteBuilder(Builder builder, Context context) { public PaletteBuilder(Builder builder, Context context) {
@ -134,7 +122,8 @@ public class ArtistGlideRequest {
public BitmapRequestBuilder<?, BitmapPaletteWrapper> build() { public BitmapRequestBuilder<?, BitmapPaletteWrapper> build() {
//noinspection unchecked //noinspection unchecked
return createBaseRequest(builder.requestManager, builder.artist, builder.noCustomImage, builder.forceDownload) return createBaseRequest(builder.requestManager, builder.artist, builder.noCustomImage,
builder.forceDownload)
.asBitmap() .asBitmap()
.transcode(new BitmapPaletteTranscoder(context), BitmapPaletteWrapper.class) .transcode(new BitmapPaletteTranscoder(context), BitmapPaletteWrapper.class)
.diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY) .diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY)
@ -145,6 +134,30 @@ public class ArtistGlideRequest {
.signature(createSignature(builder.artist)); .signature(createSignature(builder.artist));
} }
} }
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
public 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.getName()));
} else {
return requestManager.load(CustomArtistImageUtil.getFile(artist));
}
}
} }
/** /**

View File

@ -32,16 +32,15 @@ import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class ArtistImage(val artistName: String)
class ArtistImage(val artistName: String, val skipOkHttpCache: Boolean)
class ArtistImageFetcher( class ArtistImageFetcher(
private val context: Context, private val context: Context,
private val deezerApiService: DeezerApiService, private val deezerApiService: DeezerApiService,
val model: ArtistImage, val model: ArtistImage,
val urlLoader: ModelLoader<GlideUrl, InputStream>, val urlLoader: ModelLoader<GlideUrl, InputStream>,
val width: Int, val width: Int,
val height: Int val height: Int
) : DataFetcher<InputStream> { ) : DataFetcher<InputStream> {
private var urlFetcher: DataFetcher<InputStream>? = null private var urlFetcher: DataFetcher<InputStream>? = null
@ -96,9 +95,9 @@ class ArtistImageFetcher(
} }
class ArtistImageLoader( class ArtistImageLoader(
val context: Context, val context: Context,
private val deezerApiService: DeezerApiService, private val deezerApiService: DeezerApiService,
private val urlLoader: ModelLoader<GlideUrl, InputStream> private val urlLoader: ModelLoader<GlideUrl, InputStream>
) : StreamModelLoader<ArtistImage> { ) : StreamModelLoader<ArtistImage> {
override fun getResourceFetcher(model: ArtistImage, width: Int, height: Int): DataFetcher<InputStream> { override fun getResourceFetcher(model: ArtistImage, width: Int, height: Int): DataFetcher<InputStream> {
@ -107,22 +106,27 @@ class ArtistImageLoader(
} }
class Factory( class Factory(
val context: Context val context: Context
) : ModelLoaderFactory<ArtistImage, InputStream> { ) : ModelLoaderFactory<ArtistImage, InputStream> {
private var deezerApiService: DeezerApiService private var deezerApiService: DeezerApiService
private var okHttpFactory: OkHttpUrlLoader.Factory private var okHttpFactory: OkHttpUrlLoader.Factory
init { init {
okHttpFactory = OkHttpUrlLoader.Factory(OkHttpClient.Builder() okHttpFactory = OkHttpUrlLoader.Factory(
OkHttpClient.Builder()
.connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS) .connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
.readTimeout(TIMEOUT, TimeUnit.MILLISECONDS) .readTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
.writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS) .writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
.build()) .build()
deezerApiService = DeezerApiService.invoke(DeezerApiService.createDefaultOkHttpClient(context) )
deezerApiService = DeezerApiService.invoke(
DeezerApiService.createDefaultOkHttpClient(context)
.connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS) .connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
.readTimeout(TIMEOUT, TimeUnit.MILLISECONDS) .readTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
.writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS) .writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
.build()) .build()
)
} }
override fun build(context: Context?, factories: GenericLoaderFactory?): ModelLoader<ArtistImage, InputStream> { override fun build(context: Context?, factories: GenericLoaderFactory?): ModelLoader<ArtistImage, InputStream> {
@ -137,5 +141,4 @@ class Factory(
// we need these very low values to make sure our artist image loading calls doesn't block the image loading queue // 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 = 700
} }
} }