diff --git a/app/build.gradle b/app/build.gradle index 1e8ce7bd..99132647 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -123,8 +123,8 @@ dependencies { compile 'asia.ivity.android:drag-sort-listview:1.0' compile 'com.github.semoncat.seekarc:library:0.1' compile 'com.github.kabouzeid:AndroidSlidingUpPanel:3.2.1' - compile 'com.squareup.retrofit:retrofit:2.0.0-beta2' - compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2' + compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3' + compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta3' compile 'com.jakewharton:butterknife:7.0.1' compile 'org.solovyev.android.views:linear-layout-manager:0.5@aar' //noinspection GradleDynamicVersion @@ -132,5 +132,4 @@ dependencies { compile 'de.psdev.licensesdialog:licensesdialog:1.8.0' compile 'com.github.kabouzeid:AppIntro:3.3.0k' compile 'com.github.bumptech.glide:glide:3.6.1' - compile 'com.github.bumptech.glide:okhttp-integration:1.3.1@aar' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b14f5e2c..05cead43 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ - + @@ -114,6 +114,10 @@ android:name="com.kabouzeid.gramophone.glide.PhonographGlideModule" android:value="GlideModule" /> + + diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageFetcher.java b/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageFetcher.java index ef777d7e..8dd8273a 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageFetcher.java +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageFetcher.java @@ -15,7 +15,7 @@ import com.kabouzeid.gramophone.util.Util; import java.io.IOException; import java.io.InputStream; -import retrofit.Response; +import retrofit2.Response; /** * @author Karim Abou Zeid (kabouzeid) @@ -30,7 +30,6 @@ public class ArtistImageFetcher implements DataFetcher { private final int height; private volatile boolean isCancelled; private DataFetcher urlFetcher; - private InputStream inputStream; public ArtistImageFetcher(Context context, LastFMRestClient lastFMRestClient, ArtistImage model, ModelLoader urlLoader, int width, int height) { this.context = context; @@ -61,9 +60,8 @@ public class ArtistImageFetcher implements DataFetcher { GlideUrl url = new GlideUrl(LastFMUtil.getLargestArtistImageUrl(lastFmArtist.getArtist().getImage())); urlFetcher = urlLoader.getResourceFetcher(url, width, height); - inputStream = urlFetcher.loadData(priority); - return inputStream; + return urlFetcher.loadData(priority); } return null; } diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageLoader.java b/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageLoader.java index 815c9381..eb6d2a6c 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageLoader.java +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/artistimage/ArtistImageLoader.java @@ -2,24 +2,28 @@ package com.kabouzeid.gramophone.glide.artistimage; import android.content.Context; -import com.bumptech.glide.integration.okhttp.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 com.kabouzeid.gramophone.glide.okhttp.OkHttpUrlLoader; import com.kabouzeid.gramophone.lastfm.rest.LastFMRestClient; -import com.squareup.okhttp.OkHttpClient; import java.io.InputStream; import java.util.concurrent.TimeUnit; +import okhttp3.OkHttpClient; + /** * @author Karim Abou Zeid (kabouzeid) */ public class ArtistImageLoader implements StreamModelLoader { + // 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 urlLoader; @@ -40,14 +44,16 @@ public class ArtistImageLoader implements StreamModelLoader { private OkHttpUrlLoader.Factory okHttpFactory; public Factory(Context context) { - // we need these very low values to make sure our artist image loading calls doesn't block the image loading queue - OkHttpClient okHttpClient = new OkHttpClient(); - okHttpClient.setConnectTimeout(500, TimeUnit.MILLISECONDS); - okHttpClient.setReadTimeout(500, TimeUnit.MILLISECONDS); - okHttpClient.setWriteTimeout(500, TimeUnit.MILLISECONDS); - - okHttpFactory = new OkHttpUrlLoader.Factory(okHttpClient); - lastFMClient = new LastFMRestClient(context, okHttpClient); + 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 diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/okhttp/OkHttpGlideModule.java b/app/src/main/java/com/kabouzeid/gramophone/glide/okhttp/OkHttpGlideModule.java new file mode 100644 index 00000000..68881dc4 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/okhttp/OkHttpGlideModule.java @@ -0,0 +1,31 @@ +package com.kabouzeid.gramophone.glide.okhttp; + +import android.content.Context; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.GlideBuilder; +import com.bumptech.glide.load.model.GlideUrl; +import com.bumptech.glide.module.GlideModule; + +import java.io.InputStream; + +/** + * A {@link GlideModule} implementation to replace Glide's default + * {@link java.net.HttpURLConnection} based {@link com.bumptech.glide.load.model.ModelLoader} + * with an OkHttp based {@link com.bumptech.glide.load.model.ModelLoader}. + *

+ *

If you're using gradle, you can include this module simply by depending on the aar, the + * module will be merged in by manifest merger. For other build systems or for more more + * information, see {@link GlideModule}.

+ */ +public class OkHttpGlideModule implements GlideModule { + @Override + public void applyOptions(Context context, GlideBuilder builder) { + // Do nothing. + } + + @Override + public void registerComponents(Context context, Glide glide) { + glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory()); + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/okhttp/OkHttpStreamFetcher.java b/app/src/main/java/com/kabouzeid/gramophone/glide/okhttp/OkHttpStreamFetcher.java new file mode 100644 index 00000000..b27c9d7d --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/okhttp/OkHttpStreamFetcher.java @@ -0,0 +1,78 @@ +package com.kabouzeid.gramophone.glide.okhttp; + +import android.util.Log; + +import com.bumptech.glide.Priority; +import com.bumptech.glide.load.data.DataFetcher; +import com.bumptech.glide.load.model.GlideUrl; +import com.bumptech.glide.util.ContentLengthInputStream; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +import okhttp3.Call; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + +/** + * Fetches an {@link InputStream} using the okhttp library. + */ +public class OkHttpStreamFetcher implements DataFetcher { + private static final String TAG = "OkHttpFetcher"; + private final Call.Factory client; + private final GlideUrl url; + private InputStream stream; + private ResponseBody responseBody; + + public OkHttpStreamFetcher(Call.Factory client, GlideUrl url) { + this.client = client; + this.url = url; + } + + @Override + public InputStream loadData(Priority priority) throws Exception { + Request.Builder requestBuilder = new Request.Builder().url(url.toStringUrl()); + for (Map.Entry headerEntry : url.getHeaders().entrySet()) { + String key = headerEntry.getKey(); + requestBuilder.addHeader(key, headerEntry.getValue()); + } + Request request = requestBuilder.build(); + + Response response = client.newCall(request).execute(); + if (response.isSuccessful()) { + long contentLength = response.body().contentLength(); + responseBody = response.body(); + stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength); + } else { + Log.d(TAG, "OkHttp got error response: " + response.code() + ", " + response.message()); + } + + return stream; + } + + @Override + public void cleanup() { + try { + if (stream != null) { + stream.close(); + } + } catch (IOException e) { + // Ignored + } + if (responseBody != null) { + responseBody.close(); + } + } + + @Override + public String getId() { + return url.getCacheKey(); + } + + @Override + public void cancel() { + // TODO: call cancel on the client when this method is called on a background thread. See #257 + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/glide/okhttp/OkHttpUrlLoader.java b/app/src/main/java/com/kabouzeid/gramophone/glide/okhttp/OkHttpUrlLoader.java new file mode 100644 index 00000000..44d91c3a --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/glide/okhttp/OkHttpUrlLoader.java @@ -0,0 +1,76 @@ +package com.kabouzeid.gramophone.glide.okhttp; + +import android.content.Context; + +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 java.io.InputStream; + +import okhttp3.Call; +import okhttp3.OkHttpClient; + +/** + * A simple model loader for fetching media over http/https using OkHttp. + */ +public class OkHttpUrlLoader implements ModelLoader { + + private final Call.Factory client; + + public OkHttpUrlLoader(Call.Factory client) { + this.client = client; + } + + @Override + public DataFetcher getResourceFetcher(GlideUrl model, int width, int height) { + return new OkHttpStreamFetcher(client, model); + } + + /** + * The default factory for {@link OkHttpUrlLoader}s. + */ + public static class Factory implements ModelLoaderFactory { + private static volatile Call.Factory internalClient; + private Call.Factory client; + + private static Call.Factory getInternalClient() { + if (internalClient == null) { + synchronized (Factory.class) { + if (internalClient == null) { + internalClient = new OkHttpClient(); + } + } + } + return internalClient; + } + + /** + * Constructor for a new Factory that runs requests using a static singleton client. + */ + public Factory() { + this(getInternalClient()); + } + + /** + * Constructor for a new Factory that runs requests using given client. + * + * @param client this is typically an instance of {@code OkHttpClient}. + */ + public Factory(Call.Factory client) { + this.client = client; + } + + @Override + public ModelLoader build(Context context, GenericLoaderFactory factories) { + return new OkHttpUrlLoader(client); + } + + @Override + public void teardown() { + // Do nothing, this instance doesn't own the client. + } + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/LastFMRestClient.java b/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/LastFMRestClient.java index e1718087..d963e675 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/LastFMRestClient.java +++ b/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/LastFMRestClient.java @@ -2,19 +2,22 @@ package com.kabouzeid.gramophone.lastfm.rest; import android.content.Context; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import com.kabouzeid.gramophone.lastfm.rest.service.LastFMService; -import com.squareup.okhttp.Cache; -import com.squareup.okhttp.Interceptor; -import com.squareup.okhttp.OkHttpClient; -import com.squareup.okhttp.Request; -import com.squareup.okhttp.Response; import java.io.File; import java.io.IOException; -import retrofit.GsonConverterFactory; -import retrofit.Retrofit; +import okhttp3.Cache; +import okhttp3.Call; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import retrofit2.GsonConverterFactory; +import retrofit2.Retrofit; + /** * @author Karim Abou Zeid (kabouzeid) @@ -25,28 +28,13 @@ public class LastFMRestClient { private LastFMService apiService; public LastFMRestClient(@NonNull Context context) { - this(context, new OkHttpClient()); + this(createDefaultOkHttpClientBuilder(context).build()); } - public LastFMRestClient(@NonNull Context context, @NonNull OkHttpClient okHttpClient) { - File cacheDir = new File(context.getCacheDir().getAbsolutePath(), "/okhttp-lastfm/"); - if (cacheDir.mkdirs() || cacheDir.isDirectory()) { - okHttpClient.setCache(new Cache(cacheDir, 1024 * 1024 * 10)); - } - - okHttpClient.interceptors().add(new Interceptor() { - @Override - public Response intercept(Chain chain) throws IOException { - Request modifiedRequest = chain.request().newBuilder() - .addHeader("Cache-Control", String.format("max-age=%d, max-stale=%d", 31536000, 31536000)) - .build(); - return chain.proceed(modifiedRequest); - } - }); - + public LastFMRestClient(@NonNull Call.Factory client) { Retrofit restAdapter = new Retrofit.Builder() .baseUrl(BASE_URL) - .client(okHttpClient) + .callFactory(client) .addConverterFactory(GsonConverterFactory.create()) .build(); @@ -56,4 +44,31 @@ public class LastFMRestClient { public LastFMService getApiService() { return apiService; } + + @Nullable + public static Cache createDefaultCache(Context context) { + File cacheDir = new File(context.getCacheDir().getAbsolutePath(), "/okhttp-lastfm/"); + if (cacheDir.mkdirs() || cacheDir.isDirectory()) { + return new Cache(cacheDir, 1024 * 1024 * 10); + } + return null; + } + + public static Interceptor createCacheControlInterceptor() { + return new Interceptor() { + @Override + public Response intercept(Chain chain) throws IOException { + Request modifiedRequest = chain.request().newBuilder() + .addHeader("Cache-Control", String.format("max-age=%d, max-stale=%d", 31536000, 31536000)) + .build(); + return chain.proceed(modifiedRequest); + } + }; + } + + public static OkHttpClient.Builder createDefaultOkHttpClientBuilder(Context context) { + return new OkHttpClient.Builder() + .cache(createDefaultCache(context)) + .addInterceptor(createCacheControlInterceptor()); + } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/service/LastFMService.java b/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/service/LastFMService.java index df9194aa..7883af74 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/service/LastFMService.java +++ b/app/src/main/java/com/kabouzeid/gramophone/lastfm/rest/service/LastFMService.java @@ -5,10 +5,10 @@ import android.support.annotation.Nullable; import com.kabouzeid.gramophone.lastfm.rest.model.LastFmAlbum; import com.kabouzeid.gramophone.lastfm.rest.model.LastFmArtist; -import retrofit.Call; -import retrofit.http.GET; -import retrofit.http.Header; -import retrofit.http.Query; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Header; +import retrofit2.http.Query; /** * @author Karim Abou Zeid (kabouzeid) diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java index 22babb44..cdb6ab90 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java @@ -56,9 +56,8 @@ import java.util.ArrayList; import butterknife.Bind; import butterknife.ButterKnife; -import retrofit.Callback; -import retrofit.Response; -import retrofit.Retrofit; +import retrofit2.Callback; +import retrofit2.Response; /** * Be careful when changing things in this Activity! @@ -222,7 +221,7 @@ public class ArtistDetailActivity extends AbsSlidingMusicPanelActivity implement private void loadBiography() { lastFMRestClient.getApiService().getArtistInfo(artist.name, null).enqueue(new Callback() { @Override - public void onResponse(Response response, Retrofit retrofit) { + public void onResponse(Response response) { LastFmArtist lastFmArtist = response.body(); if (lastFmArtist.getArtist() != null) { String bio = lastFmArtist.getArtist().getBio().getContent(); diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AlbumTagEditorActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AlbumTagEditorActivity.java index 9a0ebd3d..83a04e0c 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AlbumTagEditorActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/tageditor/AlbumTagEditorActivity.java @@ -43,9 +43,8 @@ import java.util.Map; import butterknife.Bind; import butterknife.ButterKnife; -import retrofit.Callback; -import retrofit.Response; -import retrofit.Retrofit; +import retrofit2.Callback; +import retrofit2.Response; public class AlbumTagEditorActivity extends AbsTagEditorActivity implements TextWatcher { @@ -107,7 +106,7 @@ public class AlbumTagEditorActivity extends AbsTagEditorActivity implements Text } lastFMRestClient.getApiService().getAlbumInfo(albumTitleStr, albumArtistNameStr).enqueue(new Callback() { @Override - public void onResponse(final Response response, Retrofit retrofit) { + public void onResponse(final Response response) { LastFmAlbum lastFmAlbum = response.body(); if (lastFmAlbum.getAlbum() != null) { String url = LastFMUtil.getLargestAlbumImageUrl(lastFmAlbum.getAlbum().getImage());