/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.camera; import android.content.ContentResolver; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.provider.MediaStore.Images; import android.provider.MediaStore.Video; import android.util.Log; import java.io.FileDescriptor; import java.util.WeakHashMap; /** * Provides utilities to decode bitmap, get thumbnail, and cancel the * operations. * * <p>The function {@link #decodeFileDescriptor(FileDescriptor, * BitmapFactory.Options)} is used to decode a bitmap. During decoding another * thread can cancel it using the function {@link #cancelThreadDecoding(Thread, * ContentResolver)} specifying the {@code Thread} which is in decoding. * * <p>{@code cancelThreadDecoding(Thread,ContentResolver)} is sticky until * {@code allowThreadDecoding(Thread) } is called. */ public class BitmapManager { private static final String TAG = "BitmapManager"; private static enum State {CANCEL, ALLOW} private static class ThreadStatus { public State mState = State.ALLOW; public BitmapFactory.Options mOptions; @Override public String toString() { String s; if (mState == State.CANCEL) { s = "Cancel"; } else if (mState == State.ALLOW) { s = "Allow"; } else { s = "?"; } s = "thread state = " + s + ", options = " + mOptions; return s; } } private final WeakHashMap<Thread, ThreadStatus> mThreadStatus = new WeakHashMap<Thread, ThreadStatus>(); private static BitmapManager sManager = null; private BitmapManager() { } /** * Get thread status and create one if specified. */ private synchronized ThreadStatus getOrCreateThreadStatus(Thread t) { ThreadStatus status = mThreadStatus.get(t); if (status == null) { status = new ThreadStatus(); mThreadStatus.put(t, status); } return status; } public synchronized boolean canThreadDecoding(Thread t) { ThreadStatus status = mThreadStatus.get(t); if (status == null) { // allow decoding by default return true; } boolean result = (status.mState != State.CANCEL); return result; } /** * Gets the thumbnail of the given ID of the original image. * * <p> This method wraps around @{code getThumbnail} in {@code * android.provider.MediaStore}. It provides the ability to cancel it. */ public Bitmap getThumbnail(ContentResolver cr, long origId, int kind, BitmapFactory.Options options, boolean isVideo) { Thread t = Thread.currentThread(); ThreadStatus status = getOrCreateThreadStatus(t); if (!canThreadDecoding(t)) { Log.d(TAG, "Thread " + t + " is not allowed to decode."); return null; } try { if (isVideo) { return Video.Thumbnails.getThumbnail(cr, origId, t.getId(), kind, null); } else { return Images.Thumbnails.getThumbnail(cr, origId, t.getId(), kind, null); } } finally { synchronized (status) { status.notifyAll(); } } } public static synchronized BitmapManager instance() { if (sManager == null) { sManager = new BitmapManager(); } return sManager; } }