com.c4mprod.utils.ImageManager.java Source code

Java tutorial

Introduction

Here is the source code for com.c4mprod.utils.ImageManager.java

Source

/*******************************************************************************
 * This file is part of the C4MAndroidImageManager project.
 * 
 * Copyright (c) 2012 C4M PROD.
 * 
 * C4MAndroidImageManager is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * C4MAndroidImageManager 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with C4MAndroidImageManager. If not, see <http://www.gnu.org/licenses/lgpl.html>.
 * 
 * Contributors:
 * C4M PROD - initial API and implementation
 ******************************************************************************/
package com.c4mprod.utils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Locale;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.http.AndroidHttpClient;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.text.TextUtils;
import android.util.FloatMath;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;

public class ImageManager {
    private static final int CACHE_SIZE = 4 * 1024 * 1024; // 4Mib
    private static final long SD_CACHE_SIZE = 50 * 1024 * 1024; // 50Mib
    public static final int BITMAP_MAX_HEIGHT = 480;
    public static final int BITMAP_MAX_WIDTH = 800;
    public static final int THUMB_MAX_HEIGHT = 200;

    public static final int FLAG_ROUNDED_CORNERS = 1;
    public static final int FLAG_GET_THUMBNAIL = 1 << 1;
    public static final int FLAG_NO_THUMBNAIL = 1 << 2;
    public static final int FLAG_IN_BACKGROUND = 1 << 3;

    private static final int MSG_LOAD_FROM_SD = 0;
    private static final int MSG_DOWNLOAD = 1;
    private static final int MSG_STOP = 2;
    private static final String THUMB_FOLDER = "/thumb";
    private static ImageManager instance;

    private LooperThread mDowloadLooper;
    private final LruCache<String, Bitmap> mImageLiveCache = new LruCache<String, Bitmap>(CACHE_SIZE);
    private final Handler mUiHandler;

