summaryrefslogtreecommitdiffstats
path: root/src/com/android/dreams/phototable
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/dreams/phototable')
-rw-r--r--src/com/android/dreams/phototable/LocalSource.java51
-rw-r--r--src/com/android/dreams/phototable/PhotoDreamSettingsReceiver.java69
-rw-r--r--src/com/android/dreams/phototable/PhotoSource.java14
-rw-r--r--src/com/android/dreams/phototable/PhotoTable.java119
-rw-r--r--src/com/android/dreams/phototable/PicasaSource.java20
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;