Java tutorial
/* * Copyright (C) 2010 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 com.rabross.android.minecraftskinwidget; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; 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 android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.net.http.AndroidHttpClient; import android.os.AsyncTask; import android.util.Log; /** * This helper class download images from the Internet and binds those with the * provided ImageView. * * <p> * It requires the INTERNET permission, which should be added to your * application's manifest file. * </p> * * A local cache of downloaded images is maintained internally to improve * performance. * * Additions by Robert Ross rab@rabross.com */ public class ImageDownloader { private static final String TAG = ImageDownloader.class.getSimpleName(); enum DownloadStatus { SUCCESS, FAIL, NON_PREMIUM, NETWORK_ERROR } private ImageLoadingListener mLoadingListener; private BitmapDownloaderTask mBitmapDownloaderTask; private Context mContext; public ImageDownloader(Context aContext, ImageLoadingListener aLoadingListener) { mContext = aContext; mLoadingListener = aLoadingListener; } /** * Download the specified image from the Internet and binds it to the * provided ImageView. The binding is immediate if the image is found in the * cache and will be done asynchronously otherwise. A null bitmap will be * associated to the ImageView if an error occurs. * * @param name * The URL of the image to download. * @param imageView * The ImageView to bind the downloaded image to. */ public void download(String name, String url) { Bitmap bitmap = getBitmapFromDiskCache(name, url); if (bitmap == null) { forceDownload(name, url); return; } mLoadingListener.handleLoadingFinish(bitmap); } /** * Same as download but the image is always downloaded and the cache is not * used. Kept private at the moment as its interest is not clear. */ public void forceDownload(String name, String url) { mBitmapDownloaderTask = new BitmapDownloaderTask(); mBitmapDownloaderTask.execute(name, url); } Bitmap downloadBitmap(String name, String aUrl) { String url = aUrl + name; // AndroidHttpClient is not allowed to be used from the main thread final HttpClient 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) { 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(); return BitmapFactory.decodeStream(new FlushedInputStream(inputStream)); } finally { if (inputStream != null) { inputStream.close(); } entity.consumeContent(); } } } catch (IOException e) { getRequest.abort(); Log.w(TAG, "I/O error while retrieving bitmap", e); } catch (IllegalStateException e) { getRequest.abort(); Log.w(TAG, "Incorrect URL"); } catch (Exception e) { getRequest.abort(); Log.w(TAG, "Error while retrieving bitmap", e); } finally { if ((client instanceof AndroidHttpClient)) { ((AndroidHttpClient) client).close(); } } return null; } /* * An InputStream that skips the exact number of bytes provided, unless it * reaches EOF. */ 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 b = read(); if (b < 0) { break; // we reached EOF } else { bytesSkipped = 1; // we read one byte } } totalBytesSkipped += bytesSkipped; } return totalBytesSkipped; } } /** * The actual AsyncTask that will asynchronously download the image. */ class BitmapDownloaderTask extends AsyncTask<String, Void, DownloadStatus> { private String name; private String url; private Bitmap mBitmap; @Override protected void onCancelled() { mLoadingListener.handleLoadingCancel(); super.onCancelled(); } @Override protected void onPreExecute() { mLoadingListener.handleLoading(); super.onPreExecute(); } /** * Actual download method. */ @Override protected DownloadStatus doInBackground(String... params) { name = params[0]; url = params[1]; if (!Utilities.isConnectingToInternet(mContext)) { return DownloadStatus.NETWORK_ERROR; } if (new PremiumChecker().checkPremium(name)) { if ((mBitmap = downloadBitmap(name, url)) != null) return DownloadStatus.SUCCESS; } else { return DownloadStatus.NON_PREMIUM; } return DownloadStatus.FAIL; } /** * Once the image is downloaded, associates it to the imageView */ @Override protected void onPostExecute(DownloadStatus status) { if (isCancelled()) { return; } switch (status) { case FAIL: mLoadingListener.handleLoadingError(); break; case NON_PREMIUM: mLoadingListener.handlePremiumError(); break; case SUCCESS: putBitmapInDiskCache(name, url, mBitmap); mLoadingListener.handleLoadingFinish(mBitmap); break; case NETWORK_ERROR: mLoadingListener.handleNetworkError(); break; default: break; } } } /** * Write bitmap associated with a url to disk cache */ private void putBitmapInDiskCache(String name, String url, Bitmap bitmap) { try { File cacheFile = new File(mContext.getCacheDir(), String.valueOf(getCacheName(name, url, true))); FileOutputStream fos = new FileOutputStream(cacheFile); bitmap.compress(CompressFormat.PNG, 100, fos); fos.flush(); fos.close(); cacheFile.length(); } catch (Exception e) { Log.e(TAG, "Error when saving image to cache. ", e); } } /** * Read bitmap associated with a url from disk cache */ public Bitmap getBitmapFromDiskCache(String name, String url) { FileInputStream fis = null; Bitmap bm = null; File cacheFile = null; try { // Legacy file name cacheFile = new File(mContext.getCacheDir(), String.valueOf((url + name).hashCode())); if (!cacheFile.exists()) { //new file name cacheFile = new File(mContext.getCacheDir(), String.valueOf(getCacheName(name, url, true))); if (!cacheFile.exists()) { return null; } } fis = new FileInputStream(cacheFile); bm = BitmapFactory.decodeStream(fis); fis.close(); } catch (Exception e) { Log.e(TAG, "Error when reading image from cache. ", e); return null; } return bm; } private String getCacheName(String name, String url, boolean hasHat) { if (hasHat) { return url.hashCode() + "" + name; } else { return url.hashCode() + "" + name + hasHat; } } public void cancel() { mBitmapDownloaderTask.cancel(true); } }