/* * Tweetings - Twitter client for Android * * Copyright (C) 2012-2013 RBD Solutions Limited <apps@tweetings.net> * Copyright (C) 2012 Mariotaku Lee <mariotaku.lee@gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.dwdesign.tweetings.util; import static com.dwdesign.tweetings.util.Utils.copyStream; import static com.dwdesign.tweetings.util.Utils.getBestCacheDir; import static com.dwdesign.tweetings.util.Utils.getImageLoaderHttpClient; import static com.dwdesign.tweetings.util.Utils.getRedirectedHttpResponse; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import com.dwdesign.tweetings.Constants; import twitter4j.internal.http.HttpClientWrapper; import twitter4j.internal.http.HttpResponse; import android.content.Context; import android.util.Log; import android.widget.GridView; import android.widget.ListView; /** * Lazy image loader for {@link ListView} and {@link GridView} etc.</br> </br> * Inspired by <a href="https://github.com/thest1/LazyList">LazyList</a>, this * class has extra features like image loading/caching image to * /mnt/sdcard/Android/data/[package name]/cache features.</br> </br> Requires * Android 2.2, you can modify {@link Context#getExternalCacheDir()} to other to * support Android 2.1 and below. * * @author mariotaku * */ public class ImagePreloader implements Constants { private static final String LOGTAG = ImagePreloader.class.getSimpleName(); private final Context mContext; private final ExecutorService mExecutor; private HttpClientWrapper mClient; public ImagePreloader(final Context context) { mContext = context; mExecutor = Executors.newFixedThreadPool(32, new LowestPriorityThreadFactory()); reloadConnectivitySettings(); } /** * Cancels any downloads, shuts down the executor pool, and then purges the * caches. */ public void cancel() { // We could also terminate it immediately, // but that may lead to synchronization issues. if (!mExecutor.isShutdown()) { mExecutor.shutdown(); } } public File getCachedImageFile(final String cache_dir_name, final String url) { if (cache_dir_name == null || url == null) return null; final File cache_dir = getBestCacheDir(mContext, cache_dir_name); if (cache_dir == null) return null; final File cache = new File(cache_dir, getFilename(url)); if (ImageValidator.checkImageValidity(cache)) return cache; else { preloadImage(cache_dir_name, url); } return null; } public void preloadImage(final String cache_dir_name, final String url) { final ImageToLoad p = new ImageToLoad(cache_dir_name, url); mExecutor.submit(new ImageLoader(p)); } public void reloadConnectivitySettings() { mClient = getImageLoaderHttpClient(mContext); } private String getFilename(final String url) { if (url == null) return null; return url.replaceFirst("https?:\\/\\/", "").replaceAll("[^a-zA-Z0-9]", "_"); } class ImageLoader implements Runnable { private final ImageToLoad imagetoload; public ImageLoader(final ImageToLoad imagetoload) { this.imagetoload = imagetoload; } @Override public void run() { if (imagetoload == null || imagetoload.cache_dir_name == null || imagetoload.url == null) return; final File cache_dir = getBestCacheDir(mContext, imagetoload.cache_dir_name); if (cache_dir == null) return; if (!cache_dir.isDirectory()) { cache_dir.mkdir(); } final File cache_file = new File(cache_dir, getFilename(imagetoload.url)); // from SD cache if (ImageValidator.checkImageValidity(cache_file)) return; // from web try { final HttpResponse resp = getRedirectedHttpResponse(mClient, imagetoload.url); if (resp != null && resp.getStatusCode() == 200) { final InputStream is = resp.asStream(); final OutputStream os = new FileOutputStream(cache_file); copyStream(is, os); os.flush(); os.close(); if (!ImageValidator.checkImageValidity(cache_file)) { // The file is corrupted, so we remove it from cache. if (cache_file.isFile() && cache_file.length() == 0) { cache_file.delete(); } } } } catch (final Exception e) { Log.w(LOGTAG, e); } } } static class ImageToLoad { public final String url; public final String cache_dir_name; public ImageToLoad(final String cache_dir_name, final String url) { this.url = url; this.cache_dir_name = cache_dir_name; } } static class LowestPriorityThreadFactory implements ThreadFactory { @Override public Thread newThread(final Runnable r) { final Thread t = new Thread(r); t.setPriority(Thread.MIN_PRIORITY); return t; } } }