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.util.Log;
2783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
2888d80f4471c900628e2cb6eef23029b99af48e09Chris Wrenimport java.io.BufferedInputStream;
2983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.io.FileNotFoundException;
3083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.io.IOException;
3188d80f4471c900628e2cb6eef23029b99af48e09Chris Wrenimport java.io.InputStream;
3283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.util.Collection;
3383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.util.Collections;
3488d80f4471c900628e2cb6eef23029b99af48e09Chris Wrenimport java.util.HashMap;
3583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.util.LinkedList;
3683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenimport java.util.Random;
3783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
3883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren/**
3983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren * Picks a random image from a source of photos.
4083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren */
4183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wrenpublic abstract class PhotoSource {
4283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    private static final String TAG = "PhotoTable.PhotoSource";
43b7fe7200dcc6efc90aa5441bb8366d3205452c3eChris Wren    private static final boolean DEBUG = false;
4483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
4583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    // This should be large enough for BitmapFactory to decode the header so
4683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    // that we can mark and reset the input stream to avoid duplicate network i/o
4783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    private static final int BUFFER_SIZE = 128 * 1024;
4883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
49d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    public class ImageData {
5083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        public String id;
5183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        public String url;
5283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        public int orientation;
53d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
5488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        protected String albumId;
5588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        protected Cursor cursor;
5688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        protected int position;
5788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren
58c6bebae5e07c0108294d05e33fbace209d2f9b0dChris Wren        InputStream getStream(int longSide) {
59c6bebae5e07c0108294d05e33fbace209d2f9b0dChris Wren            return PhotoSource.this.getStream(this, longSide);
60d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        }
6188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        ImageData naturalNext() {
6288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren            return PhotoSource.this.naturalNext(this);
6388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        }
6488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        ImageData naturalPrevious() {
6588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren            return PhotoSource.this.naturalPrevious(this);
6688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        }
67bcfd4439d730a4d783a02596c8ab444796323aadChris Wren        public void donePaging() {
68bcfd4439d730a4d783a02596c8ab444796323aadChris Wren            PhotoSource.this.donePaging(this);
69bcfd4439d730a4d783a02596c8ab444796323aadChris Wren        }
70d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    }
71d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
72e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren    public class AlbumData {
73d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        public String id;
74d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        public String title;
75d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        public String thumbnailUrl;
76e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren        public String account;
77d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        public long updated;
78d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
79e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren        public String getType() {
80e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren            String type = PhotoSource.this.getClass().getName();
81e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren            log(TAG, "type is " + type);
82e38c0c80e3e9b3b835e5c2e014ccf23e29663396Chris Wren            return type;
83d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren        }
8483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    }
8583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
8683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    private final LinkedList<ImageData> mImageQueue;
8783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    private final int mMaxQueueSize;
885b4b44688dac0053be77b282b7501bd291efb0d3Chris Wren    private final float mMaxCropRatio;
8958e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren    private final int mBadImageSkipLimit;
90b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren    private final PhotoSource mFallbackSource;
9188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren    private final HashMap<Bitmap, ImageData> mImageMap;
9283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
93d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    protected final Context mContext;
9483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    protected final Resources mResources;
9583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    protected final Random mRNG;
96d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren    protected final AlbumSettings mSettings;
97d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    protected final ContentResolver mResolver;
98d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren
99d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    protected String mSourceName;
10083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
101d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    public PhotoSource(Context context, SharedPreferences settings) {
102b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        this(context, settings, new StockSource(context, settings));
103b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren    }
104b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
105b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren    public PhotoSource(Context context, SharedPreferences settings, PhotoSource fallbackSource) {
10683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mSourceName = TAG;
10783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mContext = context;
108d9b659aa5dfa4a3af96582ae49ba9ae145854a84Chris Wren        mSettings = AlbumSettings.getAlbumSettings(settings);
10983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mResolver = mContext.getContentResolver();
11083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mResources = context.getResources();
11183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mImageQueue = new LinkedList<ImageData>();
11283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mMaxQueueSize = mResources.getInteger(R.integer.image_queue_size);
1135b4b44688dac0053be77b282b7501bd291efb0d3Chris Wren        mMaxCropRatio = mResources.getInteger(R.integer.max_crop_ratio) / 1000000f;
11458e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren        mBadImageSkipLimit = mResources.getInteger(R.integer.bad_image_skip_limit);
11588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        mImageMap = new HashMap<Bitmap, ImageData>();
11683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mRNG = new Random();
117b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        mFallbackSource = fallbackSource;
11883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    }
11983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
12083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    protected void fillQueue() {
12183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        log(TAG, "filling queue");
12283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mImageQueue.addAll(findImages(mMaxQueueSize - mImageQueue.size()));
12383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        Collections.shuffle(mImageQueue);
12483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        log(TAG, "queue contains: " + mImageQueue.size() + " items.");
12583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    }
12683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
12783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    public Bitmap next(BitmapFactory.Options options, int longSide, int shortSide) {
12883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        log(TAG, "decoding a picasa resource to " +  longSide + ", " + shortSide);
12983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        Bitmap image = null;
1305006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren        ImageData imageData = null;
13158e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren        int tries = 0;
13283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
13358e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren        while (image == null && tries < mBadImageSkipLimit) {
1345006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren            synchronized(mImageQueue) {
1355006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren                if (mImageQueue.isEmpty()) {
1365006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren                    fillQueue();
1375006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren                }
1385006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren                imageData = mImageQueue.poll();
1395006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren            }
14068bc9d986e8eb882dc5f15defea0dab45c440ab6Chris Wren            if (imageData != null) {
1415006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren                image = load(imageData, options, longSide, shortSide);
14288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren                mImageMap.put(image, imageData);
14368bc9d986e8eb882dc5f15defea0dab45c440ab6Chris Wren                imageData = null;
14458e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren            }
14583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
14658e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren            tries++;
147b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        }
14883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
149b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        if (image == null && mFallbackSource != null) {
150b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            image = load((ImageData) mFallbackSource.findImages(1).toArray()[0],
15158e2a16d9237d2a5674a97062b5d614d8d397845Chris Wren                    options, longSide, shortSide);
152b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        }
15383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
154b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        return image;
155b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren    }
15683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
157b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren    public Bitmap load(ImageData data, BitmapFactory.Options options, int longSide, int shortSide) {
158b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        log(TAG, "decoding photo resource to " +  longSide + ", " + shortSide);
159c6bebae5e07c0108294d05e33fbace209d2f9b0dChris Wren        InputStream is = data.getStream(longSide);
160b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
161b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        Bitmap image = null;
162b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        try {
163b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            BufferedInputStream bis = new BufferedInputStream(is);
164b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            bis.mark(BUFFER_SIZE);
165b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
166b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            options.inJustDecodeBounds = true;
167b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            options.inSampleSize = 1;
168b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            image = BitmapFactory.decodeStream(new BufferedInputStream(bis), null, options);
169b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            int rawLongSide = Math.max(options.outWidth, options.outHeight);
170b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            int rawShortSide = Math.min(options.outWidth, options.outHeight);
171b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            log(TAG, "I see bounds of " +  rawLongSide + ", " + rawShortSide);
172b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
173b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            if (rawLongSide != -1 && rawShortSide != -1) {
174b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                float insideRatio = Math.max((float) longSide / (float) rawLongSide,
175b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                                             (float) shortSide / (float) rawShortSide);
176b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                float outsideRatio = Math.max((float) longSide / (float) rawLongSide,
177b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                                              (float) shortSide / (float) rawShortSide);
178b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                float ratio = (outsideRatio / insideRatio < mMaxCropRatio ?
179b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                               outsideRatio : insideRatio);
180b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
181b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                while (ratio < 0.5) {
182b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                    options.inSampleSize *= 2;
183b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                    ratio *= 2;
18483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren                }
185b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
186b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                log(TAG, "decoding with inSampleSize " +  options.inSampleSize);
187b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                bis.reset();
188b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                options.inJustDecodeBounds = false;
189b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                image = BitmapFactory.decodeStream(bis, null, options);
190b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                rawLongSide = Math.max(options.outWidth, options.outHeight);
191b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                rawShortSide = Math.max(options.outWidth, options.outHeight);
19289ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                if (image != null && rawLongSide != -1 && rawShortSide != -1) {
19389ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                    ratio = Math.max((float) longSide / (float) rawLongSide,
19489ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                            (float) shortSide / (float) rawShortSide);
19589ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren
19689ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                    if (Math.abs(ratio - 1.0f) > 0.001) {
19789ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        log(TAG, "still too big, scaling down by " + ratio);
19889ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        options.outWidth = (int) (ratio * options.outWidth);
19989ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        options.outHeight = (int) (ratio * options.outHeight);
20089ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren
20189ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        image = Bitmap.createScaledBitmap(image,
20289ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                                options.outWidth, options.outHeight,
20389ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                                true);
20489ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                    }
205b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
20689ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                    if (data.orientation != 0) {
20789ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        log(TAG, "rotated by " + data.orientation + ": fixing");
2089ab175f5902c66a266261ccf191bc2d213918ecaChris Wren                        Matrix matrix = new Matrix();
2099ab175f5902c66a266261ccf191bc2d213918ecaChris Wren                        matrix.setRotate(data.orientation,
2109ab175f5902c66a266261ccf191bc2d213918ecaChris Wren                                (float) Math.floor(image.getWidth() / 2f),
2119ab175f5902c66a266261ccf191bc2d213918ecaChris Wren                                (float) Math.floor(image.getHeight() / 2f));
2129ab175f5902c66a266261ccf191bc2d213918ecaChris Wren                        image = Bitmap.createBitmap(image, 0, 0,
2139ab175f5902c66a266261ccf191bc2d213918ecaChris Wren                                                    options.outWidth, options.outHeight,
2149ab175f5902c66a266261ccf191bc2d213918ecaChris Wren                                                    matrix, true);
21589ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        if (data.orientation == 90 || data.orientation == 270) {
21689ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                            int tmp = options.outWidth;
21789ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                            options.outWidth = options.outHeight;
21889ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                            options.outHeight = tmp;
21989ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        }
22083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren                    }
221b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren
22289ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                    log(TAG, "returning bitmap " + image.getWidth() + ", " + image.getHeight());
22389ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                } else {
22489ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                    image = null;
22589ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                }
226b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            } else {
22789ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                image = null;
22889ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren            }
22989ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren            if (image == null) {
23089ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                log(TAG, "Stream decoding failed with no error" +
23189ada9791087c8004f300ddd9e8ba58fbc84eaaeChris Wren                        (options.mCancel ? " due to cancelation." : "."));
232b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            }
2335006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren        } catch (OutOfMemoryError ome) {
2345006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren            log(TAG, "OUT OF MEMORY: " + ome);
2355006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren            image = null;
236b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        } catch (FileNotFoundException fnf) {
237b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            log(TAG, "file not found: " + fnf);
2385006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren            image = null;
239b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        } catch (IOException ioe) {
240b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            log(TAG, "i/o exception: " + ioe);
2415006d4093ad1455ee98c157a71f57e9ea42b4daeChris Wren            image = null;
242b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren        } finally {
243b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            try {
244b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                if (is != null) {
245b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                    is.close();
24683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren                }
247b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren            } catch (Throwable t) {
248b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren                log(TAG, "close fail: " + t.toString());
24983fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren            }
25083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        }
25183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
25283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        return image;
25383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    }
25483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
25583fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    public void setSeed(long seed) {
25683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        mRNG.setSeed(seed);
25783fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    }
25883fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
259b8235acb0fdc33c50e864ec801b93b9750d7600cChris Wren    protected static void log(String tag, String message) {
26083fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        if (DEBUG) {
26183fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren            Log.i(tag, message);
26283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren        }
26383fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    }
26483fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren
265bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren    protected int pickRandomStart(int total, int max) {
266bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren        if (max >= total) {
267bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren            return -1;
268bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren        } else {
269bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren            return (mRNG.nextInt() % (total - max)) - 1;
270bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren        }
271bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren    }
272bed703ef28f013639db3cd7a6b8a6e94d61075daChris Wren
27388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren    public Bitmap naturalNext(Bitmap current, BitmapFactory.Options options,
27488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren            int longSide, int shortSide) {
27588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        Bitmap image = null;
27688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        ImageData data = mImageMap.get(current);
27788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        if (data != null) {
27888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren          ImageData next = data.naturalNext();
27988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren          if (next != null) {
28088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren            image = load(next, options, longSide, shortSide);
28188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren            mImageMap.put(image, next);
28288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren          }
28388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        }
28488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        return image;
28588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren    }
28688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren
28788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren    public Bitmap naturalPrevious(Bitmap current, BitmapFactory.Options options,
28888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren            int longSide, int shortSide) {
28988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        Bitmap image = null;
29088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        ImageData data = mImageMap.get(current);
29188d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        if (current != null) {
29288d80f4471c900628e2cb6eef23029b99af48e09Chris Wren          ImageData prev = data.naturalPrevious();
29388d80f4471c900628e2cb6eef23029b99af48e09Chris Wren          if (prev != null) {
29488d80f4471c900628e2cb6eef23029b99af48e09Chris Wren            image = load(prev, options, longSide, shortSide);
29588d80f4471c900628e2cb6eef23029b99af48e09Chris Wren            mImageMap.put(image, prev);
29688d80f4471c900628e2cb6eef23029b99af48e09Chris Wren          }
29788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        }
29888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren        return image;
29988d80f4471c900628e2cb6eef23029b99af48e09Chris Wren    }
30088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren
301bcfd4439d730a4d783a02596c8ab444796323aadChris Wren    public void donePaging(Bitmap current) {
302bcfd4439d730a4d783a02596c8ab444796323aadChris Wren        ImageData data = mImageMap.get(current);
303bcfd4439d730a4d783a02596c8ab444796323aadChris Wren        if (data != null) {
304bcfd4439d730a4d783a02596c8ab444796323aadChris Wren            data.donePaging();
305bcfd4439d730a4d783a02596c8ab444796323aadChris Wren        }
306bcfd4439d730a4d783a02596c8ab444796323aadChris Wren    }
307bcfd4439d730a4d783a02596c8ab444796323aadChris Wren
308bcfd4439d730a4d783a02596c8ab444796323aadChris Wren    public void recycle(Bitmap trash) {
309bcfd4439d730a4d783a02596c8ab444796323aadChris Wren        if (trash != null) {
310bcfd4439d730a4d783a02596c8ab444796323aadChris Wren            mImageMap.remove(trash);
311bcfd4439d730a4d783a02596c8ab444796323aadChris Wren            trash.recycle();
312bcfd4439d730a4d783a02596c8ab444796323aadChris Wren        }
313bcfd4439d730a4d783a02596c8ab444796323aadChris Wren    }
314bcfd4439d730a4d783a02596c8ab444796323aadChris Wren
315c6bebae5e07c0108294d05e33fbace209d2f9b0dChris Wren    protected abstract InputStream getStream(ImageData data, int longSide);
31683fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren    protected abstract Collection<ImageData> findImages(int howMany);
31788d80f4471c900628e2cb6eef23029b99af48e09Chris Wren    protected abstract ImageData naturalNext(ImageData current);
31888d80f4471c900628e2cb6eef23029b99af48e09Chris Wren    protected abstract ImageData naturalPrevious(ImageData current);
319bcfd4439d730a4d783a02596c8ab444796323aadChris Wren    protected abstract void donePaging(ImageData current);
32088d80f4471c900628e2cb6eef23029b99af48e09Chris Wren
321d85f53c69dead1f1f6c0290b8104422143bc5166Chris Wren    public abstract Collection<AlbumData> findAlbums();
32283fee9012b6d5c5940de5b96fe8d98653ba14c0dChris Wren}
323