diff options
author | Chris Wren <cwren@android.com> | 2012-09-05 17:35:10 -0400 |
---|---|---|
committer | Chris Wren <cwren@android.com> | 2012-09-05 17:38:47 -0400 |
commit | 83fee9012b6d5c5940de5b96fe8d98653ba14c0d (patch) | |
tree | efe4be787e7390528834a1d9e608cf66603808a0 /src/com | |
parent | c1501041b64faa6c205a93baf403c4c87a0c1acf (diff) | |
download | android_packages_screensavers_PhotoTable-83fee9012b6d5c5940de5b96fe8d98653ba14c0d.tar.gz android_packages_screensavers_PhotoTable-83fee9012b6d5c5940de5b96fe8d98653ba14c0d.tar.bz2 android_packages_screensavers_PhotoTable-83fee9012b6d5c5940de5b96fe8d98653ba14c0d.zip |
refactor photo sources and other cleanup.
Change-Id: I22fda6f1e443776133bc9e0e242d4a6081eb1ba3
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/android/dreams/phototable/LocalSource.java | 175 | ||||
-rw-r--r-- | src/com/android/dreams/phototable/PhotoCarousel.java | 49 | ||||
-rw-r--r-- | src/com/android/dreams/phototable/PhotoSource.java | 183 | ||||
-rw-r--r-- | src/com/android/dreams/phototable/PhotoSourcePlexor.java | 81 | ||||
-rw-r--r-- | src/com/android/dreams/phototable/PicasaSource.java | 194 | ||||
-rw-r--r-- | src/com/android/dreams/phototable/StockSource.java | 82 | ||||
-rw-r--r-- | src/com/android/dreams/phototable/Table.java | 63 |
7 files changed, 441 insertions, 386 deletions
diff --git a/src/com/android/dreams/phototable/LocalSource.java b/src/com/android/dreams/phototable/LocalSource.java index 4b260db..8dbc079 100644 --- a/src/com/android/dreams/phototable/LocalSource.java +++ b/src/com/android/dreams/phototable/LocalSource.java @@ -15,60 +15,52 @@ */ package com.android.dreams.phototable; -import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Matrix; import android.provider.MediaStore; -import android.util.Log; import java.io.FileInputStream; -import java.io.BufferedInputStream; -import java.util.Collections; +import java.io.InputStream; +import java.util.Collection; import java.util.LinkedList; -import java.util.Random; /** - * Picks a random image from the local store. + * Loads images from the local store. */ -public class LocalSource { +public class LocalSource extends PhotoSource { private static final String TAG = "PhotoTable.LocalSource"; - static final boolean DEBUG = false; - public static class ImageData { - public String path; - public int orientation; - } - - private final ContentResolver mResolver; - private final Context mContext; - private final LinkedList<ImageData> mImageQueue; - private final float mImageRatio; - private final int mMaxQueueSize; - private final Random mRNG; private int mNextPosition; + public static final int TYPE = 2; + public LocalSource(Context context) { - mContext = context; - mResolver = mContext.getContentResolver(); + super(context); + mSourceName = TAG; mNextPosition = -1; - mImageQueue = new LinkedList<ImageData>(); - mImageRatio = context.getResources().getInteger(R.integer.image_ratio) / 1000000f; - mMaxQueueSize = context.getResources().getInteger(R.integer.image_queue_size); - mRNG = new Random(); fillQueue(); } - private void fillQueue() { - log("filling queue"); - String[] projection = {MediaStore.Images.Media.DATA, MediaStore.Images.Media.ORIENTATION}; + @Override + protected Collection<ImageData> findImages(int howMany) { + log(TAG, "finding images"); + LinkedList<ImageData> foundImages = new LinkedList<ImageData>(); + + String[] projection = {MediaStore.Images.Media.DATA, MediaStore.Images.Media.ORIENTATION, + MediaStore.Images.Media.BUCKET_ID, MediaStore.Images.Media.BUCKET_DISPLAY_NAME}; + String[] selectionArgs = {}; // settings go here + String selection = ""; + for (String arg : selectionArgs) { + if (selection.length() > 0) { + selection += " OR "; + } + selection += MediaStore.Images.Media.BUCKET_ID + " = '" + arg + "'"; + } Cursor cursor = mResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - projection, null, null, null); + projection, selection, null, null); if (cursor != null) { - if (cursor.getCount() > mMaxQueueSize && mNextPosition == -1) { - mNextPosition = mRNG.nextInt() % (cursor.getCount() - mMaxQueueSize); + if (cursor.getCount() > howMany && mNextPosition == -1) { + mNextPosition = mRNG.nextInt() % (cursor.getCount() - howMany); } if (mNextPosition == -1) { mNextPosition = 0; @@ -77,14 +69,18 @@ public class LocalSource { int dataIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA); int orientationIndex = cursor.getColumnIndex(MediaStore.Images.Media.ORIENTATION); + int bucketIndex = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_ID); + int nameIndex = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME); + if (dataIndex < 0) { - log("can't find the DATA column!"); + log(TAG, "can't find the DATA column!"); } else { - while (mImageQueue.size() < mMaxQueueSize && !cursor.isAfterLast()) { + while (foundImages.size() < howMany && !cursor.isAfterLast()) { ImageData data = new ImageData(); - data.path = cursor.getString(dataIndex); + data.type = TYPE; + data.url = cursor.getString(dataIndex); data.orientation = cursor.getInt(orientationIndex); - mImageQueue.offer(data); + foundImages.offer(data); if (cursor.moveToNext()) { mNextPosition++; } @@ -93,101 +89,24 @@ public class LocalSource { mNextPosition = 0; } } + cursor.close(); } - Collections.shuffle(mImageQueue); - log("queue contains: " + mImageQueue.size() + " items."); + log(TAG, "found " + foundImages.size() + " items."); + return foundImages; } - public Bitmap next(BitmapFactory.Options options, int longSide, int shortSide) { - log("decoding a local resource to " + longSide + ", " + shortSide); - Bitmap image = null; - - if (mImageQueue.isEmpty()) { - fillQueue(); - } - - if (!mImageQueue.isEmpty()) { - ImageData data = mImageQueue.poll(); - - FileInputStream fis = null; - try { - log("decoding:" + data.path); - fis = new FileInputStream(data.path); - options.inJustDecodeBounds = true; - options.inSampleSize = 1; - BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options); - int rawLongSide = Math.max(options.outWidth, options.outHeight); - int rawShortSide = Math.min(options.outWidth, options.outHeight); - log("I see bounds of " + rawLongSide + ", " + rawShortSide); - - float ratio = Math.max((float) longSide / (float) rawLongSide, - (float) shortSide / (float) rawShortSide); - while (ratio < 0.5) { - options.inSampleSize *= 2; - ratio *= 2; - } - - log("decoding with inSampleSize " + options.inSampleSize); - options.inJustDecodeBounds = false; - image = BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options); - rawLongSide = Math.max(options.outWidth, options.outHeight); - rawShortSide = Math.max(options.outWidth, options.outHeight); - ratio = Math.max((float) longSide / (float) rawLongSide, - (float) shortSide / (float) rawShortSide); - - if (ratio < 1.0f) { - log("still too big, scaling down by " + ratio); - options.outWidth = (int) (ratio * options.outWidth); - options.outHeight = (int) (ratio * options.outHeight); - image = Bitmap.createScaledBitmap(image, - options.outWidth, options.outHeight, - true); - } - - if (data.orientation != 0) { - log("rotated by " + data.orientation + ": fixing"); - if (data.orientation == 90 || data.orientation == 270) { - int tmp = options.outWidth; - options.outWidth = options.outHeight; - options.outHeight = tmp; - } - Matrix matrix = new Matrix(); - matrix.setRotate(data.orientation, - (float) image.getWidth() / 2, - (float) image.getHeight() / 2); - image = Bitmap.createBitmap(image, 0, 0, - options.outHeight, options.outWidth, - matrix, true); - } - - log("returning bitmap sized to " + image.getWidth() + ", " + image.getHeight()); - } catch (Exception ex) { - log(ex.toString()); - return null; - } finally { - try { - if (fis != null) { - fis.close(); - } - } catch (Throwable t) { - log("close fail: " + t.toString()); - } - } - } else { - log("device has no local images."); + @Override + protected InputStream getStream(ImageData data) { + FileInputStream fis = null; + try { + log(TAG, "opening:" + data.url); + fis = new FileInputStream(data.url); + } catch (Exception ex) { + log(TAG, ex.toString()); + fis = null; } - return image; - } - - public void setSeed(long seed) { - mRNG.setSeed(seed); - } - - private void log(String message) { - if (DEBUG) { - Log.i(TAG, message); - } + return (InputStream) fis; } } diff --git a/src/com/android/dreams/phototable/PhotoCarousel.java b/src/com/android/dreams/phototable/PhotoCarousel.java index d704a88..838af18 100644 --- a/src/com/android/dreams/phototable/PhotoCarousel.java +++ b/src/com/android/dreams/phototable/PhotoCarousel.java @@ -30,6 +30,8 @@ import android.view.ViewPropertyAnimator; import android.widget.FrameLayout; import android.widget.ImageView; +import java.util.HashMap; + /** * A FrameLayout that holds two photos, back to back. */ @@ -37,9 +39,7 @@ public class PhotoCarousel extends FrameLayout { private static final String TAG = "PhotoCarousel"; private final Flipper mFlipper; - private final PicasaSource mPicasaSource; - private final LocalSource mLocalSource; - private final StockSource mStockSource; + private final PhotoSourcePlexor mPhotoSource; private final GestureDetector mGestureDetector; private final View[] mPanel; private final BitmapFactory.Options mOptions; @@ -48,6 +48,7 @@ public class PhotoCarousel extends FrameLayout { private boolean mOnce; private int mLongSide; private int mShortSide; + private final HashMap<View, Bitmap> mBitmapStore; class Flipper implements Runnable { @Override @@ -63,9 +64,9 @@ public class PhotoCarousel extends FrameLayout { mFlipDuration = resources.getInteger(R.integer.flip_duration); mOptions = new BitmapFactory.Options(); mOptions.inTempStorage = new byte[32768]; - mPicasaSource = new PicasaSource(context); - mLocalSource = new LocalSource(context); - mStockSource = new StockSource(context); + mPhotoSource = new PhotoSourcePlexor(context); + mBitmapStore = new HashMap<View, Bitmap>(); + mPanel = new View[2]; mFlipper = new Flipper(); mGestureDetector = new GestureDetector(context, @@ -88,34 +89,34 @@ public class PhotoCarousel extends FrameLayout { } private class PhotoLoadTask extends AsyncTask<Void, Void, Bitmap> { - private PhotoCarousel mCarousel; + private int mTries; private ImageView mDestination; - public PhotoLoadTask(PhotoCarousel carousel, View destination) { - mCarousel = carousel; + public PhotoLoadTask(View destination) { + mTries = 0; mDestination = (ImageView) destination; } + @Override public Bitmap doInBackground(Void... unused) { - Bitmap decodedPhoto = null; - decodedPhoto = mCarousel.mPicasaSource.next(mCarousel.mOptions, - mCarousel.mLongSide, mCarousel.mShortSide); - if (decodedPhoto == null) { - decodedPhoto = mCarousel.mLocalSource.next(mCarousel.mOptions, - mCarousel.mLongSide, mCarousel.mShortSide); - } - if (decodedPhoto == null) { - decodedPhoto = mCarousel.mStockSource.next(mCarousel.mOptions, - mCarousel.mLongSide, mCarousel.mShortSide); - } + Bitmap decodedPhoto = mPhotoSource.next(PhotoCarousel.this.mOptions, + PhotoCarousel.this.mLongSide, PhotoCarousel.this.mShortSide); return decodedPhoto; } @Override public void onPostExecute(Bitmap photo) { if (photo != null) { + Bitmap old = mBitmapStore.get(mDestination); mDestination.setImageBitmap(photo); - mCarousel.requestLayout(); + mBitmapStore.put(mDestination, photo); + if (old != null) { + old.recycle(); + } + PhotoCarousel.this.requestLayout(); + } else if (mTries < 3) { + mTries++; + this.execute(); } } }; @@ -166,7 +167,7 @@ public class PhotoCarousel extends FrameLayout { replaceAnim.withEndAction(new Runnable() { @Override public void run() { - new PhotoLoadTask(PhotoCarousel.this, replaceView) + new PhotoLoadTask(replaceView) .execute(); } }); @@ -192,8 +193,8 @@ public class PhotoCarousel extends FrameLayout { mPanel[0] = findViewById(R.id.front); mPanel[1] = findViewById(R.id.back); - new PhotoLoadTask(this, mPanel[0]).execute(); - new PhotoLoadTask(this, mPanel[1]).execute(); + new PhotoLoadTask(mPanel[0]).execute(); + new PhotoLoadTask(mPanel[1]).execute(); scheduleNext(mDropPeriod); } diff --git a/src/com/android/dreams/phototable/PhotoSource.java b/src/com/android/dreams/phototable/PhotoSource.java new file mode 100644 index 0000000..2851c6c --- /dev/null +++ b/src/com/android/dreams/phototable/PhotoSource.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.dreams.phototable; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.net.Uri; +import android.provider.MediaStore; +import android.util.Log; + +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.IOException; +import java.io.BufferedInputStream; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.Random; + +/** + * Picks a random image from a source of photos. + */ +public abstract class PhotoSource { + private static final String TAG = "PhotoTable.PhotoSource"; + private static final boolean DEBUG = false; + + // This should be large enough for BitmapFactory to decode the header so + // that we can mark and reset the input stream to avoid duplicate network i/o + private static final int BUFFER_SIZE = 128 * 1024; + + public static class ImageData { + public String id; + public String url; + public int orientation; + public int type; + } + + private final Context mContext; + private final LinkedList<ImageData> mImageQueue; + private final int mMaxQueueSize; + + protected final Resources mResources; + protected ContentResolver mResolver; + protected String mSourceName; + protected final Random mRNG; + + public PhotoSource(Context context) { + mSourceName = TAG; + mContext = context; + mResolver = mContext.getContentResolver(); + mResources = context.getResources(); + mImageQueue = new LinkedList<ImageData>(); + mMaxQueueSize = mResources.getInteger(R.integer.image_queue_size); + mRNG = new Random(); + } + + protected void fillQueue() { + log(TAG, "filling queue"); + mImageQueue.addAll(findImages(mMaxQueueSize - mImageQueue.size())); + Collections.shuffle(mImageQueue); + log(TAG, "queue contains: " + mImageQueue.size() + " items."); + } + + public Bitmap next(BitmapFactory.Options options, int longSide, int shortSide) { + log(TAG, "decoding a picasa resource to " + longSide + ", " + shortSide); + Bitmap image = null; + + if (mImageQueue.isEmpty()) { + fillQueue(); + } + + if (!mImageQueue.isEmpty()) { + ImageData data = mImageQueue.poll(); + InputStream is = null; + try { + is = getStream(data); + BufferedInputStream bis = new BufferedInputStream(is); + bis.mark(BUFFER_SIZE); + + options.inJustDecodeBounds = true; + options.inSampleSize = 1; + image = BitmapFactory.decodeStream(new BufferedInputStream(bis), null, options); + int rawLongSide = Math.max(options.outWidth, options.outHeight); + int rawShortSide = Math.min(options.outWidth, options.outHeight); + log(TAG, "I see bounds of " + rawLongSide + ", " + rawShortSide); + + if (rawLongSide != -1 && rawShortSide != -1) { + float ratio = Math.max((float) longSide / (float) rawLongSide, + (float) shortSide / (float) rawShortSide); + while (ratio < 0.5) { + options.inSampleSize *= 2; + ratio *= 2; + } + + log(TAG, "decoding with inSampleSize " + options.inSampleSize); + bis.reset(); + options.inJustDecodeBounds = false; + image = BitmapFactory.decodeStream(bis, null, options); + rawLongSide = Math.max(options.outWidth, options.outHeight); + rawShortSide = Math.max(options.outWidth, options.outHeight); + ratio = Math.max((float) longSide / (float) rawLongSide, + (float) shortSide / (float) rawShortSide); + + if (ratio < 1.0f) { + log(TAG, "still too big, scaling down by " + ratio); + options.outWidth = (int) (ratio * options.outWidth); + options.outHeight = (int) (ratio * options.outHeight); + image = Bitmap.createScaledBitmap(image, + options.outWidth, options.outHeight, + true); + } + + if (data.orientation != 0) { + log(TAG, "rotated by " + data.orientation + ": fixing"); + if (data.orientation == 90 || data.orientation == 270) { + int tmp = options.outWidth; + options.outWidth = options.outHeight; + options.outHeight = tmp; + } + Matrix matrix = new Matrix(); + matrix.setRotate(data.orientation, + (float) image.getWidth() / 2, + (float) image.getHeight() / 2); + image = Bitmap.createBitmap(image, 0, 0, + options.outHeight, options.outWidth, + matrix, true); + } + + log(TAG, "returning bitmap " + image.getWidth() + ", " + image.getHeight()); + } else { + log(TAG, "decoding failed with no error: " + options.mCancel); + } + } catch (FileNotFoundException fnf) { + log(TAG, "file not found: " + fnf); + } catch (IOException ioe) { + log(TAG, "i/o exception: " + ioe); + } finally { + try { + if (is != null) { + is.close(); + } + } catch (Throwable t) { + log(TAG, "close fail: " + t.toString()); + } + } + } else { + log(TAG, mSourceName + " has no images."); + } + + return image; + } + + public void setSeed(long seed) { + mRNG.setSeed(seed); + } + + protected void log(String tag, String message) { + if (DEBUG) { + Log.i(tag, message); + } + } + + protected abstract InputStream getStream(ImageData data); + protected abstract Collection<ImageData> findImages(int howMany); +} diff --git a/src/com/android/dreams/phototable/PhotoSourcePlexor.java b/src/com/android/dreams/phototable/PhotoSourcePlexor.java new file mode 100644 index 0000000..4c4550e --- /dev/null +++ b/src/com/android/dreams/phototable/PhotoSourcePlexor.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.dreams.phototable; + +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; + +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.IOException; +import java.util.Collection; +import java.util.LinkedList; + +/** + * Loads images from a variety of sources. + */ +public class PhotoSourcePlexor extends PhotoSource { + private static final String TAG = "PhotoTable.PhotoSourcePlexor"; + + private final PhotoSource mPicasaSource; + private final PhotoSource mLocalSource; + private final PhotoSource mStockSource; + + public PhotoSourcePlexor(Context context) { + super(context); + mSourceName = TAG; + mPicasaSource = new PicasaSource(context); + mLocalSource = new LocalSource(context); + mStockSource = new StockSource(context); + } + + @Override + protected Collection<ImageData> findImages(int howMany) { + log(TAG, "finding images"); + LinkedList<ImageData> foundImages = new LinkedList<ImageData>(); + + foundImages.addAll(mPicasaSource.findImages(howMany)); + log(TAG, "found " + foundImages.size() + " network images"); + + foundImages.addAll(mLocalSource.findImages(howMany)); + log(TAG, "found " + foundImages.size() + " user images"); + + if (foundImages.isEmpty()) { + foundImages.addAll(mStockSource.findImages(howMany)); + } + log(TAG, "found " + foundImages.size() + " images"); + + return foundImages; + } + + @Override + protected InputStream getStream(ImageData data) { + switch (data.type) { + case PicasaSource.TYPE: + return mPicasaSource.getStream(data); + + case LocalSource.TYPE: + return mLocalSource.getStream(data); + + case StockSource.TYPE: + return mStockSource.getStream(data); + + default: + return null; + } + } +} diff --git a/src/com/android/dreams/phototable/PicasaSource.java b/src/com/android/dreams/phototable/PicasaSource.java index d049a54..061b66b 100644 --- a/src/com/android/dreams/phototable/PicasaSource.java +++ b/src/com/android/dreams/phototable/PicasaSource.java @@ -15,30 +15,21 @@ */ package com.android.dreams.phototable; -import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Matrix; import android.net.Uri; -import android.provider.MediaStore; -import android.util.Log; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.IOException; -import java.io.BufferedInputStream; -import java.util.Collections; +import java.util.Collection; import java.util.LinkedList; -import java.util.Random; /** - * Picks a random image from the local store. + * Loads images from Picasa. */ -public class PicasaSource { +public class PicasaSource extends PhotoSource { private static final String TAG = "PhotoTable.PicasaSource"; - private static final boolean DEBUG = false; private static final String PICASA_ID = "_id"; private static final String PICASA_URL = "content_url"; @@ -49,38 +40,21 @@ public class PicasaSource { private static final String PICASA_TYPE_KEY = "type"; private static final String PICASA_TYPE_THUMB_VALUE = "full"; - // This should be large enough for BitmapFactory to decode the header so - // that we can mark and reset the input stream to avoid duplicate network i/o - private static final int BUFFER_SIZE = 128 * 1024; - - public static class ImageData { - public String id; - public String url; - public String bucketId; - public int orientation; - } - - private final ContentResolver mResolver; - private final Context mContext; - private final LinkedList<ImageData> mImageQueue; - private final float mImageRatio; - private final int mMaxQueueSize; - private final Random mRNG; private int mNextPosition; + public static final int TYPE = 3; + public PicasaSource(Context context) { - mContext = context; - mResolver = mContext.getContentResolver(); + super(context); + mSourceName = TAG; mNextPosition = -1; - mImageQueue = new LinkedList<ImageData>(); - mImageRatio = context.getResources().getInteger(R.integer.image_ratio) / 1000000f; - mMaxQueueSize = context.getResources().getInteger(R.integer.image_queue_size); - mRNG = new Random(); fillQueue(); } - private void fillQueue() { - log("filling queue"); + @Override + protected Collection<ImageData> findImages(int howMany) { + log(TAG, "finding images"); + LinkedList<ImageData> foundImages = new LinkedList<ImageData>(); String[] projection = {PICASA_ID, PICASA_URL, PICASA_ROTATION, PICASA_ALBUM_ID}; String[] selectionArgs = {}; // settings go here String selection = ""; @@ -97,16 +71,14 @@ public class PicasaSource { Cursor cursor = mResolver.query(picasaUriBuilder.build(), projection, selection, null, null); if (cursor != null) { - if (cursor.getCount() > mMaxQueueSize && mNextPosition == -1) { - log("getcount: " + cursor.getCount()); - log("mMaxQueueSize: " + mMaxQueueSize); + if (cursor.getCount() > howMany && mNextPosition == -1) { mNextPosition = - (int) Math.abs(mRNG.nextInt() % (cursor.getCount() - mMaxQueueSize)); + (int) Math.abs(mRNG.nextInt() % (cursor.getCount() - howMany)); } if (mNextPosition == -1) { mNextPosition = 0; } - log("moving to position: " + mNextPosition); + log(TAG, "moving to position: " + mNextPosition); cursor.moveToPosition(mNextPosition); int idIndex = cursor.getColumnIndex(PICASA_ID); @@ -115,22 +87,19 @@ public class PicasaSource { int bucketIndex = cursor.getColumnIndex(PICASA_ALBUM_ID); if (idIndex < 0) { - log("can't find the ID column!"); + log(TAG, "can't find the ID column!"); } else { - while (mImageQueue.size() < mMaxQueueSize && !cursor.isAfterLast()) { + while (foundImages.size() < howMany && !cursor.isAfterLast()) { if (idIndex >= 0) { ImageData data = new ImageData(); + data.type = TYPE; data.id = cursor.getString(idIndex); - if (bucketIndex >= 0) { - data.bucketId = cursor.getString(bucketIndex); - } - if (urlIndex >= 0) { data.url = cursor.getString(urlIndex); } - mImageQueue.offer(data); + foundImages.offer(data); } if (cursor.moveToNext()) { mNextPosition++; @@ -143,115 +112,32 @@ public class PicasaSource { cursor.close(); } - Collections.shuffle(mImageQueue); - log("queue contains: " + mImageQueue.size() + " items."); + log(TAG, "found " + foundImages.size() + " items."); + return foundImages; } - public Bitmap next(BitmapFactory.Options options, int longSide, int shortSide) { - log("decoding a picasa resource to " + longSide + ", " + shortSide); - Bitmap image = null; - - if (mImageQueue.isEmpty()) { - fillQueue(); - } - - - if (!mImageQueue.isEmpty()) { - ImageData data = mImageQueue.poll(); - InputStream is = null; - try { - log("bucket is: " + data.bucketId); - - options.inJustDecodeBounds = false; - Uri.Builder photoUriBuilder = new Uri.Builder() - .scheme("content") - .authority("com.google.android.gallery3d.GooglePhotoProvider") - .appendPath("photos") - .appendPath(data.id) - .appendQueryParameter(PICASA_TYPE_KEY, PICASA_TYPE_THUMB_VALUE); - if (data.url != null) { - photoUriBuilder.appendQueryParameter(PICASA_URL_KEY, data.url); - } - is = mResolver.openInputStream(photoUriBuilder.build()); - BufferedInputStream bis = new BufferedInputStream(is); - bis.mark(BUFFER_SIZE); - - options.inJustDecodeBounds = true; - options.inSampleSize = 1; - BitmapFactory.decodeStream(new BufferedInputStream(bis), null, options); - int rawLongSide = Math.max(options.outWidth, options.outHeight); - int rawShortSide = Math.min(options.outWidth, options.outHeight); - log("I see bounds of " + rawLongSide + ", " + rawShortSide); - - float ratio = Math.max((float) longSide / (float) rawLongSide, - (float) shortSide / (float) rawShortSide); - while (ratio < 0.5) { - options.inSampleSize *= 2; - ratio *= 2; - } - - log("decoding with inSampleSize " + options.inSampleSize); - bis.reset(); - options.inJustDecodeBounds = false; - image = BitmapFactory.decodeStream(bis, null, options); - rawLongSide = Math.max(options.outWidth, options.outHeight); - rawShortSide = Math.max(options.outWidth, options.outHeight); - ratio = Math.max((float) longSide / (float) rawLongSide, - (float) shortSide / (float) rawShortSide); - - if (ratio < 1.0f) { - log("still too big, scaling down by " + ratio); - options.outWidth = (int) (ratio * options.outWidth); - options.outHeight = (int) (ratio * options.outHeight); - image = Bitmap.createScaledBitmap(image, - options.outWidth, options.outHeight, - true); - } - - if (data.orientation != 0) { - log("rotated by " + data.orientation + ": fixing"); - if (data.orientation == 90 || data.orientation == 270) { - int tmp = options.outWidth; - options.outWidth = options.outHeight; - options.outHeight = tmp; - } - Matrix matrix = new Matrix(); - matrix.setRotate(data.orientation, - (float) image.getWidth() / 2, - (float) image.getHeight() / 2); - image = Bitmap.createBitmap(image, 0, 0, - options.outHeight, options.outWidth, - matrix, true); - } - - log("returning bitmap sized to " + image.getWidth() + ", " + image.getHeight()); - } catch (FileNotFoundException fnf) { - log("file not found: " + fnf); - } catch (IOException ioe) { - log("i/o exception: " + ioe); - } finally { - try { - if (is != null) { - is.close(); - } - } catch (Throwable t) { - log("close fail: " + t.toString()); - } + @Override + protected InputStream getStream(ImageData data) { + InputStream is = null; + try { + Uri.Builder photoUriBuilder = new Uri.Builder() + .scheme("content") + .authority("com.google.android.gallery3d.GooglePhotoProvider") + .appendPath("photos") + .appendPath(data.id) + .appendQueryParameter(PICASA_TYPE_KEY, PICASA_TYPE_THUMB_VALUE); + if (data.url != null) { + photoUriBuilder.appendQueryParameter(PICASA_URL_KEY, data.url); } - } else { - log("device has no picasa images."); + is = mResolver.openInputStream(photoUriBuilder.build()); + } catch (FileNotFoundException fnf) { + log(TAG, "file not found: " + fnf); + is = null; + } catch (IOException ioe) { + log(TAG, "i/o exception: " + ioe); + is = null; } - return image; - } - - public void setSeed(long seed) { - mRNG.setSeed(seed); - } - - private void log(String message) { - if (DEBUG) { - Log.i(TAG, message); - } + return is; } } diff --git a/src/com/android/dreams/phototable/StockSource.java b/src/com/android/dreams/phototable/StockSource.java index 829ba86..63e834b 100644 --- a/src/com/android/dreams/phototable/StockSource.java +++ b/src/com/android/dreams/phototable/StockSource.java @@ -15,21 +15,18 @@ */ package com.android.dreams.phototable; -import android.content.ContentResolver; import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.util.Log; -import java.util.Random; +import java.io.InputStream; +import java.util.Collection; +import java.util.LinkedList; /** * Picks a random image from the local store. */ -public class StockSource { +public class StockSource extends PhotoSource { private static final String TAG = "PhotoTable.StockSource"; - private static final boolean DEBUG = false; private static final int[] PHOTOS = {R.drawable.photo_044_002, R.drawable.photo_039_002, R.drawable.photo_059_003, @@ -43,54 +40,43 @@ public class StockSource { R.drawable.photo_147_002, R.drawable.photo_175_004 }; - private static Random sRNG = new Random(); - private final Context mContext; - private final Resources mResources; - public StockSource(Context context) { - mContext = context; - mResources = context.getResources(); - } + private final LinkedList<ImageData> mImageList; + private int mNextPosition; - public Bitmap next(BitmapFactory.Options options, int longSide, int shortSide) { - log("decoding a local resource to " + longSide + ", " + shortSide); - int photo = PHOTOS[Math.abs(sRNG.nextInt() % PHOTOS.length)]; + public static final int TYPE = 1; - options.inJustDecodeBounds = true; - options.inSampleSize = 1; - BitmapFactory.decodeResource(mResources, photo, options); - int rawLongSide = Math.max(options.outWidth, options.outHeight); - int rawShortSide = Math.max(options.outWidth, options.outHeight); - log("I see bounds of " + rawLongSide + ", " + rawShortSide); - float ratio = Math.min((float) longSide / (float) rawLongSide, - (float) shortSide / (float) rawShortSide); - while (ratio < 0.5) { - options.inSampleSize *= 2; - ratio *= 2; - } - log("decoding with inSampleSize " + options.inSampleSize); - options.inJustDecodeBounds = false; - Bitmap bitmap = BitmapFactory.decodeResource(mResources, photo, options); - rawLongSide = Math.max(options.outWidth, options.outHeight); - rawShortSide = Math.max(options.outWidth, options.outHeight); - ratio = Math.min((float) longSide / (float) rawLongSide, - (float) shortSide / (float) rawShortSide); + public StockSource(Context context) { + super(context); + mSourceName = TAG; + mImageList = new LinkedList<ImageData>(); + fillQueue(); + } - if (ratio < 1.0f) { - log("still too big, scaling down by " + ratio); - int photoWidth = (int) (ratio * options.outWidth); - int photoHeight = (int) (ratio * options.outHeight); - bitmap = Bitmap.createScaledBitmap(bitmap, photoWidth, photoHeight, true); + @Override + protected Collection<ImageData> findImages(int howMany) { + if (mImageList.isEmpty()) { + for (int i = 0; i < PHOTOS.length; i++) { + ImageData data = new ImageData(); + data.type = TYPE; + data.id = Integer.toString(PHOTOS[i]); + mImageList.offer(data); + } } - - log("returning bitmap sized to " + bitmap.getWidth() + ", " + bitmap.getHeight()); - return bitmap; + return mImageList; } - private static void log(String message) { - if (DEBUG) { - Log.i(TAG, message); + @Override + protected InputStream getStream(ImageData data) { + InputStream is = null; + try { + log(TAG, "opening:" + data.id); + is = mResources.openRawResource(Integer.valueOf(data.id)); + } catch (Exception ex) { + log(TAG, ex.toString()); + is = null; } - } + return is; + } } diff --git a/src/com/android/dreams/phototable/Table.java b/src/com/android/dreams/phototable/Table.java index 8fc90bf..d673175 100644 --- a/src/com/android/dreams/phototable/Table.java +++ b/src/com/android/dreams/phototable/Table.java @@ -73,8 +73,7 @@ public class Table extends FrameLayout { private final boolean mTapToExit; private final int mTableCapacity; private final int mInset; - private final LocalSource mLocalSource; - private final StockSource mStockSource; + private final PhotoSourcePlexor mPhotoSource; private final Resources mResources; private PhotoLaunchTask mPhotoLaunchTask; private boolean mStarted; @@ -104,8 +103,7 @@ public class Table extends FrameLayout { mOnTable = new LinkedList<View>(); mOptions = new BitmapFactory.Options(); mOptions.inTempStorage = new byte[32768]; - mLocalSource = new LocalSource(getContext()); - mStockSource = new StockSource(getContext()); + mPhotoSource = new PhotoSourcePlexor(getContext()); mLauncher = new Launcher(this); mStarted = false; } @@ -210,42 +208,39 @@ public class Table extends FrameLayout { return true; } - static class PhotoLaunchTask extends AsyncTask<Void, Void, View> { - private Table mTable; - public PhotoLaunchTask(Table table) { - mTable = table; + private class PhotoLaunchTask extends AsyncTask<Void, Void, View> { + private int mTries; + public PhotoLaunchTask() { + mTries = 0; } + @Override public View doInBackground(Void... unused) { log("load a new photo"); - LayoutInflater inflater = (LayoutInflater) mTable.getContext() + LayoutInflater inflater = (LayoutInflater) Table.this.getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); View photo = inflater.inflate(R.layout.photo, null); ImageView image = (ImageView) photo; Drawable[] layers = new Drawable[2]; - Bitmap decodedPhoto = null; - decodedPhoto = mTable.mLocalSource.next(mTable.mOptions, - mTable.mLongSide, mTable.mShortSide); - if (decodedPhoto == null) { - decodedPhoto = mTable.mStockSource.next(mTable.mOptions, - mTable.mLongSide, mTable.mShortSide); - } - int photoWidth = mTable.mOptions.outWidth; - int photoHeight = mTable.mOptions.outHeight; - if (mTable.mOptions.outWidth <= 0 || mTable.mOptions.outHeight <= 0) { + Bitmap decodedPhoto = Table.this.mPhotoSource.next(Table.this.mOptions, + Table.this.mLongSide, Table.this.mShortSide); + int photoWidth = Table.this.mOptions.outWidth; + int photoHeight = Table.this.mOptions.outHeight; + if (Table.this.mOptions.outWidth <= 0 || Table.this.mOptions.outHeight <= 0) { photo = null; } else { - layers[0] = new BitmapDrawable(mTable.mResources, decodedPhoto); - layers[1] = mTable.mResources.getDrawable(R.drawable.frame); + layers[0] = new BitmapDrawable(Table.this.mResources, decodedPhoto); + layers[1] = Table.this.mResources.getDrawable(R.drawable.frame); LayerDrawable layerList = new LayerDrawable(layers); - layerList.setLayerInset(0, mTable.mInset, mTable.mInset, - mTable.mInset, mTable.mInset); + layerList.setLayerInset(0, Table.this.mInset, Table.this.mInset, + Table.this.mInset, Table.this.mInset); image.setImageDrawable(layerList); photo.setTag(R.id.photo_width, new Integer(photoWidth)); photo.setTag(R.id.photo_height, new Integer(photoHeight)); - photo.setOnTouchListener(new PhotoTouchListener(mTable.getContext(), mTable)); + photo.setOnTouchListener(new PhotoTouchListener(Table.this.getContext(), + Table.this)); } return photo; @@ -254,19 +249,23 @@ public class Table extends FrameLayout { @Override public void onPostExecute(View photo) { if (photo != null) { - mTable.addView(photo, new LayoutParams(LayoutParams.WRAP_CONTENT, + Table.this.addView(photo, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); - if (mTable.hasSelection()) { - mTable.bringChildToFront(mTable.getSelected()); + if (Table.this.hasSelection()) { + Table.this.bringChildToFront(Table.this.getSelected()); } int width = ((Integer) photo.getTag(R.id.photo_width)).intValue(); int height = ((Integer) photo.getTag(R.id.photo_height)).intValue(); log("drop it"); - mTable.throwOnTable(photo); - } - if(mTable.mOnTable.size() < mTable.mTableCapacity) { - mTable.scheduleNext(mTable.mFastDropPeriod); + Table.this.throwOnTable(photo); + + if(Table.this.mOnTable.size() < Table.this.mTableCapacity) { + Table.this.scheduleNext(Table.this.mFastDropPeriod); + } + } else if (mTries < 3) { + mTries++; + this.execute(); } } }; @@ -282,7 +281,7 @@ public class Table extends FrameLayout { log("inflate it"); if (mPhotoLaunchTask == null || mPhotoLaunchTask.getStatus() == AsyncTask.Status.FINISHED) { - mPhotoLaunchTask = new PhotoLaunchTask(this); + mPhotoLaunchTask = new PhotoLaunchTask(); mPhotoLaunchTask.execute(); } } |