package com.github.miao1007.animewallpaper.utils; import android.content.Context; import android.support.annotation.IntRange; import android.support.annotation.WorkerThread; import android.util.Log; import com.github.miao1007.animewallpaper.support.GlobalContext; import com.squareup.picasso.OkHttp3Downloader; import com.squareup.picasso.Picasso; import im.fir.sdk.FIR; import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.List; import okhttp3.Cache; import okhttp3.Dispatcher; import okhttp3.Dns; import okhttp3.HttpUrl; import okhttp3.Interceptor; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; import okhttp3.logging.HttpLoggingInterceptor; import okio.Buffer; import okio.BufferedSource; import okio.ForwardingSource; import okio.Okio; import okio.Source; import retrofit2.Retrofit; import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; import rx.Scheduler; import rx.schedulers.Schedulers; /** * Created by leon on 1/26/16. * Singleton Utils */ public abstract class SquareUtils { static Dispatcher dispatcher; static private Picasso picasso; static private OkHttpClient client; static private OkHttpClient httpDnsclient; static Dns HTTP_DNS = new Dns() { @Override public List<InetAddress> lookup(String hostname) throws UnknownHostException { if (hostname == null) throw new UnknownHostException("hostname == null"); HttpUrl httpUrl = new HttpUrl.Builder().scheme("http") .host("119.29.29.29") .addPathSegment("d") .addQueryParameter("dn", hostname) .build(); Request dnsRequest = new Request.Builder().url(httpUrl).get().build(); try { String s = getHTTPDnsClient().newCall(dnsRequest).execute().body().string(); //free server may down, will return loopback ip address if (!s.matches("\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b")) { return Dns.SYSTEM.lookup(hostname); } return Arrays.asList(InetAddress.getAllByName(s)); } catch (IOException e) { FIR.addCustomizeValue("DNS", "err:" + dnsRequest.toString()); FIR.sendCrashManually(e); return Dns.SYSTEM.lookup(hostname); } } }; static private HttpLoggingInterceptor loggingInterceptor; static private Scheduler scheduler; private SquareUtils() { throw new AssertionError("Utils can't be an instance!"); } public static synchronized Dispatcher getDispatcher() { if (dispatcher == null) { dispatcher = new Dispatcher(); } return dispatcher; } public static synchronized Scheduler getRxWorkerScheduler() { if (scheduler == null) { scheduler = Schedulers.from(getDispatcher().executorService()); } return scheduler; } /** * OkHttp client for httpDNS, shared Executor */ static public synchronized OkHttpClient getHTTPDnsClient() { if (httpDnsclient == null) { final File cacheDir = GlobalContext.getInstance().getExternalCacheDir(); httpDnsclient = new OkHttpClient.Builder().dispatcher(getDispatcher()) //.addInterceptor(getLogger()) .addNetworkInterceptor(new Interceptor() { //REWRITE_CACHE_CONTROL_INTERCEPTOR @Override public Response intercept(Chain chain) throws IOException { Response originalResponse = chain.proceed(chain.request()); return originalResponse.newBuilder() //dns default cache time .header("Cache-Control", "max-age=600").build(); } }).cache(new Cache(new File(cacheDir, "httpdns"), 5 * 1024 * 1024)).build(); } return httpDnsclient; } static public synchronized OkHttpClient getClient() { if (client == null) { final File cacheDir = GlobalContext.getInstance().getExternalCacheDir(); client = new OkHttpClient.Builder() //Interceptor -> cache -> NetworkInterceptor .addNetworkInterceptor(getLogger()) .cache(new Cache(new File(cacheDir, "okhttp"), 60 * 1024 * 1024)) .dispatcher(getDispatcher()) .dns(HTTP_DNS) .build(); } return client; } private static synchronized Interceptor getLogger() { if (loggingInterceptor == null) { loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() { @Override public void log(String message) { Log.d("okhttp", message); } }).setLevel(HttpLoggingInterceptor.Level.HEADERS); } return loggingInterceptor; } /** * Not singleton */ private static OkHttpClient getProgressBarClient(final ProgressListener listener) { return getClient().newBuilder().addNetworkInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Response originalResponse = chain.proceed(chain.request()); return originalResponse.newBuilder() .body(new ProgressResponseBody(originalResponse.body(), listener)) .build(); } }).build(); } /** * Singleton Picasso shared cache with OkHttp/Retrofit */ static public Picasso getPicasso(Context context) { if (picasso == null) { synchronized (SquareUtils.class) { picasso = new Picasso.Builder(context).downloader(new OkHttp3Downloader(getClient())).build(); } } return picasso; } /** * Download Big Image only, Not singleton but shared cache */ static public Picasso getPicasso(Context context, ProgressListener listener) { OkHttpClient client = getProgressBarClient(listener); OkHttp3Downloader downloader = new OkHttp3Downloader(client); return new Picasso.Builder(context).downloader(downloader) .memoryCache(com.squareup.picasso.Cache.NONE) .build(); } /** * There is no need to let retrofit singleton * BuilderTime + DynamicProxyTime == 0.6ms */ public static Retrofit getRetrofit(String url) { return new Retrofit.Builder().baseUrl(url) .client(getClient()) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); } public interface ProgressListener { @WorkerThread void update(@IntRange(from = 0, to = 100) int percent); } private static class ProgressResponseBody extends ResponseBody { private final ResponseBody responseBody; private final ProgressListener progressListener; private BufferedSource bufferedSource; public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) { this.responseBody = responseBody; this.progressListener = progressListener; } @Override public MediaType contentType() { return responseBody.contentType(); } @Override public long contentLength() { return responseBody.contentLength(); } @Override public BufferedSource source() { if (bufferedSource == null) { bufferedSource = Okio.buffer(source(responseBody.source())); } return bufferedSource; } private Source source(Source source) { return new ForwardingSource(source) { long totalBytesRead = 0L; @Override public long read(Buffer sink, long byteCount) throws IOException { long bytesRead = super.read(sink, byteCount); // read() returns the number of bytes read, or -1 if this source is exhausted. totalBytesRead += bytesRead != -1 ? bytesRead : 0; if (progressListener != null) { progressListener.update( ((int) ((100 * totalBytesRead) / responseBody.contentLength()))); } return bytesRead; } }; } } }