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