Java tutorial
/* * Copyright 2014 Uwe Trottmann * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.battlelancer.seriesguide.util; import android.content.Context; import android.net.Uri; import android.os.StatFs; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.OkUrlFactory; import com.squareup.picasso.Downloader; import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; /** * Custom {@link com.squareup.picasso.OkHttpDownloader} that loads only from local cache if user * wishes to conserve mobile data. */ public class LocalOnlyOkHttpDownloader implements Downloader { static final int DEFAULT_READ_TIMEOUT = 20 * 1000; // 20s static final int DEFAULT_CONNECT_TIMEOUT = 15 * 1000; // 15s private static final String PICASSO_CACHE = "picasso-cache"; private static final int MIN_DISK_CACHE_SIZE = 5 * 1024 * 1024; // 5MB private static final int MAX_DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB static final String RESPONSE_SOURCE_ANDROID = "X-Android-Response-Source"; static final String RESPONSE_SOURCE_OKHTTP = "OkHttp-Response-Source"; private final Context context; private final OkUrlFactory urlFactory; public LocalOnlyOkHttpDownloader(final Context context) { this(context, createDefaultCacheDir(context)); } public LocalOnlyOkHttpDownloader(final Context context, final File cacheDir) { this(context, cacheDir, calculateDiskCacheSize(cacheDir)); } private LocalOnlyOkHttpDownloader(final Context context, final File cacheDir, final long maxSize) { this.context = context.getApplicationContext(); this.urlFactory = new OkUrlFactory(new OkHttpClient()); try { urlFactory.client().setCache(new com.squareup.okhttp.Cache(cacheDir, maxSize)); } catch (IOException ignored) { } } protected HttpURLConnection openConnection(Uri uri) throws IOException { HttpURLConnection connection = urlFactory.open(new URL(uri.toString())); connection.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT); connection.setReadTimeout(DEFAULT_READ_TIMEOUT); return connection; } protected OkHttpClient getClient() { return urlFactory.client(); } @Override public Response load(Uri uri, boolean localCacheOnly) throws IOException { if (!Utils.isAllowedLargeDataConnection(context, false)) { localCacheOnly = true; } HttpURLConnection connection = openConnection(uri); connection.setUseCaches(true); if (localCacheOnly) { // only-if-cached: do not go over network // max-age: accept cached responses up to one year of age (rec. max. value) // max-stale: accept stale images of any age connection.setRequestProperty("Cache-Control", "only-if-cached,max-age=31536000,max-stale=31536000"); } int responseCode = connection.getResponseCode(); if (responseCode >= 300) { connection.disconnect(); throw new ResponseException(responseCode + " " + connection.getResponseMessage()); } String responseSource = connection.getHeaderField(RESPONSE_SOURCE_OKHTTP); if (responseSource == null) { responseSource = connection.getHeaderField(RESPONSE_SOURCE_ANDROID); } long contentLength = connection.getHeaderFieldInt("Content-Length", -1); boolean fromCache = parseResponseSourceHeader(responseSource); return new Response(connection.getInputStream(), fromCache, contentLength); } static File createDefaultCacheDir(Context context) { File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE); if (!cache.exists()) { //noinspection ResultOfMethodCallIgnored cache.mkdirs(); } return cache; } static long calculateDiskCacheSize(File dir) { long size = MIN_DISK_CACHE_SIZE; try { StatFs statFs = new StatFs(dir.getAbsolutePath()); long available = ((long) statFs.getBlockCount()) * statFs.getBlockSize(); // Target 2% of the total space. size = available / 50; } catch (IllegalArgumentException ignored) { } // Bound inside min/max size for disk cache. return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE); } /** Returns {@code true} if header indicates the response body was loaded from the disk cache. */ static boolean parseResponseSourceHeader(String header) { if (header == null) { return false; } String[] parts = header.split(" ", 2); if ("CACHE".equals(parts[0])) { return true; } if (parts.length == 1) { return false; } try { return "CONDITIONAL_CACHE".equals(parts[0]) && Integer.parseInt(parts[1]) == 304; } catch (NumberFormatException e) { return false; } } }