de.s2hmobile.bitmaps.ImageLoader.java Source code

Java tutorial

Introduction

Here is the source code for de.s2hmobile.bitmaps.ImageLoader.java

Source

/* File modified by S2H Mobile, 2013.
 * 
 * Copyright (C) 2012 The Android Open Source Project
 *
 * 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 de.s2hmobile.bitmaps;

import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v4.app.FragmentManager;
import android.widget.ImageView;
import de.s2hmobile.bitmaps.framework.AsyncTask;

/**
 * This class wraps up completing some arbitrary long running work when loading
 * a bitmap to an ImageView. It handles things like using a memory and disk
 * cache, running the work in a background thread and setting a placeholder
 * image.
 */
public class ImageLoader {

    protected class CacheAsyncTask extends AsyncTask<Integer, Void, Void> {

        @Override
        protected Void doInBackground(final Integer... params) {
            try {
                switch (params[0]) {
                case MESSAGE_CLEAR:
                    clearCacheInternal();
                    break;
                case MESSAGE_INIT_DISK_CACHE:
                    initDiskCacheInternal();
                    break;
                case MESSAGE_FLUSH:
                    flushCacheInternal();
                    break;
                case MESSAGE_CLOSE:
                    closeCacheInternal();
                    break;
                }
            } catch (final IOException e) {
            }
            return null;
        }
    }

    /**
     * A custom Drawable that will be attached to the image view while the work
     * is in progress. Contains a reference to the actual worker task, so that
     * it can be stopped if a new binding is required, and makes sure that only
     * the last started worker process can bind its result, independently of the
     * finish order.
     */
    private static class AsyncDrawable extends BitmapDrawable {

        private final WeakReference<BitmapWorkerTask> mBitmapTaskReference;

        public AsyncDrawable(final Resources res, final Bitmap bitmap, final BitmapWorkerTask bitmapWorkerTask) {
            super(res, bitmap);
            mBitmapTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
        }

        public BitmapWorkerTask getBitmapWorkerTask() {
            return mBitmapTaskReference.get();
        }
    }

    private static final int MESSAGE_CLEAR = 0x10;
    private static final int MESSAGE_CLOSE = 0x13;
    private static final int MESSAGE_FLUSH = 0x12;
    private static final int MESSAGE_INIT_DISK_CACHE = 0x11;

    // protected boolean mPauseWork = false;
    // private boolean mExitTasksEarly = false;

    private ImageCache mImageCache = null;
    private Bitmap mLoadingBitmap = null;

    // private final Object mPauseWorkLock = new Object();

    private final Resources mResources;

    public ImageLoader(final Resources resources) {
        mResources = resources;
    }

    /**
     * Adds a cache to handle disk and memory bitmap caching.
     * 
     * @param fm
     *            - to handle the retain fragment
     * @param params
     *            - for the disk cache
     * @param fraction
     *            - the memory fraction to use for the cache
     */
    public void addCache(final FragmentManager fm, final Context context, final String directory, final int size,
            final int fraction) {

        DiskCacheParams params = null;
        try {
            params = new DiskCacheParams(context, directory, size);
        } catch (final IOException e) {
        }

        mImageCache = ImageCache.getInstance(fm, params, fraction);
        new CacheAsyncTask().execute(MESSAGE_INIT_DISK_CACHE);
    }

    public void clearCache() {
        new CacheAsyncTask().execute(MESSAGE_CLEAR);
    }

    public void closeCache() {
        new CacheAsyncTask().execute(MESSAGE_CLOSE);
    }

    public void flushCache() {
        new CacheAsyncTask().execute(MESSAGE_FLUSH);
    }

    public void loadBitmapFromFile(final ImageView imageView, final File file, final int targetWidth,
            final int targetHeight) {
        // check for file
        if (file == null || !file.exists()) {
            return;
        }
        final String path = file.getAbsolutePath();

        // TODO make key a parameter
        final String key = new StringBuilder().append(path).append("_").append(targetWidth).append("_")
                .append(targetHeight).toString();

        final BitmapDrawable drawable = mImageCache == null ? null : mImageCache.getBitmapDrawableFromMemCache(key);

        if (drawable != null) {

            // bitmap found in memory cache
            imageView.setImageDrawable(drawable);

        } else if (cancelPotentialWork(key, imageView)) {

            // instantiate the task
            final BitmapFileTask task = new BitmapFileTask(imageView, key, mResources, mImageCache, path);

            // set a loading indicator as background
            final AsyncDrawable placeHolder = new AsyncDrawable(mResources, mLoadingBitmap, task);
            imageView.setImageDrawable(placeHolder);

            // start the task with parameters
            final Integer[] params = { targetWidth, targetHeight };
            task.executeOnExecutor(AsyncTask.DUAL_THREAD_EXECUTOR, params);
        }
    }

