1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.camera; 18 19 import com.android.camera.gallery.IImage; 20 21 import android.content.ContentResolver; 22 import android.graphics.Bitmap; 23 import android.os.Handler; 24 import android.provider.MediaStore; 25 import android.util.Log; 26 27 import java.util.ArrayList; 28 29 /** 30 * A dedicated decoding thread used by ImageGallery. 31 */ 32 public class ImageLoader { 33 @SuppressWarnings("unused") 34 private static final String TAG = "ImageLoader"; 35 36 // Queue of work to do in the worker thread. The work is done in order. 37 private final ArrayList<WorkItem> mQueue = new ArrayList<WorkItem>(); 38 39 // the worker thread and a done flag so we know when to exit 40 private boolean mDone; 41 private Thread mDecodeThread; 42 private ContentResolver mCr; 43 44 public interface LoadedCallback { 45 public void run(Bitmap result); 46 } 47 48 public void getBitmap(IImage image, 49 LoadedCallback imageLoadedRunnable, 50 int tag) { 51 if (mDecodeThread == null) { 52 start(); 53 } 54 synchronized (mQueue) { 55 WorkItem w = new WorkItem(image, imageLoadedRunnable, tag); 56 mQueue.add(w); 57 mQueue.notifyAll(); 58 } 59 } 60 61 public boolean cancel(final IImage image) { 62 synchronized (mQueue) { 63 int index = findItem(image); 64 if (index >= 0) { 65 mQueue.remove(index); 66 return true; 67 } else { 68 return false; 69 } 70 } 71 } 72 73 // The caller should hold mQueue lock. 74 private int findItem(IImage image) { 75 for (int i = 0; i < mQueue.size(); i++) { 76 if (mQueue.get(i).mImage == image) { 77 return i; 78 } 79 } 80 return -1; 81 } 82 83 // Clear the queue. Returns an array of tags that were in the queue. 84 public int[] clearQueue() { 85 synchronized (mQueue) { 86 int n = mQueue.size(); 87 int[] tags = new int[n]; 88 for (int i = 0; i < n; i++) { 89 tags[i] = mQueue.get(i).mTag; 90 } 91 mQueue.clear(); 92 return tags; 93 } 94 } 95 96 private static class WorkItem { 97 IImage mImage; 98 LoadedCallback mOnLoadedRunnable; 99 int mTag; 100 101 WorkItem(IImage image, LoadedCallback onLoadedRunnable, int tag) { 102 mImage = image; 103 mOnLoadedRunnable = onLoadedRunnable; 104 mTag = tag; 105 } 106 } 107 108 public ImageLoader(ContentResolver cr, Handler handler) { 109 mCr = cr; 110 start(); 111 } 112 113 private class WorkerThread implements Runnable { 114 115 // Pick off items on the queue, one by one, and compute their bitmap. 116 // Place the resulting bitmap in the cache, then call back by executing 117 // the given runnable so things can get updated appropriately. 118 public void run() { 119 while (true) { 120 WorkItem workItem = null; 121 synchronized (mQueue) { 122 if (mDone) { 123 break; 124 } 125 if (!mQueue.isEmpty()) { 126 workItem = mQueue.remove(0); 127 } else { 128 try { 129 mQueue.wait(); 130 } catch (InterruptedException ex) { 131 // ignore the exception 132 } 133 continue; 134 } 135 } 136 137 final Bitmap b = workItem.mImage.miniThumbBitmap(); 138 139 if (workItem.mOnLoadedRunnable != null) { 140 workItem.mOnLoadedRunnable.run(b); 141 } 142 } 143 } 144 } 145 146 private void start() { 147 if (mDecodeThread != null) { 148 return; 149 } 150 151 mDone = false; 152 Thread t = new Thread(new WorkerThread()); 153 t.setName("image-loader"); 154 mDecodeThread = t; 155 t.start(); 156 } 157 158 public void stop() { 159 synchronized (mQueue) { 160 mDone = true; 161 mQueue.notifyAll(); 162 } 163 if (mDecodeThread != null) { 164 try { 165 Thread t = mDecodeThread; 166 BitmapManager.instance().cancelThreadDecoding(t, mCr); 167 t.join(); 168 mDecodeThread = null; 169 } catch (InterruptedException ex) { 170 // so now what? 171 } 172 } 173 } 174 } 175