package com.fedorvlasov.lazylist; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Handler; import java.io.File; import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import ar.rulosoft.mimanganu.componentes.Imaginable; import ar.rulosoft.navegadores.Navigator; public class ImageLoader { private static Map<Imaginable, String> imageViews = Collections.synchronizedMap(new WeakHashMap<Imaginable, String>()); private MemCache mMemCache; private FileCache mFileCache; private ExecutorService imgThreadPool; // handler to display images in UI thread private Handler handler = new Handler(); public ImageLoader(Context context) { imageViews.clear(); mMemCache = MemCache.getInstance(); mFileCache = new FileCache(context); imgThreadPool = Executors.newFixedThreadPool(4); } public void displayImg(String url, Imaginable imageView) { if (imageViewReUse(imageView, url)) { imageViews.put(imageView, url); // First, try to fetch image from memory Bitmap bitmap = mMemCache.getImageInMem(url); if (bitmap != null) { imageView.setImageBitmap(bitmap); } else { queuePhoto(url, imageView); //imageView.setImageResource(stub_id); } } } private boolean imageViewReUse(Imaginable imageView, String url) { String tag = imageViews.get(imageView); return tag == null || !tag.equals(url); } private void queuePhoto(String url, Imaginable imageView) { imgThreadPool.submit(new ImageGet(imageView, url)); } private Bitmap getBitmap(String url) { File f = mFileCache.getFile(url); // Second, try to get image from local storage, i.e. SD card Bitmap imgFile = decodeFile(f); if (imgFile != null) return imgFile; try { FileCache.writeFile(Navigator.navigator.getStream(url), f); return decodeFile(f); } catch (Throwable ex) { if (ex instanceof OutOfMemoryError) mMemCache.clearMem(); return null; } } /** * decodes image and scales it to reduce memory consumption * * @param put_file data from disk * @return Bitmap */ private Bitmap decodeFile(File put_file) { // if file not exist, skip everything if (!put_file.exists()) return null; // We want Image to be equal or smaller than 400px height int tempSampleSize = 1, requiredSize = 400; try { BitmapFactory.Options bmpOpts = new BitmapFactory.Options(); bmpOpts.inJustDecodeBounds = true; BitmapFactory.decodeFile(put_file.getAbsolutePath(), bmpOpts); while ((bmpOpts.outHeight / tempSampleSize) >= requiredSize) { tempSampleSize *= 2; } bmpOpts.inSampleSize = tempSampleSize; bmpOpts.inJustDecodeBounds = false; return BitmapFactory.decodeFile(put_file.getAbsolutePath(), bmpOpts); } catch (Exception e) { // usually file not found, but just ignore it return null; } } /** * An image getter, which is called, if Image is not found in memory * It is a runnable, which will be submit into the imgThreadPool, * so it won't block the UI */ class ImageGet implements Runnable { String url; Imaginable imageView; ImageGet(Imaginable _imageView, String _url) { this.url = _url; this.imageView = _imageView; } @Override public void run() { try { if (imageViewReUse(imageView, url)) return; Bitmap bmp = getBitmap(url); mMemCache.putImageInMem(url, bmp); if (imageViewReUse(imageView, url)) return; BitmapDisplay bd = new BitmapDisplay(bmp, imageView, url); handler.post(bd); } catch (Throwable th) { // th.printStackTrace(); } } } /** * Used to display bitmap in the UI thread, * if the image finally arrived, then update the imageView with the new image */ class BitmapDisplay implements Runnable { Bitmap bitmap; String url; Imaginable imageView; public BitmapDisplay(Bitmap _bmp, Imaginable _imageView, String _url) { bitmap = _bmp; url = _url; imageView = _imageView; } public void run() { if (imageViewReUse(imageView, url)) return; if (bitmap != null) { imageView.setAlpha(0f); imageView.setImageBitmap(bitmap); ObjectAnimator.ofFloat(imageView, "alpha", 1f).start(); } imageViews.remove(imageView); } } }