    /**
     * Load an image specified by the data parameter into an ImageView (override
     * {@link ImageWorker#processBitmap(Object)} to define the processing
     * logic). A memory and disk cache will be used if an {@link ImageCache} has
     * been added using
     * {@link ImageWorker#addImageCache(FragmentManager, DiskCacheParams)}. If
     * the image is found in the memory cache, it is set immediately, otherwise
     * an {@link AsyncTask} will be created to asynchronously load the bitmap.
     * 
     * @param resId
     *            - the URL of the image to download
     * @param imageView
     *            - the ImageView to bind the downloaded image to
     */
    public void loadImageFromResource(final ImageView imageView, final int resId, final int targetWidth,
            final int targetHeight) {
        if (resId == 0) {
            return;
        }

        // TODO make key a parameter
        final String key = new StringBuilder().append(resId).append("_").append(targetWidth).append("_")
                .append(targetHeight).toString();

        final BitmapDrawable drawable = mImageCache == null ? null : mImageCache.getBitmapDrawableFromMemCache(key);

        if (drawable != null) {

            // bitmap found in memory cache
            imageView.setImageDrawable(drawable);

        } else if (cancelPotentialWork(key, imageView)) {

            final BitmapResourceTask task = new BitmapResourceTask(imageView, key, mResources, mImageCache, resId);

            // set a loading indicator as background
            final AsyncDrawable placeHolder = new AsyncDrawable(mResources, mLoadingBitmap, task);
            imageView.setImageDrawable(placeHolder);

            // start the task with parameters
            final Integer[] params = { targetWidth, targetHeight };
            task.executeOnExecutor(AsyncTask.DUAL_THREAD_EXECUTOR, params);
        }
    }

    // public void setExitTasksEarly(final boolean exitTasksEarly) {
    // mExitTasksEarly = exitTasksEarly;
    // setPauseWork(false);
    // }

    /**
     * Set placeholder bitmap that shows when the the background thread is
     * running.
     * 
     * @param bitmap
     */
    public void setLoadingImage(final Bitmap bitmap) {
        mLoadingBitmap = bitmap;
    }

    /**
     * Set placeholder bitmap that shows when the the background thread is
     * running.
     * 
     * @param resId
     */
    public void setLoadingImage(final int resId) {
        mLoadingBitmap = BitmapFactory.decodeResource(mResources, resId);
    }

    /**
     * Pause any ongoing background work. This can be used as a temporary
     * measure to improve performance. For example background work could be
     * paused when a ListView or GridView is being scrolled using a
     * {@link android.widget.AbsListView.OnScrollListener} to keep scrolling
     * smooth.
     * <p>
     * If work is paused, be sure setPauseWork(false) is called again before
     * your fragment or activity is destroyed (for example during
     * {@link android.app.Activity#onPause()}), or there is a risk the
     * background thread will never finish.
     */
    // public void setPauseWork(final boolean pauseWork) {
    // synchronized (mPauseWorkLock) {
    // mPauseWork = pauseWork;
    // if (!mPauseWork) {
    // mPauseWorkLock.notifyAll();
    // }
    // }
    // }

    protected void clearCacheInternal() throws IOException {
        if (mImageCache != null) {
            mImageCache.clearCache();
        }
    }

    protected void closeCacheInternal() throws IOException {
        if (mImageCache != null) {
            mImageCache.close();
            mImageCache = null;
        }
    }

    protected void flushCacheInternal() throws IOException {
        if (mImageCache != null) {
            mImageCache.flush();
        }
    }

    /**
     * @return The {@link ImageCache} object currently being used by this
     *         ImageWorker.
     */
    protected ImageCache getImageCache() {
        return mImageCache;
    }

    protected void initDiskCacheInternal() throws IOException {
        if (mImageCache != null) {
            mImageCache.initDiskCache();
        }
    }

    /**
     * Cancels any pending work attached to the provided ImageView.
     * 
     * @param imageView
     */
    public static void cancelWork(final ImageView imageView) {
        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
        if (bitmapWorkerTask != null) {
            bitmapWorkerTask.cancel(true);
        }
    }

    /**
     * Returns true if the current work has been canceled or if there was no
     * work in progress on this image view. Returns false if the work in
     * progress deals with the same data. The work is not stopped in that case.
     */
    static boolean cancelPotentialWork(final String key, final ImageView imageView) {

        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
        if (bitmapWorkerTask == null) {
            return true;
        }

        // cancel task
        final String bitmapData = bitmapWorkerTask.getKey();
        if (bitmapData == null || !bitmapData.equals(key)) {
            bitmapWorkerTask.cancel(true);
            return true;
        }

        // The same work is already in progress.
        return false;
    }

    /**
     * Retrieves the currently active work task (if any) associated with this
     * view. Returns null if there is no such task.
     */
    protected static BitmapWorkerTask getBitmapWorkerTask(final ImageView imageView) {
        if (imageView == null) {
            return null;
        }

        final Drawable drawable = imageView.getDrawable();
        if (!(drawable instanceof AsyncDrawable)) {
            return null;
        }

        final AsyncDrawable placeHolder = (AsyncDrawable) drawable;
        return placeHolder.getBitmapWorkerTask();
    }
}