PhotoSource.java revision bed703ef28f013639db3cd7a6b8a6e94d61075da
183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren/*
283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren * Copyright (C) 2012 The Android Open Source Project
383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren *
483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren * Licensed under the Apache License, Version 2.0 (the "License");
583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren * you may not use this file except in compliance with the License.
683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren * You may obtain a copy of the License at
783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren *
883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren *      http://www.apache.org/licenses/LICENSE-2.0
983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren *
1083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren * Unless required by applicable law or agreed to in writing, software
1183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren * distributed under the License is distributed on an "AS IS" BASIS,
1283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren * See the License for the specific language governing permissions and
1483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren * limitations under the License.
1583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren */
1683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenpackage com.android.dreams.phototable;
1783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
1883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport android.content.ContentResolver;
1983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport android.content.Context;
20d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wrenimport android.content.SharedPreferences;
2183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport android.content.res.Resources;
2283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport android.database.Cursor;
2383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport android.graphics.Bitmap;
2483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport android.graphics.BitmapFactory;
2583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport android.graphics.Matrix;
2683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport android.net.Uri;
2783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport android.provider.MediaStore;
2883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport android.util.Log;
2983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
3083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.io.FileNotFoundException;
3183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.io.InputStream;
3283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.io.IOException;
3383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.io.BufferedInputStream;
3483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.util.Collection;
3583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.util.Collections;
3683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.util.LinkedList;
3783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.util.Random;
3883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
3983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren/**
4083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren * Picks a random image from a source of photos.
4183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren */
4283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenpublic abstract class PhotoSource {
4383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    private static final String TAG = "PhotoTable.PhotoSource";
44b7fe7200dcc6efc90aa5441bb8366d3205452c3eChris Wren    private static final boolean DEBUG = false;
4583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
46bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren    // An invalid cursor position to represent the uninitialized state.
47bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren    protected static final int INVALID = -2;
48bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren
4983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    // This should be large enough for BitmapFactory to decode the header so
5083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    // that we can mark and reset the input stream to avoid duplicate network i/o
5183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    private static final int BUFFER_SIZE = 128 * 1024;
5283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
53d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    public class ImageData {
5483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        public String id;
5583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        public String url;
5683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        public int orientation;
57d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
58c6bebae5e07c0108294d05e33fbace209d2f9b0dChris Wren        InputStream getStream(int longSide) {
59c6bebae5e07c0108294d05e33fbace209d2f9b0dChris Wren            return PhotoSource.this.getStream(this, longSide);
60d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        }
61d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    }
62d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
63e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren    public class AlbumData {
64d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        public String id;
65d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        public String title;
66d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        public String thumbnailUrl;
67e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren        public String account;
68d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        public long updated;
69d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
70e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren        public String getType() {
71e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren            String type = PhotoSource.this.getClass().getName();
72e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren            log(TAG, "type is " + type);
73e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren            return type;
74d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        }
7583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    }
7683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
7783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    private final LinkedList<ImageData> mImageQueue;
7883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    private final int mMaxQueueSize;
795b4b44688dac0053be77b282b7501bd291efb0d3Chris Wren    private final float mMaxCropRatio;
8058e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren    private final int mBadImageSkipLimit;
81b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren    private final PhotoSource mFallbackSource;
8283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
83d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    protected final Context mContext;
8483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    protected final Resources mResources;
8583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    protected final Random mRNG;
86d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren    protected final AlbumSettings mSettings;
87d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    protected final ContentResolver mResolver;
88d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
89d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    protected String mSourceName;
9083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
91d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    public PhotoSource(Context context, SharedPreferences settings) {
92b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        this(context, settings, new StockSource(context, settings));
93b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren    }
94b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
95b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren    public PhotoSource(Context context, SharedPreferences settings, PhotoSource fallbackSource) {
9683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mSourceName = TAG;
9783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mContext = context;
98d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren        mSettings = AlbumSettings.getAlbumSettings(settings);
9983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mResolver = mContext.getContentResolver();
10083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mResources = context.getResources();
10183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mImageQueue = new LinkedList<ImageData>();
10283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mMaxQueueSize = mResources.getInteger(R.integer.image_queue_size);
1035b4b44688dac0053be77b282b7501bd291efb0d3Chris Wren        mMaxCropRatio = mResources.getInteger(R.integer.max_crop_ratio) / 1000000f;
10458e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren        mBadImageSkipLimit = mResources.getInteger(R.integer.bad_image_skip_limit);
10583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mRNG = new Random();
106b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        mFallbackSource = fallbackSource;
10783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    }
10883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
10983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    protected void fillQueue() {
11083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        log(TAG, "filling queue");
11183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mImageQueue.addAll(findImages(mMaxQueueSize - mImageQueue.size()));
11283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        Collections.shuffle(mImageQueue);
11383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        log(TAG, "queue contains: " + mImageQueue.size() + " items.");
11483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    }
11583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
11683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    public Bitmap next(BitmapFactory.Options options, int longSide, int shortSide) {
11783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        log(TAG, "decoding a picasa resource to " +  longSide + ", " + shortSide);
11883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        Bitmap image = null;
1195006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren        ImageData imageData = null;
12058e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren        int tries = 0;
12183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
12258e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren        while (image == null && tries < mBadImageSkipLimit) {
1235006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren            synchronized(mImageQueue) {
1245006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren                if (mImageQueue.isEmpty()) {
1255006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren                    fillQueue();
1265006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren                }
12758e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren
1285006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren                imageData = mImageQueue.poll();
1295006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren            }
13068bc9d986e8eb882dc5f15defea0dab45c440ab6Chris Wren            if (imageData != null) {
1315006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren                image = load(imageData, options, longSide, shortSide);
13268bc9d986e8eb882dc5f15defea0dab45c440ab6Chris Wren                imageData = null;
13358e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren            }
13483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
13558e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren            tries++;
136b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        }
13783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
138b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        if (image == null && mFallbackSource != null) {
139b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            image = load((ImageData) mFallbackSource.findImages(1).toArray()[0],
14058e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren                    options, longSide, shortSide);
141b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        }
14283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
143b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        return image;
144b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren    }
14583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
146b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren    public Bitmap load(ImageData data, BitmapFactory.Options options, int longSide, int shortSide) {
147b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        log(TAG, "decoding photo resource to " +  longSide + ", " + shortSide);
148c6bebae5e07c0108294d05e33fbace209d2f9b0dChris Wren        InputStream is = data.getStream(longSide);
149b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
150b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        Bitmap image = null;
151b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        try {
152b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            BufferedInputStream bis = new BufferedInputStream(is);
153b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            bis.mark(BUFFER_SIZE);
154b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
155b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            options.inJustDecodeBounds = true;
156b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            options.inSampleSize = 1;
157b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            image = BitmapFactory.decodeStream(new BufferedInputStream(bis), null, options);
158b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            int rawLongSide = Math.max(options.outWidth, options.outHeight);
159b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            int rawShortSide = Math.min(options.outWidth, options.outHeight);
160b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            log(TAG, "I see bounds of " +  rawLongSide + ", " + rawShortSide);
161b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
162b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            if (rawLongSide != -1 && rawShortSide != -1) {
163b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                float insideRatio = Math.max((float) longSide / (float) rawLongSide,
164b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                                             (float) shortSide / (float) rawShortSide);
165b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                float outsideRatio = Math.max((float) longSide / (float) rawLongSide,
166b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                                              (float) shortSide / (float) rawShortSide);
167b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                float ratio = (outsideRatio / insideRatio < mMaxCropRatio ?
168b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                               outsideRatio : insideRatio);
169b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
170b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                while (ratio < 0.5) {
171b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                    options.inSampleSize *= 2;
172b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                    ratio *= 2;
17383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren                }
174b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
175b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                log(TAG, "decoding with inSampleSize " +  options.inSampleSize);
176b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                bis.reset();
177b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                options.inJustDecodeBounds = false;
178b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                image = BitmapFactory.decodeStream(bis, null, options);
179b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                rawLongSide = Math.max(options.outWidth, options.outHeight);
180b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                rawShortSide = Math.max(options.outWidth, options.outHeight);
18189ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                if (image != null && rawLongSide != -1 && rawShortSide != -1) {
18289ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                    ratio = Math.max((float) longSide / (float) rawLongSide,
18389ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                            (float) shortSide / (float) rawShortSide);
18489ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren
18589ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                    if (Math.abs(ratio - 1.0f) > 0.001) {
18689ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        log(TAG, "still too big, scaling down by " + ratio);
18789ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        options.outWidth = (int) (ratio * options.outWidth);
18889ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        options.outHeight = (int) (ratio * options.outHeight);
18989ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren
19089ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        image = Bitmap.createScaledBitmap(image,
19189ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                                options.outWidth, options.outHeight,
19289ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                                true);
19389ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                    }
194b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
19589ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                    if (data.orientation != 0) {
19689ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        log(TAG, "rotated by " + data.orientation + ": fixing");
1979ab175f5902c66a266261ccf191bc2d213918ecaChris Wren                        Matrix matrix = new Matrix();
1989ab175f5902c66a266261ccf191bc2d213918ecaChris Wren                        matrix.setRotate(data.orientation,
1999ab175f5902c66a266261ccf191bc2d213918ecaChris Wren                                (float) Math.floor(image.getWidth() / 2f),
2009ab175f5902c66a266261ccf191bc2d213918ecaChris Wren                                (float) Math.floor(image.getHeight() / 2f));
2019ab175f5902c66a266261ccf191bc2d213918ecaChris Wren                        image = Bitmap.createBitmap(image, 0, 0,
2029ab175f5902c66a266261ccf191bc2d213918ecaChris Wren                                                    options.outWidth, options.outHeight,
2039ab175f5902c66a266261ccf191bc2d213918ecaChris Wren                                                    matrix, true);
20489ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        if (data.orientation == 90 || data.orientation == 270) {
20589ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                            int tmp = options.outWidth;
20689ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                            options.outWidth = options.outHeight;
20789ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                            options.outHeight = tmp;
20889ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        }
20983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren                    }
210b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
21189ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                    log(TAG, "returning bitmap " + image.getWidth() + ", " + image.getHeight());
21289ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                } else {
21389ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                    image = null;
21489ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                }
215b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            } else {
21689ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                image = null;
21789ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren            }
21889ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren            if (image == null) {
21989ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                log(TAG, "Stream decoding failed with no error" +
22089ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        (options.mCancel ? " due to cancelation." : "."));
221b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            }
2225006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren        } catch (OutOfMemoryError ome) {
2235006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren            log(TAG, "OUT OF MEMORY: " + ome);
2245006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren            image = null;
225b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        } catch (FileNotFoundException fnf) {
226b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            log(TAG, "file not found: " + fnf);
2275006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren            image = null;
228b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        } catch (IOException ioe) {
229b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            log(TAG, "i/o exception: " + ioe);
2305006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren            image = null;
231b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        } finally {
232b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            try {
233b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                if (is != null) {
234b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                    is.close();
23583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren                }
236b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            } catch (Throwable t) {
237b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                log(TAG, "close fail: " + t.toString());
23883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren            }
23983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        }
24083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
24183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        return image;
24283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    }
24383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
24483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    public void setSeed(long seed) {
24583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mRNG.setSeed(seed);
24683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    }
24783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
248b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren    protected static void log(String tag, String message) {
24983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        if (DEBUG) {
25083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren            Log.i(tag, message);
25183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        }
25283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    }
25383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
254bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren    protected int pickRandomStart(int total, int max) {
255bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren        if (max >= total) {
256bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren            return -1;
257bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren        } else {
258bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren            return (mRNG.nextInt() % (total - max)) - 1;
259bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren        }
260bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren    }
261bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren
262c6bebae5e07c0108294d05e33fbace209d2f9b0dChris Wren    protected abstract InputStream getStream(ImageData data, int longSide);
26383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    protected abstract Collection<ImageData> findImages(int howMany);
264d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    public abstract Collection<AlbumData> findAlbums();
26583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren}
266