    private boolean mStopped = false;
    SimpleDateFormat mDateFormater = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.UK);
    static Context mContext;

    private final SparseArray<Bitmap> mDefaultImageCache = new SparseArray<Bitmap>(2);

    // private ImageDownloaderListener mListener;

    public static interface ImageDownloaderListener {
        void onImageDownloaded(View v, Bitmap bmp);
    }

    private ImageManager(final Context ctx) {

        instance = this;
        mDowloadLooper = new LooperThread();
        mDowloadLooper.start();
        mUiHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                if (mStopped) {
                    return;
                }
                ImageDownloadMessageData messageData = (ImageDownloadMessageData) msg.obj;
                View v = messageData.viewRef.get();
                if (v != null && messageData.bitmap != null
                        && messageData == getImageDownloadData(v, messageData.flags)) {
                    if (messageData.listerner != null) {
                        messageData.listerner.onImageDownloaded(v, messageData.bitmap);
                    } else {
                        if (v instanceof ImageView) {
                            if ((messageData.flags & FLAG_IN_BACKGROUND) != 0) {
                                BitmapDrawable bd = new BitmapDrawable(ctx.getResources(), messageData.bitmap);
                                v.setBackgroundDrawable(bd);
                            } else {
                                ((ImageView) v).setImageBitmap(messageData.bitmap);
                            }
                        } else if (v instanceof ImageButton) {
                            if ((messageData.flags & FLAG_IN_BACKGROUND) != 0) {
                                BitmapDrawable bd = new BitmapDrawable(ctx.getResources(), messageData.bitmap);
                                v.setBackgroundDrawable(bd);
                            } else {
                                ((ImageButton) v).setImageBitmap(messageData.bitmap);
                            }
                        } else { // no src
                            BitmapDrawable bd = new BitmapDrawable(ctx.getResources(), messageData.bitmap);
                            v.setBackgroundDrawable(bd);
                        }

                    }
                }
            };
        };

        // create cache dir if needed
        new File(ctx.getExternalCacheDir() + THUMB_FOLDER).mkdirs();
    }

    public static boolean hasInstance() {
        return instance != null;
    }

    // public void clearListener() {
    // mListener = null;
    // }
    /**
     * this method don't change the default image (transparent if never change)
     * 
     * @param ctx
     * @return the instance of ImageManager
     */
    public static ImageManager getInstance(Context ctx) {
        mContext = ctx;
        if (instance == null) {
            instance = new ImageManager(ctx);
        }
        return instance;
    }

    /**
     * Get a the default image from a resource ID.
     * 
     * @param defaultDrawableId
     * @return
     */
    public Bitmap getDefaultImage(int defaultDrawableId) {
        Bitmap bitmap = mDefaultImageCache.get(defaultDrawableId);
        if (bitmap == null) {
            bitmap = BitmapFactory.decodeResource(mContext.getResources(), defaultDrawableId);
            mDefaultImageCache.put(defaultDrawableId, bitmap);
        }
        return bitmap;
    }

    /**
     * 
     * @param url
     *            url of the image to download
     * @param view
     *            image container (put in background if is not an ImageView)
     * @param flags
     * @param checkNewVersionDelay
     *            delay between two checks of the image in milliseconds
     * @param defaultImageId
     *            TODO
     */
    public void download(String url, View view, int flags, long checkNewVersionDelay, int defaultImageId) {
        download(null, url, view, flags, checkNewVersionDelay, defaultImageId);
    }

    /**
     * 
     * @param listener
     *            call when download is finished
     * @param url
     *            url of the image to download
     * @param view
     *            image container (put in background if is not an ImageView)
     * @param flags
     * @param checkNewVersionDelay
     *            delay between two checks of the image in milliseconds
     * @param defaultImageId
     *            TODO
     * @param defaultDrawableId
     */
    public void download(ImageDownloaderListener listener, final String url, View view, int flags,
            long checkNewVersionDelay, int defaultImageId) {
        if (mStopped || TextUtils.isEmpty(url)) {
            return;
        }
        // mListener = listener;

        // get image from cache
        Bitmap cachedBitmap = null;
        if ((flags & FLAG_GET_THUMBNAIL) != 0) {
            cachedBitmap = mImageLiveCache.get(url + THUMB_FOLDER);
        } else {
            cachedBitmap = mImageLiveCache.get(url);
        }

        ImageDownloadMessageData messageData = new ImageDownloadMessageData(url, view, defaultImageId);
        messageData.listerner = listener;
        DownloadedDrawable downloadedDrawable;
        downloadedDrawable = new DownloadedDrawable(messageData, mContext.getResources(),
                cachedBitmap != null ? cachedBitmap : getDefaultImage(defaultImageId));

        if (view != null) {
            if (view instanceof ImageView) {
                if ((flags & FLAG_IN_BACKGROUND) != 0) {
                    view.setBackgroundDrawable(downloadedDrawable);
                } else {
                    ((ImageView) view).setImageDrawable(downloadedDrawable);
                }

            } else if (view instanceof ImageButton) {
                if ((flags & FLAG_IN_BACKGROUND) != 0) {
                    view.setBackgroundDrawable(downloadedDrawable);
                } else {
                    ((ImageButton) view).setImageDrawable(downloadedDrawable);
                }
            } else {
                view.setBackgroundDrawable(downloadedDrawable);
            }
        }

        if (cachedBitmap != null) {
            // Log.d("test", "version form cache");
            Message respMessage = new Message();
            messageData.bitmap = cachedBitmap;
            messageData.flags = flags;
            respMessage.obj = messageData;
            mUiHandler.sendMessage(respMessage);
        } else {
            messageData.flags = flags;

            // check if available from sd card
            if (isSDCacheReadable()) {
                File extCacheDir = mContext.getExternalCacheDir();
                final File img = new File(extCacheDir, md5(url));
                final Long currentDate = Calendar.getInstance().getTimeInMillis();

                if (img.exists()) {

                    Message msg = new Message();
                    msg.obj = messageData;
                    msg.what = MSG_LOAD_FROM_SD;
                    mDowloadLooper.enqueueMessage(msg, true);
                    // Log.d("test", "version form sdcard");
                    if (img.lastModified() + checkNewVersionDelay < currentDate) {
                        final ImageDownloadMessageData test = messageData;
                        Thread thread = new Thread(new Runnable() {

                            @Override
                            public void run() {

                                Message msg = new Message();
                                msg.obj = test;
                                URL connexion;
                                try {

                                    connexion = new URL(url);
                                    URLConnection urlConnection;
                                    urlConnection = connexion.openConnection();
                                    urlConnection.connect();
                                    String date = urlConnection.getHeaderField("Last-Modified");
                                    if (img.lastModified() >= mDateFormater.parse(date).getTime()) {
                                        // Log.d("test", "version form sdcard after server check");
                                        return;
                                    } else {
                                        // load from web
                                        // Log.d("test", "version form server");
                                        msg.what = MSG_DOWNLOAD;
                                        mDowloadLooper.enqueueMessage(msg, false);
                                    }
                                    urlConnection = null;
                                } catch (Exception e) {
                                    return;
                                }

                            }
                        });
                        thread.start();

                    }
                } else {
                    Message msg = new Message();
                    msg.obj = messageData;
                    // load from web
                    // Log.d("test", "version form server");
                    msg.what = MSG_DOWNLOAD;
                    mDowloadLooper.enqueueMessage(msg, false);
                }
            }

        }
    }

    private boolean isSDCacheReadable() {
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            return true;
        } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
            return true;
        } else {
            return false;
        }
    }

    private boolean isSDCacheWritable() {
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            return true;
        } else {
            return false;
        }
    }

    private static ImageDownloadMessageData getImageDownloadData(View view, int flags) {
        if (view != null) {
            Drawable drawable = null;
            if (view instanceof ImageView) {
                if ((flags & FLAG_IN_BACKGROUND) != 0) {
                    drawable = view.getBackground();
                } else {
                    drawable = ((ImageView) view).getDrawable();
                }
            } else if (view instanceof ImageButton) {
                if ((flags & FLAG_IN_BACKGROUND) != 0) {
                    drawable = view.getBackground();
                } else {
                    drawable = ((ImageView) view).getDrawable();
                }
            } else {
                drawable = view.getBackground();
            }
            if (drawable instanceof DownloadedDrawable) {
                DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable;
                return downloadedDrawable.getImageDownloadData();
            }
        }
        return null;
    }

    public static void release() {
        instance.mStopped = true;
        instance.mDowloadLooper.stopLooper();
        // for (Bitmap bmp : instance.mImageLiveCache.snapshot().values()) {
        // bmp.recycle();
        // }
        instance.mImageLiveCache.evictAll();
        instance = null;
    }

    private static class ImageDownloadMessageData {
        public String url;
        public Bitmap bitmap;
        public final WeakReference<View> viewRef;
        public int flags;
        public ImageDownloaderListener listerner;

        public ImageDownloadMessageData(String url, View view, int defaultImageId) {
            this.url = url;
            viewRef = new WeakReference<View>(view);
        }
    }

    private class LooperThread extends Thread {
        public Handler mHandler;

        @Override
        public void run() {
            Looper.prepare();

            synchronized (instance) {
                mHandler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        if (msg.what == MSG_STOP) {
                            Looper.myLooper().quit();
                            return;
                        }
                        // separate from stop message to do the looper quit once
                        if (mStopped) {
                            return;
                        }
                        ImageDownloadMessageData messageData = (ImageDownloadMessageData) msg.obj;

                        boolean thumbMode = (messageData.flags & FLAG_GET_THUMBNAIL) != 0;

                        // check cache in case the dowload has already be done
                        messageData.bitmap = mImageLiveCache.get(messageData.url);

                        if (messageData.bitmap == null) {
                            switch (msg.what) {
                            case MSG_LOAD_FROM_SD: {

                                // read from SD
                                View v = messageData.viewRef.get();
                                if (v != null) {
                                    File cacheDir = mContext.getExternalCacheDir();
                                    String fileName = md5(messageData.url);
                                    File img;
                                    if (thumbMode) {
                                        img = new File(cacheDir + THUMB_FOLDER, fileName);
                                    } else {
                                        img = new File(cacheDir, fileName);
                                    }
                                    // messageData.bitmap = shrinkBitmap(img.getPath(), BITMAP_MAX_WIDTH, BITMAP_MAX_HEIGHT);
                                    messageData.bitmap = BitmapFactory.decodeFile(img.getPath());
                                } else {
                                    // view has been garbaged
                                    return;
                                }
                                break;
                            }
                            case MSG_DOWNLOAD: {

                                // download bitmap
                                Bitmap bitmap = downloadBitmap(messageData.url);
                                Bitmap thumbBmp = null;

                                // save to SD cache
                                if (bitmap != null && isSDCacheWritable()) {
                                    View v = messageData.viewRef.get();
                                    if (v != null) {
                                        File cacheDir = mContext.getExternalCacheDir();
                                        File img = new File(cacheDir, md5(messageData.url));
                                        File thumb = new File(cacheDir + THUMB_FOLDER, md5(messageData.url));

                                        try {
                                            // make rounded corners
                                            if ((messageData.flags & FLAG_ROUNDED_CORNERS) != 0) {
                                                // Log.d("ImprovedImageDownloader.handleMessage():", "create rounded corners");
                                                bitmap = getRoundedCornerBitmap(bitmap);
                                            }

                                            // save image to SD
                                            if (img.createNewFile()) {
                                                OutputStream out = new FileOutputStream(img);
                                                bitmap.compress(Bitmap.CompressFormat.PNG, 80, out);
                                                out.flush();
                                                out.close();
                                            }

                                            // make thumnail if needed
                                            if ((messageData.flags & FLAG_NO_THUMBNAIL) == 0) {
                                                // create thumb
                                                double ratio = (double) THUMB_MAX_HEIGHT
                                                        / (double) bitmap.getHeight();
                                                thumbBmp = Bitmap.createScaledBitmap(bitmap,
                                                        (int) (bitmap.getWidth() * ratio),
                                                        (int) (bitmap.getHeight() * ratio), true);
                                                // recycle image if not needed.
                                                if (thumbMode && thumbBmp != bitmap) {
                                                    bitmap.recycle();
                                                }
                                                // save thumb to SD
                                                if (thumb.createNewFile()) {
                                                    OutputStream out = new FileOutputStream(thumb);
                                                    thumbBmp.compress(Bitmap.CompressFormat.PNG, 80, out);
                                                    out.flush();
                                                    out.close();
                                                } else {
                                                    // Log.d("ImprovedImageDownloader.LooperThread.run():", "failed to create thumb");
                                                }
                                                // recycle image if not needed
                                                if (!thumbMode && thumbBmp != bitmap) {
                                                    thumbBmp.recycle();
                                                }
                                            }

                                        } catch (FileNotFoundException e) {
                                            e.printStackTrace();
                                        } catch (IOException e) {
                                            e.printStackTrace();
                                        }
                                    }
                                }

                                // store bitmap
                                messageData.bitmap = thumbMode ? thumbBmp : bitmap;
                                cleanupSdCard();
                                break;
                            }

                            default:
                                break;
                            }

                            if (messageData.bitmap == null) {
                                return;
                            }

                            // add to live cache
                            String key;
                            if (thumbMode) {
                                key = messageData.url + THUMB_FOLDER;
                            } else {
                                key = messageData.url;
                            }
                            mImageLiveCache.put(key, messageData.bitmap);
                        }

                        // send message to ui handler to refresh view
                        Message respMessage = new Message();
                        respMessage.obj = messageData;
                        mUiHandler.sendMessage(respMessage);
                    }
                };

                instance.notifyAll();
            }

            Looper.loop();
        }

        public void stopLooper() {
            Message msg = new Message();
            msg.what = MSG_STOP;
            mHandler.sendMessageAtFrontOfQueue(msg);
        }

        public void enqueueMessage(Message msg, boolean priorityMessage) {
            // for paranoia only, seem to be useless now...
            if (mHandler == null) {
                synchronized (instance) {
                    try {
                        instance.wait(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            if (priorityMessage) {
                mHandler.sendMessageAtFrontOfQueue(msg);
            } else {
                mHandler.sendMessage(msg);
            }
        }

        private Bitmap downloadBitmap(String url) {

            final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
            final HttpGet getRequest = new HttpGet(url);

            try {
                HttpResponse response = client.execute(getRequest);
                final int statusCode = response.getStatusLine().getStatusCode();

                if (statusCode != HttpStatus.SC_OK) {
                    Header[] headers = response.getHeaders("Location");

                    if (headers != null && headers.length > 0) {
                        String newUrl = headers[headers.length - 1].getValue();
                        // call again with new URL
                        return downloadBitmap(newUrl);
                    } else {
                        Log.w("ImageDownloader", "Error " + statusCode + " while retrieving bitmap from " + url);
                        return null;
                    }
                }

                final HttpEntity entity = response.getEntity();
                if (entity != null) {
                    InputStream inputStream = null;
                    try {
                        inputStream = entity.getContent();
                        // final Bitmap bitmap = shrinkBitmap(new FlushedInputStream(inputStream), BITMAP_MAX_WIDTH, BITMAP_MAX_HEIGHT);
                        final Bitmap bitmap = BitmapFactory.decodeStream(new FlushedInputStream(inputStream));
                        return bitmap;
                    } finally {
                        if (inputStream != null) {
                            inputStream.close();
                        }
                        entity.consumeContent();
                    }
                }
            } catch (Exception e) {
                // Could provide a more explicit error message for IOException or IllegalStateException
                getRequest.abort();
                Log.w("ImageDownloader", "Error while retrieving bitmap from " + url, e);
            } finally {
                if (client != null) {
                    client.close();
                }
            }
            return null;
        }
    }

    public static Bitmap getRoundedCornerBitmap(Bitmap bitmap) {

        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);
        final float roundPx = 20;

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);

        if (output != bitmap) {
            bitmap.recycle();
        }
        return output;
    }

    public static Bitmap shrinkBitmap(String file, int width, int height) {
        try {
            BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
            bmpFactoryOptions.inJustDecodeBounds = true;
            bmpFactoryOptions.inPurgeable = true;
            bmpFactoryOptions.inInputShareable = true;
            Bitmap bitmap = BitmapFactory.decodeFile(file, bmpFactoryOptions);
            computeRatio(width, height, bmpFactoryOptions);
            bmpFactoryOptions.inJustDecodeBounds = false;
            bitmap = BitmapFactory.decodeFile(file, bmpFactoryOptions);
            return bitmap;
        } catch (OutOfMemoryError e) {
            // Log.d("ImprovedImageDownloader.shrinkBitmap():", "memory !");
            return null;
        }
    }

    public static Bitmap shrinkBitmap(InputStream stream, int width, int height) {
        try {
            // TODO check for a better solution for handling purgation
            BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
            Bitmap bitmap = BitmapFactory.decodeStream(stream, null, bmpFactoryOptions);
            computeRatio(width, height, bmpFactoryOptions);

            if (bitmap != null) {
                final int ratio = bmpFactoryOptions.inSampleSize;
                Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, bmpFactoryOptions.outWidth / ratio,
                        bmpFactoryOptions.outHeight / ratio, false);
                if (scaledBitmap != bitmap) {
                    bitmap.recycle();
                }
                return scaledBitmap;
            }
            return null;
        } catch (OutOfMemoryError e) {
            // Log.d("ImprovedImageDownloader.shrinkBitmap():", "memory !");
            return null;
        }
    }

    private static void computeRatio(int width, int height, BitmapFactory.Options bmpFactoryOptions) {
        int heightRatio;
        int widthRatio;
        if (bmpFactoryOptions.outHeight > bmpFactoryOptions.outWidth) {
            heightRatio = (int) FloatMath.floor(bmpFactoryOptions.outHeight / (float) width);
            widthRatio = (int) FloatMath.floor(bmpFactoryOptions.outWidth / (float) height);
        } else {
            heightRatio = (int) FloatMath.floor(bmpFactoryOptions.outHeight / (float) height);
            widthRatio = (int) FloatMath.floor(bmpFactoryOptions.outWidth / (float) width);
        }

        if (heightRatio > 1 || widthRatio > 1) {
            if (heightRatio < widthRatio) {
                bmpFactoryOptions.inSampleSize = widthRatio;
            } else {
                bmpFactoryOptions.inSampleSize = heightRatio;
            }
        } else {
            bmpFactoryOptions.inSampleSize = 1;
        }
    }

    static Context t;

    private static class DownloadedDrawable extends BitmapDrawable {
        private final WeakReference<ImageDownloadMessageData> imageDownloadDataReference;

        public DownloadedDrawable(ImageDownloadMessageData imageDownloadData, Resources resources, Bitmap bitmap) {

            super(resources, bitmap);
            imageDownloadDataReference = new WeakReference<ImageDownloadMessageData>(imageDownloadData);
        }

        public ImageDownloadMessageData getImageDownloadData() {
            return imageDownloadDataReference.get();
        }
    }

    private static class FlushedInputStream extends FilterInputStream {
        public FlushedInputStream(InputStream inputStream) {
            super(inputStream);
        }

        @Override
        public long skip(long n) throws IOException {
            long totalBytesSkipped = 0L;
            while (totalBytesSkipped < n) {
                long bytesSkipped = in.skip(n - totalBytesSkipped);
                if (bytesSkipped == 0L) {
                    int readByte = read();
                    if (readByte < 0) {
                        break; // we reached EOF
                    } else {
                        bytesSkipped = 1; // we read one byte
                    }
                }
                totalBytesSkipped += bytesSkipped;
            }
            return totalBytesSkipped;
        }
    }

    public static String md5(String s) {
        try {
            // Create MD5 Hash
            MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
            digest.update(s.getBytes());
            byte messageDigest[] = digest.digest();

            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            for (int i = 0; i < messageDigest.length; i++) {
                hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
            }
            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    private void cleanupSdCard() {
        File cacheDir = mContext.getExternalCacheDir();
        File[] files = cacheDir.listFiles();

        Arrays.sort(files, new Comparator<File>() {
            @Override
            public int compare(File f1, File f2) {
                return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
            }
        });
        long size = getImagesSize(files);
        int i = 0;
        while (size > SD_CACHE_SIZE && i < files.length) {
            File f = files[i];
            if (!f.getName().startsWith(".")) {
                size -= f.length();
                f.delete();
            }
            i++;
        }

    }

    private long getImagesSize(File[] files) {
        long size = 0;
        for (File file : files) {
            if (!file.getName().startsWith(".")) {
                size += file.length();
            }
        }
        return size;
    }

    public static Bitmap quickCachedImage(String url) {
        if (instance != null && url != null) {
            Bitmap cachedBitmap = instance.mImageLiveCache.get(url);
            if (cachedBitmap != null && !cachedBitmap.isRecycled()) {
                return cachedBitmap.copy(Config.ARGB_8888, false);
            }
        }
        return null;
    }

}