From 456fc5b9912053a6cde1247c711fdbd5fa806c14 Mon Sep 17 00:00:00 2001 From: jendib Date: Sat, 23 Jan 2016 23:20:09 +0100 Subject: [PATCH] #57: Android: Migrate document resource to OkHttp Closes #58: Android: OkHttpClient and cache as singleton --- docs-android/app/app.iml | 15 +- docs-android/app/build.gradle | 2 +- .../docs/activity/DocumentEditActivity.java | 9 +- .../docs/activity/DocumentViewActivity.java | 11 +- .../docs/adapter/CommentListAdapter.java | 2 +- .../docs/adapter/FilePagerAdapter.java | 6 +- .../docs/fragment/DocShareFragment.java | 7 +- .../sismics/docs/resource/BaseResource.java | 93 --------- .../docs/resource/DocumentResource.java | 91 ++++++--- .../com/sismics/docs/util/OkHttpUtil.java | 190 ++++++++++++------ 10 files changed, 214 insertions(+), 212 deletions(-) diff --git a/docs-android/app/app.iml b/docs-android/app/app.iml index 1a8f311d..0df93fa8 100644 --- a/docs-android/app/app.iml +++ b/docs-android/app/app.iml @@ -61,13 +61,6 @@ - - - - - - - @@ -75,6 +68,13 @@ + + + + + + + @@ -88,6 +88,7 @@ + diff --git a/docs-android/app/build.gradle b/docs-android/app/build.gradle index bddc240f..3c6ad05c 100644 --- a/docs-android/app/build.gradle +++ b/docs-android/app/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0-alpha5' + classpath 'com.android.tools.build:gradle:2.0.0-alpha7' } } apply plugin: 'com.android.application' diff --git a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentEditActivity.java b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentEditActivity.java index 148e61b9..6214b2fe 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentEditActivity.java +++ b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentEditActivity.java @@ -17,7 +17,7 @@ import com.sismics.docs.adapter.LanguageAdapter; import com.sismics.docs.adapter.TagAutoCompleteAdapter; import com.sismics.docs.event.DocumentAddEvent; import com.sismics.docs.event.DocumentEditEvent; -import com.sismics.docs.listener.JsonHttpResponseHandler; +import com.sismics.docs.listener.HttpCallback; import com.sismics.docs.resource.DocumentResource; import com.sismics.docs.ui.form.Validator; import com.sismics.docs.ui.form.validator.Required; @@ -35,7 +35,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import cz.msebera.android.httpclient.Header; import de.greenrobot.event.EventBus; /** @@ -174,9 +173,9 @@ public class DocumentEditActivity extends AppCompatActivity { }); // Server callback - JsonHttpResponseHandler callback = new JsonHttpResponseHandler() { + HttpCallback callback = new HttpCallback() { @Override - public void onSuccess(int statusCode, Header[] headers, JSONObject response) { + public void onSuccess(JSONObject response) { // Build a fake document JSON to update the UI final JSONObject outputDoc = new JSONObject(); try { @@ -211,7 +210,7 @@ public class DocumentEditActivity extends AppCompatActivity { } @Override - public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) { + public void onFailure(JSONObject json, Exception e) { Toast.makeText(DocumentEditActivity.this, R.string.error_editing_document, Toast.LENGTH_LONG).show(); } diff --git a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java index d3b9f07d..8c90f2c5 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java +++ b/docs-android/app/src/main/java/com/sismics/docs/activity/DocumentViewActivity.java @@ -46,6 +46,7 @@ import com.sismics.docs.event.FileAddEvent; import com.sismics.docs.event.FileDeleteEvent; import com.sismics.docs.fragment.DocExportPdfFragment; import com.sismics.docs.fragment.DocShareFragment; +import com.sismics.docs.listener.HttpCallback; import com.sismics.docs.listener.JsonHttpResponseHandler; import com.sismics.docs.model.application.ApplicationContext; import com.sismics.docs.resource.CommentResource; @@ -487,14 +488,14 @@ public class DocumentViewActivity extends AppCompatActivity { // Actual delete server call final String documentId = document.optString("id"); - DocumentResource.delete(DocumentViewActivity.this, documentId, new JsonHttpResponseHandler() { + DocumentResource.delete(DocumentViewActivity.this, documentId, new HttpCallback() { @Override - public void onSuccess(int statusCode, Header[] headers, JSONObject response) { + public void onSuccess(JSONObject response) { EventBus.getDefault().post(new DocumentDeleteEvent(documentId)); } @Override - public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) { + public void onFailure(JSONObject json, Exception e) { Toast.makeText(DocumentViewActivity.this, R.string.document_delete_failure, Toast.LENGTH_LONG).show(); } @@ -646,9 +647,9 @@ public class DocumentViewActivity extends AppCompatActivity { // Silently get the document to know if it is writable by the current user // If this call fails or is slow and the document is read-only, // write actions will be allowed and will fail - DocumentResource.get(this, document.optString("id"), new JsonHttpResponseHandler() { + DocumentResource.get(this, document.optString("id"), new HttpCallback() { @Override - public void onSuccess(int statusCode, Header[] headers, JSONObject response) { + public void onSuccess(JSONObject response) { document = response; boolean writable = document.optBoolean("writable"); diff --git a/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java b/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java index 8c725572..6983d5e3 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java +++ b/docs-android/app/src/main/java/com/sismics/docs/adapter/CommentListAdapter.java @@ -81,7 +81,7 @@ public class CommentListAdapter extends BaseAdapter { // Gravatar image String gravatarUrl = "http://www.gravatar.com/avatar/" + comment.optString("creator_gravatar") + "?s=128d=identicon"; - OkHttpUtil.picasso(context, null) + OkHttpUtil.picasso(context) .load(gravatarUrl) .into(gravatarImageView); diff --git a/docs-android/app/src/main/java/com/sismics/docs/adapter/FilePagerAdapter.java b/docs-android/app/src/main/java/com/sismics/docs/adapter/FilePagerAdapter.java index e2bd5067..8b04cede 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/adapter/FilePagerAdapter.java +++ b/docs-android/app/src/main/java/com/sismics/docs/adapter/FilePagerAdapter.java @@ -66,9 +66,9 @@ public class FilePagerAdapter extends PagerAdapter { String fileUrl = PreferenceUtil.getServerUrl(context) + "/api/file/" + file.optString("id") + "/data?size=web"; // Load image - OkHttpUtil.picasso(context, authToken) + OkHttpUtil.picasso(context) .load(fileUrl) - .memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE) + .memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE) // Don't memory cache the images .into(fileImageView, new Callback.EmptyCallback() { @Override public void onSuccess() { @@ -109,7 +109,7 @@ public class FilePagerAdapter extends PagerAdapter { * @return Object */ public JSONObject getObjectAt(int position) { - if (files == null) { + if (files == null || position < 0 || position >= files.size()) { return null; } diff --git a/docs-android/app/src/main/java/com/sismics/docs/fragment/DocShareFragment.java b/docs-android/app/src/main/java/com/sismics/docs/fragment/DocShareFragment.java index 0a39fa6c..aa85a063 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/fragment/DocShareFragment.java +++ b/docs-android/app/src/main/java/com/sismics/docs/fragment/DocShareFragment.java @@ -21,6 +21,7 @@ import com.sismics.docs.R; import com.sismics.docs.adapter.ShareListAdapter; import com.sismics.docs.event.ShareDeleteEvent; import com.sismics.docs.event.ShareSendEvent; +import com.sismics.docs.listener.HttpCallback; import com.sismics.docs.listener.JsonHttpResponseHandler; import com.sismics.docs.resource.DocumentResource; import com.sismics.docs.resource.ShareResource; @@ -122,9 +123,9 @@ public class DocShareFragment extends DialogFragment { final ProgressBar shareProgressBar = (ProgressBar) view.findViewById(R.id.shareProgressBar); shareListView.setEmptyView(shareProgressBar); - DocumentResource.get(getActivity(), getArguments().getString("id"), new JsonHttpResponseHandler() { + DocumentResource.get(getActivity(), getArguments().getString("id"), new HttpCallback() { @Override - public void onSuccess(int statusCode, Header[] headers, JSONObject response) { + public void onSuccess(JSONObject response) { document = response; JSONArray acls = response.optJSONArray("acls"); shareProgressBar.setVisibility(View.GONE); @@ -133,7 +134,7 @@ public class DocShareFragment extends DialogFragment { } @Override - public void onAllFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) { + public void onFailure(JSONObject json, Exception e) { getDialog().cancel(); Toast.makeText(getActivity(), R.string.error_loading_shares, Toast.LENGTH_SHORT).show(); } diff --git a/docs-android/app/src/main/java/com/sismics/docs/resource/BaseResource.java b/docs-android/app/src/main/java/com/sismics/docs/resource/BaseResource.java index 985c69b3..c0f421cb 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/resource/BaseResource.java +++ b/docs-android/app/src/main/java/com/sismics/docs/resource/BaseResource.java @@ -9,11 +9,7 @@ import com.sismics.docs.util.ApplicationUtil; import com.sismics.docs.util.PreferenceUtil; import java.io.IOException; -import java.net.CookieManager; -import java.net.CookiePolicy; -import java.net.HttpCookie; import java.net.Socket; -import java.net.URI; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -22,18 +18,12 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Locale; -import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory; -import okhttp3.Interceptor; -import okhttp3.JavaNetCookieJar; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; /** * Base class for API access. @@ -41,7 +31,6 @@ import okhttp3.Response; * @author bgamard */ public class BaseResource { - /** * User-Agent to use. */ @@ -57,11 +46,6 @@ public class BaseResource { */ protected static AsyncHttpClient client = new AsyncHttpClient(); - /** - * OkHttp client. - */ - protected static OkHttpClient okHttpClient = new OkHttpClient(); - static { // 20sec default timeout client.setTimeout(60000); @@ -76,43 +60,6 @@ public class BaseResource { } catch (Exception e) { // NOP } - - // OkHttp configuration - try { - // Create a trust manager that does not validate certificate chains - final TrustManager[] trustAllCerts = new TrustManager[] { - new X509TrustManager() { - @Override - public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { - } - - @Override - public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { - } - - @Override - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return null; - } - } - }; - - // Install the all-trusting trust manager - final SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); - final javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); - - // Configure OkHttpClient - okHttpClient = okHttpClient.newBuilder() - .connectTimeout(30, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .writeTimeout(30, TimeUnit.SECONDS) - .hostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) - .sslSocketFactory(sslSocketFactory) - .build(); - } catch (Exception e) { - // NOP - } } /** @@ -135,46 +82,6 @@ public class BaseResource { } } - /** - * Build an OkHttpClient. - * - * @param context Context - * @return OkHttpClient - */ - protected static OkHttpClient buildOkHttpClient(final Context context) { - // Header computation - if (USER_AGENT == null) { - USER_AGENT = "Sismics Docs Android " + ApplicationUtil.getVersionName(context) + "/Android " + Build.VERSION.RELEASE + "/" + Build.MODEL; - } - - if (ACCEPT_LANGUAGE == null) { - Locale locale = Locale.getDefault(); - ACCEPT_LANGUAGE = locale.getLanguage() + "_" + locale.getCountry(); - } - - // Cookie handling - com.sismics.docs.resource.cookie.PersistentCookieStore cookieStore = new com.sismics.docs.resource.cookie.PersistentCookieStore(context); - CookieManager cookieManager = new CookieManager(cookieStore, CookiePolicy.ACCEPT_ALL); - cookieStore.add(URI.create(PreferenceUtil.getServerUrl(context)), - new HttpCookie("auth_token", PreferenceUtil.getAuthToken(context))); // TODO Remove me when async http is ditched - - // Runtime configuration - return okHttpClient.newBuilder() - .cookieJar(new JavaNetCookieJar(cookieManager)) - .addNetworkInterceptor(new Interceptor() { - @Override - public Response intercept(Chain chain) throws IOException { - Request originalRequest = chain.request(); - return chain.proceed(originalRequest.newBuilder() - .header("User-Agent", USER_AGENT) - .header("Accept-Language", ACCEPT_LANGUAGE) - // TODO necessary?? .method(originalRequest.method(), originalRequest.body()) - .build()); - } - }) - .build(); - } - /** * Socket factory to allow self-signed certificates for Async HTTP Client. * diff --git a/docs-android/app/src/main/java/com/sismics/docs/resource/DocumentResource.java b/docs-android/app/src/main/java/com/sismics/docs/resource/DocumentResource.java index 65c8a0ed..474590bf 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/resource/DocumentResource.java +++ b/docs-android/app/src/main/java/com/sismics/docs/resource/DocumentResource.java @@ -2,12 +2,12 @@ package com.sismics.docs.resource; import android.content.Context; -import com.loopj.android.http.RequestParams; import com.sismics.docs.listener.HttpCallback; -import com.sismics.docs.listener.JsonHttpResponseHandler; +import com.sismics.docs.util.OkHttpUtil; import java.util.Set; +import okhttp3.FormBody; import okhttp3.HttpUrl; import okhttp3.Request; @@ -35,8 +35,9 @@ public class DocumentResource extends BaseResource { .addQueryParameter("asc", "false") .addQueryParameter("search", query) .build()) + .get() .build(); - buildOkHttpClient(context) + OkHttpUtil.buildClient(context) .newCall(request) .enqueue(HttpCallback.buildOkHttpCallback(callback)); } @@ -46,12 +47,16 @@ public class DocumentResource extends BaseResource { * * @param context Context * @param id ID - * @param responseHandler Callback + * @param callback Callback */ - public static void get(Context context, String id, JsonHttpResponseHandler responseHandler) { - init(context); - - client.get(getApiUrl(context) + "/document/" + id, responseHandler); + public static void get(Context context, String id, HttpCallback callback) { + Request request = new Request.Builder() + .url(HttpUrl.parse(getApiUrl(context) + "/document/" + id)) + .get() + .build(); + OkHttpUtil.buildClient(context) + .newCall(request) + .enqueue(HttpCallback.buildOkHttpCallback(callback)); } /** @@ -59,12 +64,16 @@ public class DocumentResource extends BaseResource { * * @param context Context * @param id ID - * @param responseHandler Callback + * @param callback Callback */ - public static void delete(Context context, String id, JsonHttpResponseHandler responseHandler) { - init(context); - - client.delete(getApiUrl(context) + "/document/" + id, responseHandler); + public static void delete(Context context, String id, HttpCallback callback) { + Request request = new Request.Builder() + .url(HttpUrl.parse(getApiUrl(context) + "/document/" + id)) + .delete() + .build(); + OkHttpUtil.buildClient(context) + .newCall(request) + .enqueue(HttpCallback.buildOkHttpCallback(callback)); } /** @@ -76,19 +85,27 @@ public class DocumentResource extends BaseResource { * @param tagIdList Tags ID list * @param language Language * @param createDate Create date - * @param responseHandler Callback + * @param callback Callback */ public static void add(Context context, String title, String description, - Set tagIdList, String language, long createDate, JsonHttpResponseHandler responseHandler) { - init(context); + Set tagIdList, String language, long createDate, HttpCallback callback) { + FormBody.Builder formBuilder = new FormBody.Builder() + .add("title", title) + .add("description", description) + .add("language", language) + .add("create_date", Long.toString(createDate)); + String[] tagIdArray = tagIdList.toArray(new String[tagIdList.size()]); + for (int i = 0; i < tagIdArray.length; i++) { + formBuilder.add("tags", tagIdArray[i]); + } - RequestParams params = new RequestParams(); - params.put("title", title); - params.put("description", description); - params.put("tags", tagIdList); - params.put("language", language); - params.put("create_date", createDate); - client.put(getApiUrl(context) + "/document", params, responseHandler); + Request request = new Request.Builder() + .url(HttpUrl.parse(getApiUrl(context) + "/document")) + .put(formBuilder.build()) + .build(); + OkHttpUtil.buildClient(context) + .newCall(request) + .enqueue(HttpCallback.buildOkHttpCallback(callback)); } /** @@ -101,19 +118,27 @@ public class DocumentResource extends BaseResource { * @param tagIdList Tags ID list * @param language Language * @param createDate Create date - * @param responseHandler Callback + * @param callback Callback */ public static void edit(Context context, String id, String title, String description, - Set tagIdList, String language, long createDate, JsonHttpResponseHandler responseHandler) { - init(context); + Set tagIdList, String language, long createDate, HttpCallback callback) { + FormBody.Builder formBuilder = new FormBody.Builder() + .add("title", title) + .add("description", description) + .add("language", language) + .add("create_date", Long.toString(createDate)); + String[] tagIdArray = tagIdList.toArray(new String[tagIdList.size()]); + for (int i = 0; i < tagIdArray.length; i++) { + formBuilder.add("tags", tagIdArray[i]); + } - RequestParams params = new RequestParams(); - params.put("title", title); - params.put("description", description); - params.put("tags", tagIdList); - params.put("language", language); - params.put("create_date", createDate); - client.post(getApiUrl(context) + "/document/" + id, params, responseHandler); + Request request = new Request.Builder() + .url(HttpUrl.parse(getApiUrl(context) + "/document/" + id)) + .post(formBuilder.build()) + .build(); + OkHttpUtil.buildClient(context) + .newCall(request) + .enqueue(HttpCallback.buildOkHttpCallback(callback)); } /** diff --git a/docs-android/app/src/main/java/com/sismics/docs/util/OkHttpUtil.java b/docs-android/app/src/main/java/com/sismics/docs/util/OkHttpUtil.java index be3cdc39..e6756cdf 100644 --- a/docs-android/app/src/main/java/com/sismics/docs/util/OkHttpUtil.java +++ b/docs-android/app/src/main/java/com/sismics/docs/util/OkHttpUtil.java @@ -1,23 +1,29 @@ package com.sismics.docs.util; import android.content.Context; +import android.os.Build; import android.util.Log; import com.jakewharton.picasso.OkHttp3Downloader; +import com.sismics.docs.resource.cookie.PersistentCookieStore; import com.squareup.picasso.Picasso; import java.io.IOException; +import java.net.CookieManager; +import java.net.CookiePolicy; +import java.net.HttpCookie; +import java.net.URI; import java.security.cert.CertificateException; +import java.util.Locale; +import java.util.concurrent.TimeUnit; -import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import okhttp3.Cache; import okhttp3.Interceptor; +import okhttp3.JavaNetCookieJar; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; @@ -29,57 +35,27 @@ import okhttp3.Response; */ public class OkHttpUtil { /** - * Build a Picasso object with base config. - * - * @param context Context - * @param authToken Auth token - * @return Picasso object + * OkHttp singleton client. */ - public static Picasso picasso(Context context, final String authToken) { - OkHttpClient okHttpClient = buildOkHttpClient() - .addInterceptor(new Interceptor() { - @Override - public Response intercept(Interceptor.Chain chain) throws IOException { - final Request original = chain.request(); - final Request.Builder requestBuilder = original.newBuilder() - .header("Cookie", "auth_token=" + authToken) - .header("Cache-Control", "max-age=" + (3600 * 24 * 365)) - .method(original.method(), original.body()); - return chain.proceed(requestBuilder.build()); - } - }) - .cache(new Cache(context.getCacheDir(), - PreferenceUtil.getIntegerPreference(context, PreferenceUtil.PREF_CACHE_SIZE, 0) * 1000000)) - .build(); - - Picasso picasso = new Picasso.Builder(context) - .downloader(new OkHttp3Downloader(okHttpClient)) - .build(); - picasso.setIndicatorsEnabled(false); - return picasso; - } + private static OkHttpClient okHttpClient = new OkHttpClient(); /** - * Clear the cache. - * - * @param context Context + * Singleton cache. */ - public static void clearCache(Context context) { - Cache cache = new Cache(context.getCacheDir(), - PreferenceUtil.getIntegerPreference(context, PreferenceUtil.PREF_CACHE_SIZE, Integer.MAX_VALUE)); - try { - cache.evictAll(); - } catch (IOException e) { - Log.e("OKHttpUtil", "Error clearing cache", e); - } - } + private static Cache cache = null; /** - * Build a OkHttpClient accepting all SSL certificates. - * - * @return OkHttpClient.Builder + * User-Agent to use. */ - private static OkHttpClient.Builder buildOkHttpClient() { + protected static String userAgent = null; + + /** + * Accept-Language header. + */ + protected static String acceptLanguage = null; + + static { + // OkHttp configuration try { // Create a trust manager that does not validate certificate chains final TrustManager[] trustAllCerts = new TrustManager[] { @@ -102,22 +78,114 @@ public class OkHttpUtil { // Install the all-trusting trust manager final SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + final javax.net.ssl.SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); - // Create an ssl socket factory with our all-trusting manager - final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); - - OkHttpClient.Builder builder = new OkHttpClient.Builder(); - builder.sslSocketFactory(sslSocketFactory); - builder.hostnameVerifier(new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - }); - - return builder; + // Configure OkHttpClient + okHttpClient = okHttpClient.newBuilder() + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .sslSocketFactory(sslSocketFactory) + .build(); } catch (Exception e) { - throw new RuntimeException(e); + // NOP } } + + /** + * Build a Picasso object with base config. + * + * @param context Context + * @return Picasso object + */ + public static Picasso picasso(Context context) { + OkHttpClient okHttpClient = buildClient(context) + .newBuilder() + .addInterceptor(new Interceptor() { + @Override + public Response intercept(Interceptor.Chain chain) throws IOException { // Override cache configuration + final Request original = chain.request(); + final Request.Builder requestBuilder = original.newBuilder() + .header("Cache-Control", "max-age=" + (3600 * 24 * 365)) + .method(original.method(), original.body()); + return chain.proceed(requestBuilder.build()); + } + }) + .cache(getCache(context)) + .build(); + + Picasso picasso = new Picasso.Builder(context) + .downloader(new OkHttp3Downloader(okHttpClient)) + .build(); + picasso.setIndicatorsEnabled(false); // Debug stuff + return picasso; + } + + /** + * Get and eventually build the singleton cache. + * + * @param context Context + * @return Cache + */ + private static Cache getCache(Context context) { + if (cache == null) { + cache = new Cache(context.getCacheDir(), + PreferenceUtil.getIntegerPreference(context, PreferenceUtil.PREF_CACHE_SIZE, 0) * 1000000); + } + return cache; + } + + /** + * Clear the HTTP cache. + * + * @param context Context + */ + public static void clearCache(Context context) { + Cache cache = getCache(context); + try { + cache.evictAll(); + } catch (IOException e) { + Log.e("OKHttpUtil", "Error clearing cache", e); + } + } + + /** + * Build an OkHttpClient. + * + * @param context Context + * @return OkHttpClient + */ + public static OkHttpClient buildClient(final Context context) { + // One-time header computation + if (userAgent == null) { + userAgent = "Sismics Docs Android " + ApplicationUtil.getVersionName(context) + "/Android " + Build.VERSION.RELEASE + "/" + Build.MODEL; + } + + if (acceptLanguage == null) { + Locale locale = Locale.getDefault(); + acceptLanguage = locale.getLanguage() + "_" + locale.getCountry(); + } + + // Cookie handling + PersistentCookieStore cookieStore = new PersistentCookieStore(context); + CookieManager cookieManager = new CookieManager(cookieStore, CookiePolicy.ACCEPT_ALL); + cookieStore.add(URI.create(PreferenceUtil.getServerUrl(context)), + new HttpCookie("auth_token", PreferenceUtil.getAuthToken(context))); // TODO Remove me when async http is ditched + + // Runtime configuration + return okHttpClient.newBuilder() + .cookieJar(new JavaNetCookieJar(cookieManager)) + .addNetworkInterceptor(new Interceptor() { + @Override + public Response intercept(Chain chain) throws IOException { + Request originalRequest = chain.request(); + return chain.proceed(originalRequest.newBuilder() + .header("User-Agent", userAgent) + .header("Accept-Language", acceptLanguage) + // TODO necessary?? .method(originalRequest.method(), originalRequest.body()) + .build()); + } + }) + .build(); + } }