blob: 3ae6b9381741e8756d447f6b4339f3ee8030118e [file] [log] [blame]
Owen Lin666ea1b2009-10-14 22:34:47 -07001/*
2 * Copyright (C) 2009 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
17package com.android.camera;
18
Owen Lin8aa3fbc2009-10-22 20:24:04 -070019import android.content.ContentResolver;
Owen Lin666ea1b2009-10-14 22:34:47 -070020import android.graphics.Bitmap;
21import android.graphics.BitmapFactory;
Owen Lin8aa3fbc2009-10-22 20:24:04 -070022import android.provider.MediaStore.Images;
23import android.provider.MediaStore.Video;
Owen Lin666ea1b2009-10-14 22:34:47 -070024import android.util.Log;
25
26import java.io.FileDescriptor;
27import java.util.WeakHashMap;
28
29/**
30 * This class provides several utilities to cancel bitmap decoding.
31 *
32 * The function decodeFileDescriptor() is used to decode a bitmap. During
33 * decoding if another thread wants to cancel it, it calls the function
34 * cancelThreadDecoding() specifying the Thread which is in decoding.
35 *
36 * cancelThreadDecoding() is sticky until allowThreadDecoding() is called.
37 */
38public class BitmapManager {
39 private static final String TAG = "BitmapManager";
40 private static enum State {CANCEL, ALLOW}
41 private static class ThreadStatus {
42 public State mState = State.ALLOW;
43 public BitmapFactory.Options mOptions;
Owen Lin8aa3fbc2009-10-22 20:24:04 -070044 public boolean mThumbRequesting;
Owen Lin666ea1b2009-10-14 22:34:47 -070045 @Override
46 public String toString() {
47 String s;
48 if (mState == State.CANCEL) {
49 s = "Cancel";
50 } else if (mState == State.ALLOW) {
51 s = "Allow";
52 } else {
53 s = "?";
54 }
55 s = "thread state = " + s + ", options = " + mOptions;
56 return s;
57 }
58 }
59
60 private final WeakHashMap<Thread, ThreadStatus> mThreadStatus =
61 new WeakHashMap<Thread, ThreadStatus>();
62
63 private static BitmapManager sManager = null;
64
65 private BitmapManager() {
66 }
67
68 /**
69 * Get thread status and create one if specified.
70 */
71 private synchronized ThreadStatus getOrCreateThreadStatus(Thread t) {
72 ThreadStatus status = mThreadStatus.get(t);
73 if (status == null) {
74 status = new ThreadStatus();
75 mThreadStatus.put(t, status);
76 }
77 return status;
78 }
79
80 /**
81 * The following three methods are used to keep track of
82 * BitmapFaction.Options used for decoding and cancelling.
83 */
84 private synchronized void setDecodingOptions(Thread t,
85 BitmapFactory.Options options) {
86 getOrCreateThreadStatus(t).mOptions = options;
87 }
88
89 synchronized void removeDecodingOptions(Thread t) {
90 ThreadStatus status = mThreadStatus.get(t);
91 status.mOptions = null;
92 }
93
94 /**
95 * The following three methods are used to keep track of which thread
96 * is being disabled for bitmap decoding.
97 */
98 public synchronized boolean canThreadDecoding(Thread t) {
99 ThreadStatus status = mThreadStatus.get(t);
100 if (status == null) {
101 // allow decoding by default
102 return true;
103 }
104
105 boolean result = (status.mState != State.CANCEL);
106 return result;
107 }
108
109 public synchronized void allowThreadDecoding(Thread t) {
110 getOrCreateThreadStatus(t).mState = State.ALLOW;
111 }
112
Owen Lin8aa3fbc2009-10-22 20:24:04 -0700113 public synchronized void cancelThreadDecoding(Thread t, ContentResolver cr) {
Owen Lin666ea1b2009-10-14 22:34:47 -0700114 ThreadStatus status = getOrCreateThreadStatus(t);
115 status.mState = State.CANCEL;
116 if (status.mOptions != null) {
117 status.mOptions.requestCancelDecode();
118 }
119
120 // Wake up threads in waiting list
121 notifyAll();
Owen Lin8aa3fbc2009-10-22 20:24:04 -0700122
123 // Since our cancel request can arrive MediaProvider earlier than getThumbnail request,
124 // we use mThumbRequesting flag to make sure our request does cancel the request.
125 try {
126 synchronized (status) {
127 while (status.mThumbRequesting) {
128 Images.Thumbnails.cancelThumbnailRequest(cr, -1, t.getId());
129 Video.Thumbnails.cancelThumbnailRequest(cr, -1, t.getId());
130 status.wait(200);
131 }
132 }
133 } catch (InterruptedException ex) {
134 // ignore it.
135 }
136 }
137
138 public Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
139 BitmapFactory.Options options, boolean isVideo) {
140 Thread t = Thread.currentThread();
141 ThreadStatus status = getOrCreateThreadStatus(t);
142
143 if (!canThreadDecoding(t)) {
144 Log.d(TAG, "Thread " + t + " is not allowed to decode.");
145 return null;
146 }
147
148 try {
149 synchronized (status) {
150 status.mThumbRequesting = true;
151 }
152 if (isVideo) {
153 return Video.Thumbnails.getThumbnail(cr, origId, t.getId(),
154 kind, null);
155 } else {
156 return Images.Thumbnails.getThumbnail(cr, origId, t.getId(),
157 kind, null);
158 }
159 } finally {
160 synchronized (status) {
161 status.mThumbRequesting = false;
162 status.notifyAll();
163 }
164 }
Owen Lin666ea1b2009-10-14 22:34:47 -0700165 }
166
167 public static synchronized BitmapManager instance() {
168 if (sManager == null) {
169 sManager = new BitmapManager();
170 }
171 return sManager;
172 }
173
174 /**
175 * The real place to delegate bitmap decoding to BitmapFactory.
176 */
177 public Bitmap decodeFileDescriptor(FileDescriptor fd,
178 BitmapFactory.Options options) {
179 if (options.mCancel) {
180 return null;
181 }
182
183 Thread thread = Thread.currentThread();
184 if (!canThreadDecoding(thread)) {
185 Log.d(TAG, "Thread " + thread + " is not allowed to decode.");
186 return null;
187 }
188
189 setDecodingOptions(thread, options);
190 Bitmap b = BitmapFactory.decodeFileDescriptor(fd, null, options);
191
192 removeDecodingOptions(thread);
193 return b;
194 }
195}