vn.evolus.droidreader.util.ImageCache.java Source code

Java tutorial

Introduction

Here is the source code for vn.evolus.droidreader.util.ImageCache.java

Source

/* Copyright (c) 2009 Matthias Kppler
 *
 * 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 vn.evolus.droidreader.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

import vn.evolus.droidreader.Constants;
import vn.evolus.droidreader.Settings;
import vn.evolus.droidreader.content.ContentManager;
import vn.evolus.droidreader.model.Image;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.CompressFormat;
import android.os.Environment;
import android.util.Log;

import com.google.common.collect.MapMaker;

/**
 * <p>
 * A simple 2-level cache for bitmap images consisting of a small and fast
 * in-memory cache (1st level cache) and a slower but bigger disk cache (2nd
 * level cache). For second level caching, the application's cache directory
 * will be used. Please note that Android may at any point decide to wipe that
 * directory.
 * </p>
 * <p>
 * When pulling from the cache, it will first attempt to load the image from
 * memory. If that fails, it will try to load it from disk. If that succeeds,
 * the image will be put in the 1st level cache and returned. Otherwise it's a
 * cache miss, and the caller is responsible for loading the image from
 * elsewhere (probably the Internet).
 * </p>
 * <p>
 * Pushes to the cache are always write-through (i.e., the image will be stored
 * both on disk and in memory).
 * </p>
 * 
 * @author Matthias Kaeppler
 */
public class ImageCache implements Map<String, Bitmap> {
    private static ImageCache instance;

    private int cachedImageQuality = 100;
    private String secondLevelCacheDir;
    private Map<String, Bitmap> cache;
    private CompressFormat compressedImageFormat = CompressFormat.PNG;

    private ImageCache(Context context, int initialCapacity, int concurrencyLevel) {
        this.cache = new MapMaker().initialCapacity(initialCapacity).concurrencyLevel(concurrencyLevel).weakValues()
                .makeMap();
        this.secondLevelCacheDir = Environment.getExternalStorageDirectory().getAbsolutePath()
                + "/droidreader/imagecache";
        new File(secondLevelCacheDir).mkdirs();
    }

    public static synchronized ImageCache createInstance(Context context, int initialCapacity,
            int concurrencyLevel) {
        instance = new ImageCache(context, initialCapacity, concurrencyLevel);
        return instance;
    }

    public static synchronized ImageCache getInstance() {
        return instance;
    }

    /**
     * @param cachedImageQuality
     *        the quality of images being compressed and written to disk (2nd
     *        level cache) as a number in [0..100]
     */
    public void setCachedImageQuality(int cachedImageQuality) {
        this.cachedImageQuality = cachedImageQuality;
    }

    public int getCachedImageQuality() {
        return cachedImageQuality;
    }

    public synchronized Bitmap get(Object key) {
        String imageUrl = (String) key;
        Bitmap bitmap = cache.get(imageUrl);

        if (bitmap != null) {
            // 1st level cache hit (memory)
            return bitmap;
        }

        File imageFile = getImageFile(imageUrl);
        if (imageFile.exists()) {
            // 2nd level cache hit (disk)
            try {
                bitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath());
            } catch (Throwable e) {
            }

            if (bitmap == null) {
                // treat decoding errors as a cache miss
                return null;
            }
            cache.put(imageUrl, bitmap);
            return bitmap;
        }

        // cache miss
        return null;
    }

    public Bitmap put(String imageUrl, Bitmap image) {
        File imageFile = getImageFile(imageUrl);
        try {
            imageFile.createNewFile();
            FileOutputStream ostream = new FileOutputStream(imageFile);
            image.compress(compressedImageFormat, cachedImageQuality, ostream);
            ostream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return cache.put(imageUrl, image);
    }

    public void putAll(Map<? extends String, ? extends Bitmap> t) {
        throw new UnsupportedOperationException();
    }

    public boolean containsKey(Object key) {
        return cache.containsKey(key);
    }

    public boolean containsValue(Object value) {
        return cache.containsValue(value);
    }

    public Bitmap remove(Object key) {
        return cache.remove(key);
    }

    public Set<String> keySet() {
        return cache.keySet();
    }

    public Set<java.util.Map.Entry<String, Bitmap>> entrySet() {
        return cache.entrySet();
    }

    public int size() {
        return cache.size();
    }

    public boolean isEmpty() {
        return cache.isEmpty();
    }

    public void clear() {
        cache.clear();
    }

    public Collection<Bitmap> values() {
        return cache.values();
    }

    private File getImageFile(String imageUrl) {
        return new File(getCacheFileName(imageUrl));
    }

    public static void downloadImage(String imageUrl) throws IOException {
        HttpClient client = new DefaultHttpClient();
        HttpParams params = client.getParams();
        HttpConnectionParams.setConnectionTimeout(params, ImageLoader.CONNECT_TIMEOUT);
        HttpConnectionParams.setSoTimeout(params, ImageLoader.READ_TIMEOUT);
        HttpGet httpGet = new HttpGet(imageUrl);
        HttpResponse response = client.execute(httpGet);
        InputStream is = response.getEntity().getContent();

        // save to cache folder
        if (Constants.DEBUG_MODE)
            Log.d(Constants.LOG_TAG, "Save image to " + getCacheFileName(imageUrl));
        File imageFile = new File(getCacheFileName(imageUrl));
        imageFile.createNewFile();
        FileOutputStream os = null;
        try {
            os = new FileOutputStream(imageFile);
            StreamUtils.writeStream(is, os);
        } finally {
            if (os != null) {
                os.close();
            }
        }
    }

    public static String getCacheFileName(String imageUrl) {
        long imageId = 0;
        int pos = imageUrl.lastIndexOf('#');
        if (pos > 0) {
            try {
                imageId = Integer.parseInt(imageUrl.substring(pos + 1));
            } catch (NumberFormatException nfe) {
            }
        }
        if (imageId == 0) {
            Image image = ContentManager.loadImage(imageUrl);
            if (image == null)
                return "";
            imageId = image.id;
        }
        return getCacheFileName(imageId);
    }

    public static String getCacheFileName(long imageId) {
        return instance.secondLevelCacheDir + "/" + imageId;
    }

    public static boolean isCached(String imageUrl) {
        File file = new File(getCacheFileName(imageUrl));
        return file.exists();
    }

    public static void clearCacheIfNecessary() {
        List<Integer> oldestImageIds = ContentManager.loadOldestImageIds(Settings.getKeepMaxImages());
        for (int imageId : oldestImageIds) {
            try {
                File imageFile = new File(getCacheFileName(imageId));
                imageFile.delete();
                ContentManager.deleteImage(imageId);
            } catch (RuntimeException e) {
            }
        }
    }

    public static void clearCacheFolder() {
        File cacheDir = new File(instance.secondLevelCacheDir);
        if (!cacheDir.exists())
            return;

        File[] files = cacheDir.listFiles();
        if (files != null) {
            for (File file : files) {
                try {
                    file.delete();
                } catch (Throwable t) {
                }
            }
        }
    }
}