package com.masterofcode.android.magreader.utils; import java.io.File; import java.io.FileOutputStream; import java.net.URL; import java.util.Iterator; import java.util.Map; import java.util.Stack; import java.util.WeakHashMap; import android.app.Activity; import android.content.Context; import android.content.ContextWrapper; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.Log; import android.widget.ImageView; import com.masterofcode.android.magreader.utils.constants.Constants; import com.sandsmedia.apps.mobile.android.epub.lib.R; public class ImageLoader { private static Map<String, Bitmap> imageMap = new WeakHashMap<String, Bitmap>(); private File cacheDir; private ImageQueue imageQueue = new ImageQueue(); private Thread imageLoaderThread = new Thread(new ImageQueueManager()); private Activity mActivity; public ImageLoader(Context context, Activity activity) { this.mActivity = activity; // Make background thread low priority, to avoid affecting UI performance imageLoaderThread.setPriority(Thread.NORM_PRIORITY-1); ContextWrapper cw = new ContextWrapper(context); cacheDir = cw.getDir("thumbnails", Context.MODE_PRIVATE); } public void displayImage(final String url, final ImageView imageView) { if(imageMap.containsKey(url)){ imageView.setImageBitmap(imageMap.get(url));} else { queueImage(url, mActivity, imageView); imageView.setImageResource(R.drawable.no_image); } } private void queueImage(String url, Activity activity, ImageView imageView) { // This ImageView might have been used for other images, so we clear // the queue of old tasks before starting. imageQueue.Clean(imageView); ImageRef p=new ImageRef(url, imageView); synchronized(imageQueue.imageRefs) { imageQueue.imageRefs.push(p); imageQueue.imageRefs.notifyAll(); } // Start thread if it's not started yet if(imageLoaderThread.getState() == Thread.State.NEW) imageLoaderThread.start(); } private Bitmap getBitmap(String url) { String filename = String.valueOf(url.hashCode()); File f = new File(cacheDir, filename); // Is the bitmap in our cache? Bitmap bitmap = BitmapFactory.decodeFile(f.getPath()); if(bitmap != null) return bitmap; // Nope, have to download it try { bitmap = BitmapFactory.decodeStream(new URL(url).openConnection().getInputStream()); // save bitmap to cache for later writeFile(bitmap, f); return bitmap; } catch (Exception ex) { ex.printStackTrace(); return null; } } private void writeFile(Bitmap bmp, File f) { FileOutputStream out = null; try { out = new FileOutputStream(f); if(bmp != null) bmp.compress(Bitmap.CompressFormat.PNG, 80, out); } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null ) out.close(); } catch(Exception ex) {} } } /** Classes **/ private class ImageRef { public String url; public ImageView imageView; public ImageRef(String u, ImageView i) { url=u; imageView=i; } } //stores list of images to download private class ImageQueue { private Stack<ImageRef> imageRefs = new Stack<ImageRef>(); //removes all instances of this ImageView public synchronized void Clean(ImageView view) { try{ for(Iterator<ImageRef> it = imageRefs.iterator(); it.hasNext();){ ImageRef mImageRef = it.next(); if(mImageRef.imageView == view) it.remove(); } /** for (ImageRef mImageRef : imageRefs){ if(mImageRef.imageView == view) imageRefs.remove(mImageRef); } */ } catch(Exception exc){ exc.printStackTrace(); } } } private class ImageQueueManager implements Runnable { @Override public void run() { try { while(true) { // Thread waits until there are images in the // queue to be retrieved if(imageQueue.imageRefs.size() == 0) { synchronized(imageQueue.imageRefs) { imageQueue.imageRefs.wait(); } } // When we have images to be loaded if(imageQueue.imageRefs.size() != 0) { ImageRef imageToLoad; synchronized(imageQueue.imageRefs) { imageToLoad = imageQueue.imageRefs.pop(); } Bitmap bmp = getBitmap(imageToLoad.url); imageMap.put(imageToLoad.url, bmp); if(Constants.Debug) Log.d("favicon", "imageMap.put: " + imageToLoad.url); Object tag = imageToLoad.imageView.getTag(); // Make sure we have the right view - thread safety defender if(tag != null && ((String)tag).equals(imageToLoad.url)) { BitmapDisplayer bmpDisplayer = new BitmapDisplayer(bmp, imageToLoad.imageView); //Activity a = (Activity)imageToLoad.imageView.getContext(); //a.runOnUiThread(bmpDisplayer); mActivity.runOnUiThread(bmpDisplayer); } } if(Thread.interrupted()) break; } } catch (InterruptedException e) {} } } //Used to display bitmap in the UI thread private class BitmapDisplayer implements Runnable { Bitmap bitmap; ImageView imageView; public BitmapDisplayer(Bitmap b, ImageView i) { bitmap=b; imageView=i; } public void run() { if(bitmap != null) imageView.setImageBitmap(bitmap); else imageView.setImageResource(R.drawable.no_image); } } public static Map<String, Bitmap> getImageMap() { return imageMap; } }