1/*
2 * Copyright (C) 2010 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.gallery3d.ui;
18
19import android.graphics.Bitmap;
20
21import com.android.photos.data.GalleryBitmapPool;
22import com.android.gallery3d.util.Future;
23import com.android.gallery3d.util.FutureListener;
24
25// We use this class to
26//     1.) load bitmaps in background.
27//     2.) as a place holder for the loaded bitmap
28public abstract class BitmapLoader implements FutureListener<Bitmap> {
29    @SuppressWarnings("unused")
30    private static final String TAG = "BitmapLoader";
31
32    /* Transition Map:
33     *   INIT -> REQUESTED, RECYCLED
34     *   REQUESTED -> INIT (cancel), LOADED, ERROR, RECYCLED
35     *   LOADED, ERROR -> RECYCLED
36     */
37    private static final int STATE_INIT = 0;
38    private static final int STATE_REQUESTED = 1;
39    private static final int STATE_LOADED = 2;
40    private static final int STATE_ERROR = 3;
41    private static final int STATE_RECYCLED = 4;
42
43    private int mState = STATE_INIT;
44    // mTask is not null only when a task is on the way
45    private Future<Bitmap> mTask;
46    private Bitmap mBitmap;
47
48    @Override
49    public void onFutureDone(Future<Bitmap> future) {
50        synchronized (this) {
51            mTask = null;
52            mBitmap = future.get();
53            if (mState == STATE_RECYCLED) {
54                if (mBitmap != null) {
55                    GalleryBitmapPool.getInstance().put(mBitmap);
56                    mBitmap = null;
57                }
58                return; // don't call callback
59            }
60            if (future.isCancelled() && mBitmap == null) {
61                if (mState == STATE_REQUESTED) mTask = submitBitmapTask(this);
62                return; // don't call callback
63            } else {
64                mState = mBitmap == null ? STATE_ERROR : STATE_LOADED;
65            }
66        }
67        onLoadComplete(mBitmap);
68    }
69
70    public synchronized void startLoad() {
71        if (mState == STATE_INIT) {
72            mState = STATE_REQUESTED;
73            if (mTask == null) mTask = submitBitmapTask(this);
74        }
75    }
76
77    public synchronized void cancelLoad() {
78        if (mState == STATE_REQUESTED) {
79            mState = STATE_INIT;
80            if (mTask != null) mTask.cancel();
81        }
82    }
83
84    // Recycle the loader and the bitmap
85    public synchronized void recycle() {
86        mState = STATE_RECYCLED;
87        if (mBitmap != null) {
88            GalleryBitmapPool.getInstance().put(mBitmap);
89            mBitmap = null;
90        }
91        if (mTask != null) mTask.cancel();
92    }
93
94    public synchronized boolean isRequestInProgress() {
95        return mState == STATE_REQUESTED;
96    }
97
98    public synchronized boolean isRecycled() {
99        return mState == STATE_RECYCLED;
100    }
101
102    public synchronized Bitmap getBitmap() {
103        return mBitmap;
104    }
105
106    abstract protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l);
107    abstract protected void onLoadComplete(Bitmap bitmap);
108}
109