Project: android-cropimage
Code Examples
/*
 * 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; 
 
/**
 * This class provides several utilities to cancel bitmap decoding. 
 * 
 * The function decodeFileDescriptor() is used to decode a bitmap. During 
 * decoding if another thread wants to cancel it, it calls the function 
 * cancelThreadDecoding() specifying the Thread which is in decoding. 
 * 
 * cancelThreadDecoding() is sticky until allowThreadDecoding() 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; 
        public boolean mThumbRequesting; 
        @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; 
    } 
 
    /**
     * The following three methods are used to keep track of 
     * BitmapFaction.Options used for decoding and cancelling. 
     */
 
    private synchronized void setDecodingOptions(Thread t, 
            BitmapFactory.Options options) { 
        getOrCreateThreadStatus(t).mOptions = options; 
    } 
 
    synchronized void removeDecodingOptions(Thread t) { 
        ThreadStatus status = mThreadStatus.get(t); 
        status.mOptions = null
    } 
 
    /**
     * The following three methods are used to keep track of which thread 
     * is being disabled for bitmap decoding. 
     */
 
    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; 
    } 
 
    public synchronized void allowThreadDecoding(Thread t) { 
        getOrCreateThreadStatus(t).mState = State.ALLOW; 
    } 
 
    public synchronized void cancelThreadDecoding(Thread t, ContentResolver cr) { 
        ThreadStatus status = getOrCreateThreadStatus(t); 
        status.mState = State.CANCEL; 
        if (status.mOptions != null) { 
            status.mOptions.requestCancelDecode(); 
        } 
 
        // Wake up threads in waiting list 
        notifyAll(); 
 
        // Since our cancel request can arrive MediaProvider earlier than getThumbnail request, 
        // we use mThumbRequesting flag to make sure our request does cancel the request. 
        try { 
            synchronized (status) { 
                while (status.mThumbRequesting) { 
                    Images.Thumbnails.cancelThumbnailRequest(cr, t.getId()); 
                    Video.Thumbnails.cancelThumbnailRequest(cr, t.getId()); 
                    status.wait(200); 
                } 
            } 
        } catch (InterruptedException ex) { 
            // ignore 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 { 
            synchronized (status) { 
                status.mThumbRequesting = true
            } 
            if (isVideo) { 
                return Video.Thumbnails.getThumbnail(cr, t.getId(), 
                        kind, null); 
            } else { 
                return Images.Thumbnails.getThumbnail(cr, t.getId(), 
                        kind, null); 
            } 
        } finally { 
            synchronized (status) { 
                status.mThumbRequesting = false
                status.notifyAll(); 
            } 
        } 
    } 
 
    public static synchronized BitmapManager instance() { 
        if (sManager == null) { 
            sManager = new BitmapManager(); 
        } 
        return sManager; 
    } 
 
    /**
     * The real place to delegate bitmap decoding to BitmapFactory. 
     */
 
    public Bitmap decodeFileDescriptor(FileDescriptor fd, 
                                       BitmapFactory.Options options) { 
        if (options.mCancel) { 
            return null
        } 
 
        Thread thread = Thread.currentThread(); 
        if (!canThreadDecoding(thread)) { 
            Log.d(TAG, "Thread " + thread + " is not allowed to decode."); 
            return null
        } 
 
        setDecodingOptions(thread, options); 
        Bitmap b = BitmapFactory.decodeFileDescriptor(fd, null, options); 
 
        removeDecodingOptions(thread); 
        return b; 
    } 
}