com.blork.anpod.util.BitmapUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.blork.anpod.util.BitmapUtils.java

Source

/*
 * Copyright 2011 Google Inc.
 *
 * 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.blork.anpod.util;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
import java.util.regex.Pattern;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.util.EntityUtils;

import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.util.Log;

import com.blork.anpod.model.Picture;

// TODO: Auto-generated Javadoc
/**
 * Helper class for fetching and disk-caching images from the web.
 */
public class BitmapUtils {

    // TODO: for concurrent connections, DefaultHttpClient isn't great, consider other options
    // that still allow for sharing resources across bitmap fetches.

    /**
     * The listener interface for receiving onFetchComplete events.
     * The class that is interested in processing a onFetchComplete
     * event implements this interface, and the object created
     * with that class is registered with a component using the
     * component's <code>addOnFetchCompleteListener<code> method. When
     * the onFetchComplete event occurs, that object's appropriate
     * method is invoked.
     *
     * @see OnFetchCompleteEvent
     */
    public static interface OnFetchCompleteListener {

        /**
         * On fetch complete.
         *
         * @param cookie the cookie
         * @param result the result
         */
        public void onFetchComplete(BitmapResult result);
    }

    /**
     * Only call this method from the main (UI) thread. The {@link OnFetchCompleteListener} callback
     * be invoked on the UI thread, but image fetching will be done in an {@link AsyncTask}.
     *
     * @param context the context
     * @param desiredHeight 
     * @param desiredWidth 
     * @param url the url
     * @param name the name
     * @param decodeOptions the decode options
     * @param cookie An arbitrary object that will be passed to the callback.
     * @param callback the callback
     */
    public static void fetchImage(final Context context, final Picture picture, final int desiredWidth,
            final int desiredHeight, final OnFetchCompleteListener callback) {
        new AsyncTask<String, Void, BitmapResult>() {

            @Override
            protected BitmapResult doInBackground(String... params) {

                BitmapResult result = fetchImage(context, picture, desiredWidth, desiredHeight);

                return result;
            }

            @Override
            protected void onPostExecute(BitmapResult result) {
                callback.onFetchComplete(result);
            }
        }.execute(picture.getFullSizeImageUrl());
    }

    public static BitmapResult fetchImage(Context context, Picture picture, int desiredWidth, int desiredHeight) {

        // First compute the cache key and cache file path for this URL
        File cacheFile = null;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            Log.d("APOD", "creating cache file");
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);

