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