1/*
2 * Copyright (C) 2011 Google Inc.
3 * Licensed to The Android Open Source Project.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.android.ex.photo.loaders;
19
20import android.content.ContentResolver;
21import android.content.Context;
22import android.graphics.Bitmap;
23import android.net.Uri;
24import android.support.v4.content.AsyncTaskLoader;
25import android.util.DisplayMetrics;
26
27import com.android.ex.photo.PhotoViewController;
28import com.android.ex.photo.loaders.PhotoBitmapLoaderInterface.BitmapResult;
29import com.android.ex.photo.util.ImageUtils;
30
31/**
32 * Loader for the bitmap of a photo.
33 */
34public class PhotoBitmapLoader extends AsyncTaskLoader<BitmapResult>
35    implements PhotoBitmapLoaderInterface {
36    private String mPhotoUri;
37    private Bitmap mBitmap;
38
39    public PhotoBitmapLoader(Context context, String photoUri) {
40        super(context);
41        mPhotoUri = photoUri;
42    }
43
44    @Override
45    public void setPhotoUri(String photoUri) {
46        mPhotoUri = photoUri;
47    }
48
49    @Override
50    public BitmapResult loadInBackground() {
51        BitmapResult result = new BitmapResult();
52        Context context = getContext();
53        if (context != null && mPhotoUri != null) {
54            final ContentResolver resolver = context.getContentResolver();
55            try {
56                result = ImageUtils.createLocalBitmap(resolver, Uri.parse(mPhotoUri),
57                        PhotoViewController.sMaxPhotoSize);
58                if (result.bitmap != null) {
59                    result.bitmap.setDensity(DisplayMetrics.DENSITY_MEDIUM);
60                }
61            } catch (UnsupportedOperationException ex) {
62                // We got image bytes, but unable to decode to a Bitmap
63                result.status = BitmapResult.STATUS_EXCEPTION;
64            }
65        }
66
67        return result;
68    }
69
70    /**
71     * Called when there is new data to deliver to the client.  The
72     * super class will take care of delivering it; the implementation
73     * here just adds a little more logic.
74     */
75    @Override
76    public void deliverResult(BitmapResult result) {
77        Bitmap bitmap = result != null ? result.bitmap : null;
78        if (isReset()) {
79            // An async query came in while the loader is stopped.  We
80            // don't need the result.
81            if (bitmap != null) {
82                onReleaseResources(bitmap);
83            }
84            return;
85        }
86        Bitmap oldBitmap = mBitmap;
87        mBitmap = bitmap;
88
89        if (isStarted()) {
90            // If the Loader is currently started, we can immediately
91            // deliver its results.
92            super.deliverResult(result);
93        }
94
95        // At this point we can release the resources associated with
96        // 'oldBitmap' if needed; now that the new result is delivered we
97        // know that it is no longer in use.
98        if (oldBitmap != null && oldBitmap != bitmap && !oldBitmap.isRecycled()) {
99            onReleaseResources(oldBitmap);
100        }
101    }
102
103    /**
104     * Handles a request to start the Loader.
105     */
106    @Override
107    protected void onStartLoading() {
108        if (mBitmap != null) {
109            // If we currently have a result available, deliver it
110            // immediately.
111            BitmapResult result = new BitmapResult();
112            result.status = BitmapResult.STATUS_SUCCESS;
113            result.bitmap = mBitmap;
114            deliverResult(result);
115        }
116
117        if (takeContentChanged() || mBitmap == null) {
118            // If the data has changed since the last time it was loaded
119            // or is not currently available, start a load.
120            forceLoad();
121        }
122    }
123
124    /**
125     * Handles a request to stop the Loader.
126     */
127    @Override protected void onStopLoading() {
128        // Attempt to cancel the current load task if possible.
129        cancelLoad();
130    }
131
132    /**
133     * Handles a request to cancel a load.
134     */
135    @Override
136    public void onCanceled(BitmapResult result) {
137        super.onCanceled(result);
138
139        // At this point we can release the resources associated with 'bitmap'
140        // if needed.
141        if (result != null) {
142            onReleaseResources(result.bitmap);
143        }
144    }
145
146    /**
147     * Handles a request to completely reset the Loader.
148     */
149    @Override
150    protected void onReset() {
151        super.onReset();
152
153        // Ensure the loader is stopped
154        onStopLoading();
155
156        // At this point we can release the resources associated with 'bitmap'
157        // if needed.
158        if (mBitmap != null) {
159            onReleaseResources(mBitmap);
160            mBitmap = null;
161        }
162    }
163
164    /**
165     * Helper function to take care of releasing resources associated
166     * with an actively loaded data set.
167     */
168    protected void onReleaseResources(Bitmap bitmap) {
169        if (bitmap != null && !bitmap.isRecycled()) {
170            bitmap.recycle();
171        }
172    }
173}
174