1/* 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 19import android.content.ContentResolver; 20import android.graphics.Bitmap; 21import android.graphics.BitmapFactory; 22import android.provider.MediaStore.Images; 23import android.provider.MediaStore.Video; 24import android.util.Log; 25 26import java.io.FileDescriptor; 27import java.util.WeakHashMap; 28 29/** 30 * Provides utilities to decode bitmap, get thumbnail, and cancel the 31 * operations. 32 * 33 * <p>The function {@link #decodeFileDescriptor(FileDescriptor, 34 * BitmapFactory.Options)} is used to decode a bitmap. During decoding another 35 * thread can cancel it using the function {@link #cancelThreadDecoding(Thread, 36 * ContentResolver)} specifying the {@code Thread} which is in decoding. 37 * 38 * <p>{@code cancelThreadDecoding(Thread,ContentResolver)} is sticky until 39 * {@code allowThreadDecoding(Thread) } is called. 40 */ 41public class BitmapManager { 42 private static final String TAG = "BitmapManager"; 43 private static enum State {CANCEL, ALLOW} 44 private static class ThreadStatus { 45 public State mState = State.ALLOW; 46 public BitmapFactory.Options mOptions; 47 48 @Override 49 public String toString() { 50 String s; 51 if (mState == State.CANCEL) { 52 s = "Cancel"; 53 } else if (mState == State.ALLOW) { 54 s = "Allow"; 55 } else { 56 s = "?"; 57 } 58 s = "thread state = " + s + ", options = " + mOptions; 59 return s; 60 } 61 } 62 63 private final WeakHashMap<Thread, ThreadStatus> mThreadStatus = 64 new WeakHashMap<Thread, ThreadStatus>(); 65 66 private static BitmapManager sManager = null; 67 68 private BitmapManager() { 69 } 70 71 /** 72 * Get thread status and create one if specified. 73 */ 74 private synchronized ThreadStatus getOrCreateThreadStatus(Thread t) { 75 ThreadStatus status = mThreadStatus.get(t); 76 if (status == null) { 77 status = new ThreadStatus(); 78 mThreadStatus.put(t, status); 79 } 80 return status; 81 } 82 83 public synchronized boolean canThreadDecoding(Thread t) { 84 ThreadStatus status = mThreadStatus.get(t); 85 if (status == null) { 86 // allow decoding by default 87 return true; 88 } 89 90 boolean result = (status.mState != State.CANCEL); 91 return result; 92 } 93 94 /** 95 * Gets the thumbnail of the given ID of the original image. 96 * 97 * <p> This method wraps around @{code getThumbnail} in {@code 98 * android.provider.MediaStore}. It provides the ability to cancel it. 99 */ 100 public Bitmap getThumbnail(ContentResolver cr, long origId, int kind, 101 BitmapFactory.Options options, boolean isVideo) { 102 Thread t = Thread.currentThread(); 103 ThreadStatus status = getOrCreateThreadStatus(t); 104 105 if (!canThreadDecoding(t)) { 106 Log.d(TAG, "Thread " + t + " is not allowed to decode."); 107 return null; 108 } 109 110 try { 111 if (isVideo) { 112 return Video.Thumbnails.getThumbnail(cr, origId, t.getId(), 113 kind, null); 114 } else { 115 return Images.Thumbnails.getThumbnail(cr, origId, t.getId(), 116 kind, null); 117 } 118 } finally { 119 synchronized (status) { 120 status.notifyAll(); 121 } 122 } 123 } 124 125 public static synchronized BitmapManager instance() { 126 if (sManager == null) { 127 sManager = new BitmapManager(); 128 } 129 return sManager; 130 } 131} 132