package co.mwater.clientapp.db; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.Stack; import co.mwater.clientapp.dbsync.RESTClient; import co.mwater.clientapp.dbsync.RESTClient.RequestProgress; import co.mwater.clientapp.dbsync.RESTClientException; import co.mwater.clientapp.util.ProgressTask; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.support.v4.app.FragmentActivity; import android.widget.ImageView; import android.widget.Toast; /** * Manages image download of thumbnails * * @author Clayton * */ public class ImageManager { private ImageQueue imageQueue = new ImageQueue(); private Thread imageLoaderThread = new Thread(new ImageQueueManager()); RESTClient restClient; Context context; private static ImageManager defaultImageManager; public ImageManager(Context context, RESTClient restClient) { this.context = context; this.restClient = restClient; // Make background thread low priority, to avoid affecting UI // performance imageLoaderThread.setPriority(Thread.NORM_PRIORITY - 1); } public static ImageManager getDefault(Context applicationContext) { if (defaultImageManager == null) { // Start image manager defaultImageManager = new ImageManager(applicationContext, MWaterServer.createClient(applicationContext)); } return defaultImageManager; } public void displayThumbnailImage(String uid, ImageView imageView, int defaultDrawableId) { queueThumbnailImage(uid, imageView, defaultDrawableId); imageView.setImageResource(defaultDrawableId); } public void displayImage(final String uid, FragmentActivity activity) throws IOException { // Check if image exists locally final File pendingImage = new File(ImageStorage.getPendingImagePath(activity, uid)); if (pendingImage.exists()) { displayCachedImage(activity, pendingImage); return; } final File cachedImage = new File(ImageStorage.getCachedImagePath(activity, uid)); if (cachedImage.exists()) { displayCachedImage(activity, cachedImage); return; } final File tempImage = new File(ImageStorage.getTempImagePath(activity, uid)); final RESTClient client = MWaterServer.createClient(context); // Download image with progress box new ProgressTask() { @Override protected void runInBackground() { final ProgressTask task = this; try { client.getBytesToFile("downloadimage", new RequestProgress() { public void progress(long completed, long total) { task.updateProgress((int) (completed / 1024), (int) (total / 1024)); } public boolean isCancelled() { return task.isCancelled(); } }, tempImage, "clientuid", MWaterServer.getClientUid(context), "imageuid", uid); } catch (RESTClientException e) { runOnActivity(new ActivityTask() { public void run(FragmentActivity activity) { Toast.makeText(activity, "Unable to download image", Toast.LENGTH_LONG).show(); } }); return; } if (!tempImage.renameTo(cachedImage)) { runOnActivity(new ActivityTask() { public void run(FragmentActivity activity) { Toast.makeText(activity, "Unable to display image", Toast.LENGTH_LONG).show(); } }); return; } runOnActivity(new ActivityTask() { public void run(FragmentActivity activity) { task.finish(); displayCachedImage(activity, cachedImage); } }); } }.execute(activity, "Downloading Image", null); } private void displayCachedImage(Context context, File imageFile) { // Launch image viewer Intent intent = new Intent(); intent.setAction(android.content.Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(imageFile), "image/jpeg"); context.startActivity(intent); } private void queueThumbnailImage(String uid, ImageView imageView, int defaultDrawableId) { // 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(uid, imageView, defaultDrawableId); 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(File cacheFile, RESTClient client, String command, String... args) { try { // Check cache if (!cacheFile.exists()) { // Download file byte[] bytes = client.getBytes(command, null, args); if (bytes == null) return null; // Save to file FileOutputStream fos = new FileOutputStream(cacheFile); fos.write(bytes); fos.close(); } Bitmap bitmap = BitmapFactory.decodeFile(cacheFile.getPath()); return bitmap; } catch (Exception ex) { ex.printStackTrace(); return null; } } /** Classes **/ private class ImageRef { public String uid; public ImageView imageView; public int defDrawableId; public ImageRef(String u, ImageView i, int defaultDrawableId) { uid = u; imageView = i; defDrawableId = defaultDrawableId; } } // stores list of images to download private class ImageQueue { private Stack<ImageRef> imageRefs = new Stack<ImageRef>(); // removes all instances of this ImageView public void Clean(ImageView view) { for (int i = 0; i < imageRefs.size();) { if (imageRefs.get(i).imageView == view) imageRefs.remove(i); else ++i; } } } private class ImageQueueManager implements Runnable { 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(); } // Get file to load into File cacheFile; try { cacheFile = new File(ImageStorage.getCachedThumbnailImagePath(context, imageToLoad.uid)); } catch (IOException e) { continue; } Bitmap bmp = getBitmap(cacheFile, restClient, "downloadimagethumbnail", "clientuid", MWaterServer.getClientUid(context), "imageuid", imageToLoad.uid); BitmapDisplayer bmpDisplayer = new BitmapDisplayer(bmp, imageToLoad.imageView, imageToLoad.defDrawableId); Activity a = (Activity) imageToLoad.imageView.getContext(); a.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; int defDrawableId; public BitmapDisplayer(Bitmap b, ImageView i, int defaultDrawableId) { bitmap = b; imageView = i; defDrawableId = defaultDrawableId; } public void run() { if (bitmap != null) imageView.setImageBitmap(bitmap); else imageView.setImageResource(defDrawableId); } } }