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 {
run(Bitmap result)45         public void run(Bitmap result);
46     }
47 
getBitmap(IImage image, LoadedCallback imageLoadedRunnable, int tag)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 
cancel(final IImage image)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.
findItem(IImage image)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.
clearQueue()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 
WorkItem(IImage image, LoadedCallback onLoadedRunnable, int tag)101         WorkItem(IImage image, LoadedCallback onLoadedRunnable, int tag) {
102             mImage = image;
103             mOnLoadedRunnable = onLoadedRunnable;
104             mTag = tag;
105         }
106     }
107 
ImageLoader(ContentResolver cr, Handler handler)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.
run()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 
start()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 
stop()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