diff options
Diffstat (limited to 'src/com/android/dreams/phototable')
5 files changed, 207 insertions, 66 deletions
diff --git a/src/com/android/dreams/phototable/LocalSource.java b/src/com/android/dreams/phototable/LocalSource.java index cf2e0ec..a483d60 100644 --- a/src/com/android/dreams/phototable/LocalSource.java +++ b/src/com/android/dreams/phototable/LocalSource.java @@ -18,6 +18,7 @@ package com.android.dreams.phototable; import android.content.Context; import android.content.SharedPreferences; import android.database.Cursor; +import android.net.Uri; import android.provider.MediaStore; import java.io.FileInputStream; @@ -58,12 +59,21 @@ public class LocalSource extends CursorPhotoSource { public Collection<AlbumData> findAlbums() { log(TAG, "finding albums"); HashMap<String, AlbumData> foundAlbums = new HashMap<String, AlbumData>(); + findAlbums(false, foundAlbums); + findAlbums(true, foundAlbums); + log(TAG, "found " + foundAlbums.size() + " items."); + mFoundAlbumIds = foundAlbums.keySet(); + return foundAlbums.values(); + } + + public void findAlbums(boolean internal, HashMap<String, AlbumData> foundAlbums) { + Uri uri = internal ? MediaStore.Images.Media.INTERNAL_CONTENT_URI + : MediaStore.Images.Media.EXTERNAL_CONTENT_URI; String[] projection = {MediaStore.Images.Media.DATA, MediaStore.Images.Media.BUCKET_ID, MediaStore.Images.Media.BUCKET_DISPLAY_NAME, MediaStore.Images.Media.DATE_TAKEN}; // This is a horrible hack that closes the where clause and injects a grouping clause. - Cursor cursor = mResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - projection, null, null, null); + Cursor cursor = mResolver.query(uri, projection, null, null, null); if (cursor != null) { cursor.moveToPosition(-1); @@ -76,7 +86,7 @@ public class LocalSource extends CursorPhotoSource { log(TAG, "can't find the ID column!"); } else { while (cursor.moveToNext()) { - String id = TAG + ":" + cursor.getString(bucketIndex); + String id = constructId(internal, cursor.getString(bucketIndex)); AlbumData data = foundAlbums.get(id); if (foundAlbums.get(id) == null) { data = new AlbumData(); @@ -105,11 +115,11 @@ public class LocalSource extends CursorPhotoSource { } } cursor.close(); - } - log(TAG, "found " + foundAlbums.size() + " items."); - mFoundAlbumIds = foundAlbums.keySet(); - return foundAlbums.values(); + } + + public static String constructId(boolean internal, String bucketId) { + return TAG + ":" + bucketId + (internal ? ":i" : ""); } @Override @@ -120,8 +130,7 @@ public class LocalSource extends CursorPhotoSource { MediaStore.Images.Media.BUCKET_ID, MediaStore.Images.Media.BUCKET_DISPLAY_NAME}; String selection = MediaStore.Images.Media.BUCKET_ID + " = '" + data.albumId + "'"; - data.cursor = mResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - projection, selection, null, null); + data.cursor = mResolver.query(data.uri, projection, selection, null, null); } @Override @@ -169,12 +178,21 @@ public class LocalSource extends CursorPhotoSource { protected Collection<ImageData> findImages(int howMany) { log(TAG, "finding images"); LinkedList<ImageData> foundImages = new LinkedList<ImageData>(); + boolean internalFirst = mRNG.nextInt(2) == 0; // filp a coin to be fair + findImages(internalFirst, howMany, foundImages); + findImages(!internalFirst, howMany - foundImages.size(), foundImages); + log(TAG, "found " + foundImages.size() + " items."); + return foundImages; + } + protected void findImages(boolean internal, int howMany, LinkedList<ImageData> foundImages ) { + Uri uri = internal ? MediaStore.Images.Media.INTERNAL_CONTENT_URI + : MediaStore.Images.Media.EXTERNAL_CONTENT_URI; String[] projection = {MediaStore.Images.Media.DATA, MediaStore.Images.Media.ORIENTATION, MediaStore.Images.Media.BUCKET_ID, MediaStore.Images.Media.BUCKET_DISPLAY_NAME}; String selection = ""; for (String id : getFoundAlbums()) { - if (mSettings.isAlbumEnabled(id)) { + if (isInternalId(id) == internal && mSettings.isAlbumEnabled(id)) { String[] parts = id.split(":"); if (parts.length > 1) { if (selection.length() > 0) { @@ -185,11 +203,9 @@ public class LocalSource extends CursorPhotoSource { } } if (selection.isEmpty()) { - return foundImages; + return; } - - Cursor cursor = mResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - projection, selection, null, null); + Cursor cursor = mResolver.query(uri, projection, selection, null, null); if (cursor != null) { int dataIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA); @@ -203,6 +219,7 @@ public class LocalSource extends CursorPhotoSource { } else { while (foundImages.size() < howMany && cursor.moveToNext()) { ImageData data = unpackImageData(cursor, null); + data.uri = uri; foundImages.offer(data); mLastPosition = cursor.getPosition(); } @@ -216,8 +233,10 @@ public class LocalSource extends CursorPhotoSource { cursor.close(); } - log(TAG, "found " + foundImages.size() + " items."); - return foundImages; + } + + private boolean isInternalId(String id) { + return id.endsWith("i"); } @Override diff --git a/src/com/android/dreams/phototable/PhotoDreamSettingsReceiver.java b/src/com/android/dreams/phototable/PhotoDreamSettingsReceiver.java new file mode 100644 index 0000000..208441b --- /dev/null +++ b/src/com/android/dreams/phototable/PhotoDreamSettingsReceiver.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2013 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.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +public class PhotoDreamSettingsReceiver extends BroadcastReceiver { + private static final String TAG = "PhotoDreamSettingsReceiver"; + private static final String LOCAL_AUTHORITY = "media"; + private static final String INTERNAL = "internal"; + private static final boolean DEBUG = false; + + public static final String ACTION_ADD_ALBUM = "add"; + public static final String ACTION_REMOVE_ALBUM = "remove"; + public static final String EXTRA_ALBUMS = "albums"; + + @Override + public void onReceive(Context context, Intent intent) { + AlbumSettings settings[] = { + AlbumSettings.getAlbumSettings( + context.getSharedPreferences(FlipperDreamSettings.PREFS_NAME, 0)), + AlbumSettings.getAlbumSettings( + context.getSharedPreferences(PhotoTableDreamSettings.PREFS_NAME, 0)) + }; + + boolean shown = ACTION_ADD_ALBUM.equals(intent.getAction()); + ArrayList<String> albumUris = intent.getStringArrayListExtra(EXTRA_ALBUMS); + for (String albumUriString: albumUris) { + Uri albumUri = Uri.parse(albumUriString); + String type = albumUri.getEncodedAuthority(); + List<String> path = albumUri.getPathSegments(); + + String albumId = null; + if (LOCAL_AUTHORITY.equals(type)) { + if (path.size() > 3) { + albumId = LocalSource.constructId(INTERNAL.equals(path.get(0)), path.get(3)); + } + } else { + if (path.size() > 1) { + albumId = PicasaSource.constructId(path.get(1)); + } + } + if (DEBUG) Log.d(TAG, "receive: " + albumId + " is " + shown); + for (int idx = 0; idx < settings.length; idx++) { + settings[idx].setAlbumEnabled(albumId, shown); + } + } + } +} diff --git a/src/com/android/dreams/phototable/PhotoSource.java b/src/com/android/dreams/phototable/PhotoSource.java index fc4cf7b..d05eace 100644 --- a/src/com/android/dreams/phototable/PhotoSource.java +++ b/src/com/android/dreams/phototable/PhotoSource.java @@ -23,6 +23,7 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; +import android.net.Uri; import android.util.Log; import java.io.BufferedInputStream; @@ -44,7 +45,7 @@ public abstract class PhotoSource { // 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; + private static final int BUFFER_SIZE = 32 * 1024; public class ImageData { public String id; @@ -54,6 +55,7 @@ public abstract class PhotoSource { protected String albumId; protected Cursor cursor; protected int position; + protected Uri uri; InputStream getStream(int longSide) { return PhotoSource.this.getStream(this, longSide); @@ -184,7 +186,15 @@ public abstract class PhotoSource { } log(TAG, "decoding with inSampleSize " + options.inSampleSize); - bis.reset(); + try { + bis.reset(); + } catch (IOException ioe) { + // start over, something went wrong and we read too far into the image. + bis.close(); + is = data.getStream(longSide); + bis = new BufferedInputStream(is); + log(TAG, "resetting the stream"); + } options.inJustDecodeBounds = false; image = BitmapFactory.decodeStream(bis, null, options); rawLongSide = Math.max(options.outWidth, options.outHeight); diff --git a/src/com/android/dreams/phototable/PhotoTable.java b/src/com/android/dreams/phototable/PhotoTable.java index 7e7f92e..5cdd70f 100644 --- a/src/com/android/dreams/phototable/PhotoTable.java +++ b/src/com/android/dreams/phototable/PhotoTable.java @@ -34,6 +34,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import android.view.ViewPropertyAnimator; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; @@ -44,7 +45,6 @@ import java.util.ArrayList; import java.util.Formatter; import java.util.HashSet; import java.util.LinkedList; -import java.util.List; import java.util.Random; import java.util.Set; @@ -105,11 +105,10 @@ public class PhotoTable extends FrameLayout { private final EdgeSwipeDetector mEdgeSwipeDetector; private final KeyboardInterpreter mKeyboardInterpreter; private final boolean mStoryModeEnabled; + private final boolean mBackgroudOptimization; private final long mPickUpDuration; private final int mMaxSelectionTime; private final int mMaxFocusTime; - private final List<View> mAnimating; - private DreamService mDream; private PhotoLaunchTask mPhotoLaunchTask; private LoadNaturalSiblingTask mLoadOnDeckTasks[]; @@ -125,6 +124,8 @@ public class PhotoTable extends FrameLayout { private int mHighlightColor; private ViewGroup mBackground; private ViewGroup mStageLeft; + private View mScrim; + private final Set<View> mWaitingToJoinBackground; public PhotoTable(Context context, AttributeSet as) { super(context, as); @@ -143,6 +144,7 @@ public class PhotoTable extends FrameLayout { mRedealCount = mResources.getInteger(R.integer.redeal_count); mTapToExit = mResources.getBoolean(R.bool.enable_tap_to_exit); mStoryModeEnabled = mResources.getBoolean(R.bool.enable_story_mode); + mBackgroudOptimization = mResources.getBoolean(R.bool.enable_background_optimization); mHighlightColor = mResources.getColor(R.color.highlight_color); mMaxSelectionTime = mResources.getInteger(R.integer.max_selection_time); mMaxFocusTime = mResources.getInteger(R.integer.max_focus_time); @@ -154,7 +156,7 @@ public class PhotoTable extends FrameLayout { mOnTable = new LinkedList<View>(); mPhotoSource = new PhotoSourcePlexor(getContext(), getContext().getSharedPreferences(PhotoTableDreamSettings.PREFS_NAME, 0)); - mAnimating = new ArrayList<View>(); + mWaitingToJoinBackground = new HashSet<View>(); mLauncher = new Launcher(); mFocusReaper = new FocusReaper(); mSelectionReaper = new SelectionReaper(); @@ -170,6 +172,7 @@ public class PhotoTable extends FrameLayout { public void onFinishInflate() { mBackground = (ViewGroup) findViewById(R.id.background); mStageLeft = (ViewGroup) findViewById(R.id.stageleft); + mScrim = findViewById(R.id.scrim); } public void setDream(DreamService dream) { @@ -283,7 +286,9 @@ public class PhotoTable extends FrameLayout { } public void setDefaultFocus() { - setFocus(mOnTable.getLast()); + if (mOnTable.size() > 0) { + setFocus(mOnTable.getLast()); + } } public void setFocus(View focus) { @@ -353,7 +358,9 @@ public class PhotoTable extends FrameLayout { public View moveFocus(View focus, float direction, float angle) { if (focus == null) { - setFocus(mOnTable.getLast()); + if (mOnTable.size() > 0) { + setFocus(mOnTable.getLast()); + } } else { final double alpha = Math.toRadians(direction); final double beta = Math.toRadians(Math.min(angle, 180f) / 2f); @@ -603,29 +610,60 @@ public class PhotoTable extends FrameLayout { /** De-emphasize the other photos on the table. */ public void fadeOutBackground(final View photo) { - mBackground.animate() - .withLayer() - .setDuration(mPickUpDuration) - .alpha(0f); + resolveBackgroundQueue(); + if (mBackgroudOptimization) { + mBackground.animate() + .withLayer() + .setDuration(mPickUpDuration) + .alpha(0f); + } else { + mScrim.setAlpha(0f); + mScrim.setVisibility(View.VISIBLE); + bringChildToFront(mScrim); + bringChildToFront(photo); + mScrim.animate() + .withLayer() + .setDuration(mPickUpDuration) + .alpha(1f); + } } /** Return the other photos to foreground status. */ public void fadeInBackground(final View photo) { - mAnimating.add(photo); - mBackground.animate() - .withLayer() - .setDuration(mPickUpDuration) - .alpha(1f) - .withEndAction(new Runnable() { - @Override - public void run() { - mAnimating.remove(photo); - if (!mAnimating.contains(photo)) { - moveToBackground(photo); - } - } - }); + if (mBackgroudOptimization) { + mWaitingToJoinBackground.add(photo); + mBackground.animate() + .withLayer() + .setDuration(mPickUpDuration) + .alpha(1f) + .withEndAction(new Runnable() { + @Override + public void run() { + resolveBackgroundQueue(); + } + }); + } else { + bringChildToFront(mScrim); + bringChildToFront(photo); + mScrim.animate() + .withLayer() + .setDuration(mPickUpDuration) + .alpha(0f) + .withEndAction(new Runnable() { + @Override + public void run() { + mScrim.setVisibility(View.GONE); + } + }); + } + } + + private void resolveBackgroundQueue() { + for(View photo: mWaitingToJoinBackground) { + moveToBackground(photo); + } + mWaitingToJoinBackground.clear(); } /** Dispose of the photo gracefully, in case we can see some of it. */ @@ -824,7 +862,7 @@ public class PhotoTable extends FrameLayout { log("animate it"); // toss onto table - mAnimating.add(photo); + resolveBackgroundQueue(); photo.animate() .withLayer() .scaleX(mTableRatio / mImageRatio) @@ -837,34 +875,34 @@ public class PhotoTable extends FrameLayout { .withEndAction(new Runnable() { @Override public void run() { - mAnimating.remove(photo); - if (!mAnimating.contains(photo)) { - moveToBackground(photo); - } + mWaitingToJoinBackground.add(photo); } }); } private void moveToBackground(View photo) { - if (!isInBackground(photo)) { - removeView(photo); + if (mBackgroudOptimization && !isInBackground(photo)) { + removeViewFromParent(photo); mBackground.addView(photo, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); } } private void exitStageLeft(View photo) { - if (isInBackground(photo)) { - mBackground.removeView(photo); - } else { - removeView(photo); - } + removeViewFromParent(photo); mStageLeft.addView(photo, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); } + private void removeViewFromParent(View photo) { + ViewParent parent = photo.getParent(); + if (parent != null) { // should never be null, just being paranoid + ((ViewGroup) parent).removeView(photo); + } + } + private void moveToForeground(View photo) { - if (isInBackground(photo)) { + if (mBackgroudOptimization && isInBackground(photo)) { mBackground.removeView(photo); addView(photo, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); @@ -872,7 +910,7 @@ public class PhotoTable extends FrameLayout { } private boolean isInBackground(View photo) { - return mBackground.indexOfChild(photo) != -1; + return mBackgroudOptimization && mBackground.indexOfChild(photo) != -1; } /** wrap all orientations to the interval [-180, 180). */ @@ -883,7 +921,7 @@ public class PhotoTable extends FrameLayout { return result; } - /** Animate the selected photo to the foregound: zooming in to bring it foreward. */ + /** Animate the selected photo to the foreground: zooming in to bring it forward. */ private void pickUp(final View photo) { float photoWidth = photo.getWidth(); float photoHeight = photo.getHeight(); @@ -899,6 +937,7 @@ public class PhotoTable extends FrameLayout { log("animate it"); // lift up to the glass for a good look + mWaitingToJoinBackground.remove(photo); moveToForeground(photo); photo.animate() .withLayer() @@ -937,7 +976,7 @@ public class PhotoTable extends FrameLayout { private void recycle(View photo) { if (photo != null) { - removeView(photo); + removeViewFromParent(photo); mPhotoSource.recycle(getBitmap(photo)); } } diff --git a/src/com/android/dreams/phototable/PicasaSource.java b/src/com/android/dreams/phototable/PicasaSource.java index 0db98af..26fd03e 100644 --- a/src/com/android/dreams/phototable/PicasaSource.java +++ b/src/com/android/dreams/phototable/PicasaSource.java @@ -67,7 +67,6 @@ public class PicasaSource extends CursorPhotoSource { private final int mMaxPostAblums; private final String mPostsAlbumName; - private final String mUploadsAlbumName; private final String mUnknownAlbumName; private final LinkedList<ImageData> mRecycleBin; private final ConnectivityManager mConnectivityManager; @@ -83,7 +82,6 @@ public class PicasaSource extends CursorPhotoSource { mLastPosition = INVALID; mMaxPostAblums = mResources.getInteger(R.integer.max_post_albums); mPostsAlbumName = mResources.getString(R.string.posts_album_name, "Posts"); - mUploadsAlbumName = mResources.getString(R.string.uploads_album_name, "Instant Uploads"); mUnknownAlbumName = mResources.getString(R.string.unknown_album_name, "Unknown"); mMaxRecycleSize = mResources.getInteger(R.integer.recycle_image_pool_size); mConnectivityManager = @@ -354,7 +352,7 @@ public class PicasaSource extends CursorPhotoSource { log(TAG, "can't find the ID column!"); } else { while (cursor.moveToNext()) { - String id = TAG + ":" + cursor.getString(idIndex); + String id = constructId(cursor.getString(idIndex)); String user = (userIndex >= 0 ? cursor.getString(userIndex) : "-1"); String type = (typeIndex >= 0 ? cursor.getString(typeIndex) : "none"); boolean isPosts = (typeIndex >= 0 && PICASA_POSTS_TYPE.equals(type)); @@ -369,12 +367,16 @@ public class PicasaSource extends CursorPhotoSource { if (isPosts) { log(TAG, "replacing " + id + " with " + PICASA_POSTS_TYPE); - id = TAG + ":" + PICASA_POSTS_TYPE + ":" + user; + id = constructId(PICASA_POSTS_TYPE + ":" + user); } if (isUpload) { - log(TAG, "replacing " + id + " with " + PICASA_UPLOAD_TYPE); - id = TAG + ":" + PICASA_UPLOAD_TYPE + ":" + user; + // check for old-style name for this album, and upgrade settings. + String uploadId = constructId(PICASA_UPLOAD_TYPE + ":" + user); + if (mSettings.isAlbumEnabled(uploadId)) { + mSettings.setAlbumEnabled(uploadId, false); + mSettings.setAlbumEnabled(id, true); + } } String thumbnailUrl = null; @@ -387,8 +389,6 @@ public class PicasaSource extends CursorPhotoSource { if (isPosts) { data.title = mPostsAlbumName; - } else if (isUpload) { - data.title = mUploadsAlbumName; } else if (titleIndex >= 0) { data.title = cursor.getString(titleIndex); } else { @@ -425,6 +425,10 @@ public class PicasaSource extends CursorPhotoSource { return foundAlbums.values(); } + public static String constructId(String serverId) { + return TAG + ":" + serverId; + } + @Override protected InputStream getStream(ImageData data, int longSide) { InputStream is = null; |