            if (prefs.getBoolean("archive", false)) {
                cacheFile = new File(Environment.getExternalStorageDirectory() + File.separator + "APOD"
                        + File.separator + toSlug(picture.title) + ".jpg");
            } else {
                cacheFile = new File(Environment.getExternalStorageDirectory() + File.separator + "Android"
                        + File.separator + "data" + File.separator + "com.blork.anpod" + File.separator + "cache"
                        + File.separator + toSlug(picture.title) + ".jpg");
            }
        } else {
            Log.d("APOD", "SD card not mounted");
            Log.d("APOD", "creating cache file");
            cacheFile = new File(context.getCacheDir() + File.separator + toSlug(picture.title) + ".jpg");
        }

        if (cacheFile != null && cacheFile.exists()) {
            Log.d("APOD", "Cache file exists, using it.");
            try {
                Bitmap bitmap = decodeStream(new FileInputStream(cacheFile), desiredWidth, desiredHeight);
                return new BitmapResult(bitmap, Uri.fromFile(cacheFile));
            } catch (FileNotFoundException e) {
            }
        }

        try {
            Log.d("APOD", "Not cached, fetching");
            BitmapUtils.manageCache(toSlug(picture.title), context);
            // TODO: check for HTTP caching headers
            final HttpClient httpClient = SyncUtils.getHttpClient(context.getApplicationContext());
            final HttpResponse resp = httpClient.execute(new HttpGet(picture.getFullSizeImageUrl()));
            final HttpEntity entity = resp.getEntity();

            final int statusCode = resp.getStatusLine().getStatusCode();
            if (statusCode != HttpStatus.SC_OK || entity == null) {
                return null;
            }

            final byte[] respBytes = EntityUtils.toByteArray(entity);

            Log.d("APOD", "Writing cache file " + cacheFile.getName());
            try {
                cacheFile.getParentFile().mkdirs();
                cacheFile.createNewFile();
                FileOutputStream fos = new FileOutputStream(cacheFile);
                fos.write(respBytes);
                fos.close();
            } catch (FileNotFoundException e) {
                Log.d("APOD", "Error writing to bitmap cache: " + cacheFile.toString(), e);
            } catch (IOException e) {
                Log.d("APOD", "Error writing to bitmap cache: " + cacheFile.toString(), e);
            }

            // Decode the bytes and return the bitmap.
            Log.d("APOD", "Reiszing bitmap image");

            Bitmap bitmap = decodeStream(new ByteArrayInputStream(respBytes), desiredWidth, desiredHeight);
            Log.d("APOD", "Returning bitmap image");
            return new BitmapResult(bitmap, Uri.fromFile(cacheFile));
        } catch (Exception e) {
            Log.d("APOD", "Problem while loading image: " + e.toString(), e);
        }

        return null;
    }

    private static void manageCache(String slug, Context context) {
        String filename = slug + ".jpg";
        File folder;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            folder = new File(Environment.getExternalStorageDirectory() + File.separator + "Android"
                    + File.separator + "data" + File.separator + "com.blork.anpod" + File.separator + "cache");
        } else {
            folder = context.getCacheDir();
        }

        int cacheSize = 15;
        Log.w("", "current file: " + filename);
        Log.w("", "Managing cache");
        File[] files = folder.listFiles();
        if (files == null || files.length <= cacheSize) {
            Log.w("", "Cache size is fine");
            return;
        }

        int count = files.length;

        Arrays.sort(files, new Comparator<File>() {
            public int compare(File f1, File f2) {
                return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
            }
        });

        for (File f : files) {
            if (count > cacheSize && !filename.equals(f.getName())) {
                Log.w("", "Deleting " + f.getName());
                f.delete();
                count--;
            }
        }
    }

    private static final Pattern NONLATIN = Pattern.compile("[^\\w-]");
    private static final Pattern WHITESPACE = Pattern.compile("[\\s]");

    public static String toSlug(String input) {
        String nowhitespace = WHITESPACE.matcher(input).replaceAll("-");
        String slug = NONLATIN.matcher(nowhitespace).replaceAll("");
        return slug.toLowerCase(Locale.ENGLISH);
    }

    private static Bitmap decodeStream(InputStream is, int desiredWidth, int desiredHeight) {
        Bitmap b = null;
        try {
            //Decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;

            BufferedInputStream bis = new BufferedInputStream(is);
            bis.mark(Integer.MAX_VALUE);

            BitmapFactory.decodeStream(bis, null, o);
            bis.reset();

            int scale = 1;
            if (o.outHeight > desiredHeight || o.outWidth > desiredWidth) {
                scale = (int) Math.pow(2,
                        (int) Math.round(Math.log(
                                Math.max(desiredHeight, desiredWidth) / (double) Math.max(o.outHeight, o.outWidth))
                                / Math.log(0.5)));
            }

            //Decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            o2.inDither = true;
            o2.inPurgeable = true;

            b = BitmapFactory.decodeStream(bis, null, o2);
            bis.close();
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return b;
    }

    public static class BitmapResult {
        private Bitmap bitmap;
        private Uri uri;

        public BitmapResult(Bitmap bitmap, Uri uri) {
            this.bitmap = bitmap;
            this.uri = uri;
        }

        public Bitmap getBitmap() {
            return bitmap;
        }

        public Uri getUri() {
            return uri;
        }

        public void recycle() {
            bitmap.recycle();
        }
    }
}