summaryrefslogtreecommitdiffstats
path: root/src/com/android/dreams/phototable/PhotoTable.java
diff options
context:
space:
mode:
authorChris Wren <cwren@android.com>2012-08-29 15:57:23 -0400
committerChris Wren <cwren@android.com>2012-08-30 07:59:53 -0400
commit23f9b01ba3f51a33a2ba92bca7d7a53f25b1b146 (patch)
treec5f5fbc1a21c8d54e7de161eaa6318364e5b80ae /src/com/android/dreams/phototable/PhotoTable.java
parent20e2554251954f6757462ab13470c1ebdcfba62e (diff)
downloadandroid_packages_screensavers_PhotoTable-23f9b01ba3f51a33a2ba92bca7d7a53f25b1b146.tar.gz
android_packages_screensavers_PhotoTable-23f9b01ba3f51a33a2ba92bca7d7a53f25b1b146.tar.bz2
android_packages_screensavers_PhotoTable-23f9b01ba3f51a33a2ba92bca7d7a53f25b1b146.zip
PhotoTable Polish:
+ remove manual rotation + better icon + gradient background + remove tap to dismiss + (temporarily disabled) flick to replace Change-Id: I2177077e37ce07a131a67bfbd11f8a447775ab98
Diffstat (limited to 'src/com/android/dreams/phototable/PhotoTable.java')
-rw-r--r--src/com/android/dreams/phototable/PhotoTable.java453
1 files changed, 302 insertions, 151 deletions
diff --git a/src/com/android/dreams/phototable/PhotoTable.java b/src/com/android/dreams/phototable/PhotoTable.java
index 082b3b5..9066396 100644
--- a/src/com/android/dreams/phototable/PhotoTable.java
+++ b/src/com/android/dreams/phototable/PhotoTable.java
@@ -31,12 +31,13 @@ import android.os.AsyncTask;
import android.os.PowerManager;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.ViewPropertyAnimator;
import android.view.animation.DecelerateInterpolator;
-import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
import android.widget.ImageView;
@@ -68,6 +69,284 @@ public class PhotoTable extends Dream {
private Table mTable;
+ static class PhotoTouchListener implements View.OnTouchListener,
+ GestureDetector.OnGestureListener {
+ private static final int INVALID_POINTER = -1;
+ private static final int MAX_POINTER_COUNT = 10;
+ private final int mTouchSlop;
+ private final int mTapTimeout;
+ private final Table mTable;
+ private final GestureDetector mDetector;
+ private final float mBeta;
+ private final float mTableRatio;
+ private final boolean mEnableFling;
+ private View mTarget;
+ private float mInitialTouchX;
+ private float mInitialTouchY;
+ private float mInitialTouchA;
+ private long mInitialTouchTime;
+ private float mInitialTargetX;
+ private float mInitialTargetY;
+ private float mInitialTargetA;
+ private int mA = INVALID_POINTER;
+ private int mB = INVALID_POINTER;
+ private float[] pts = new float[MAX_POINTER_COUNT];
+
+ public PhotoTouchListener(Context context, Table table) {
+ mTable = table;
+ mDetector = new GestureDetector(context, this);
+ final ViewConfiguration configuration = ViewConfiguration.get(context);
+ mTouchSlop = configuration.getScaledTouchSlop();
+ mTapTimeout = configuration.getTapTimeout();
+ final Resources resources = context.getResources();
+ mBeta = resources.getInteger(R.integer.table_damping) / 1000000f;
+ mTableRatio = resources.getInteger(R.integer.table_ratio) / 1000000f;
+ mEnableFling = resources.getBoolean(R.bool.enable_fling);
+ }
+
+ /** Get angle defined by first two touches, in degrees */
+ private float getAngle(View target, MotionEvent ev) {
+ float alpha = 0f;
+ int a = ev.findPointerIndex(mA);
+ int b = ev.findPointerIndex(mB);
+ if (a >=0 && b >=0) {
+ alpha = (float) (Math.atan2(pts[2*a + 1] - pts[2*b + 1],
+ pts[2*a] - pts[2*b]) *
+ 180f / Math.PI);
+ }
+ return alpha;
+ }
+
+ private void resetTouch(View target) {
+ mInitialTouchX = -1;
+ mInitialTouchY = -1;
+ mInitialTouchA = 0f;
+ mInitialTargetX = (float) target.getX();
+ mInitialTargetY = (float) target.getY();
+ mInitialTargetA = (float) target.getRotation();
+ }
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ return false;
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float deltaX, float deltaY) {
+ return false;
+ }
+
+ @Override
+ public void onShowPress(MotionEvent e) {
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ return false;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float dX, float dY) {
+ if (!mEnableFling) {
+ return false;
+ }
+ pts[0] = dX;
+ pts[1] = dY;
+ mTarget.getMatrix().mapVectors(pts);
+ // velocity components in global coordinate frame
+ dX = - pts[0];
+ dY = - pts[1];
+
+ if (DEBUG) {
+ Log.i(TAG, "fling " + dX + ", " + dY);
+ }
+
+ final int idx = e2.getActionIndex();
+ pts[0] = e2.getX(idx);
+ pts[1] = e2.getY(idx);
+ mTarget.getMatrix().mapPoints(pts);
+ // starting position compionents in global corrdinate frame
+ final float x0 = pts[0];
+ final float y0 = pts[1];
+
+ // velocity
+ final float v = (float) Math.hypot(dX, dY);
+ // number of steps to come to a stop
+ final float n = (float) (- Math.log(v) / Math.log(mBeta));
+ // distance travelled before stopping
+ final float s = (float) (v * (1f - Math.pow(mBeta, n)) / (1f - mBeta));
+
+ // ending posiiton after stopping
+ final float x1 = x0 + s * dX / v;
+ final float y1 = y0 + s * dY / v;
+
+ if (DEBUG) {
+ Log.i(TAG, "fling v = " + v);
+ Log.i(TAG, "fling n = " + n);
+ Log.i(TAG, "fling s = " + n);
+ Log.i(TAG, "fling x0 = " + x0);
+ Log.i(TAG, "fling y0 = " + y0);
+ Log.i(TAG, "fling x1 = " + x1);
+ Log.i(TAG, "fling y1 = " + y1);
+ }
+
+ final float photoWidth = ((Integer) mTarget.getTag(R.id.photo_width)).floatValue();
+ final float photoHeight = ((Integer) mTarget.getTag(R.id.photo_height)).floatValue();
+ final float tableWidth = mTable.getWidth();
+ final float tableHeight = mTable.getHeight();
+
+ pts[0] = 0f;
+ pts[1] = 0f;
+ pts[2] = photoHeight;
+ pts[3] = photoWidth;
+ mTarget.getMatrix().mapPoints(pts);
+ pts[0] += x1;
+ pts[1] += y1;
+ pts[2] += x1;
+ pts[3] += y1;
+
+ boolean xOut = true;
+ boolean yOut = true;
+ for (int i = 0; i < 2; i++) {
+ if(pts[2 * i] >= 0f && pts[2 * i] < tableWidth) {
+ xOut = false;
+ if (DEBUG) {
+ Log.i(TAG, "fling x in: " + pts[2 * i]);
+ }
+ }
+ if(pts[2 * i + 1] >= 0f && pts[2 * i + 1] < tableHeight) {
+ yOut = false;
+ if (DEBUG) {
+ Log.i(TAG, "fling y in: " + pts[2 * i + 1]);
+ }
+ }
+ }
+ final View photo = mTarget;
+ ViewPropertyAnimator animator = photo.animate()
+ .withLayer()
+ .x(x1)
+ .y(y1)
+ .setDuration((int) (100f * n));
+
+ if (xOut || yOut) {
+ if (DEBUG) {
+ Log.i(TAG, "fling away");
+ }
+ animator.withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mTable.fadeAway(photo);
+ mTable.launch();
+ }
+ });
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onTouch(View target, MotionEvent ev) {
+ mTarget = target;
+ if (mDetector.onTouchEvent(ev)) {
+ return true;
+ }
+ final int action = ev.getActionMasked();
+
+ // compute raw coordinates
+ for(int i = 0; i < 10 && i < ev.getPointerCount(); i++) {
+ pts[i*2] = ev.getX(i);
+ pts[i*2 + 1] = ev.getY(i);
+ }
+ target.getMatrix().mapPoints(pts);
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mTable.moveToBackOfQueue(target);
+ mInitialTouchTime = ev.getEventTime();
+ mA = ev.getPointerId(ev.getActionIndex());
+ resetTouch(target);
+ break;
+
+ case MotionEvent.ACTION_POINTER_DOWN:
+ if (mB == INVALID_POINTER) {
+ mB = ev.getPointerId(ev.getActionIndex());
+ mInitialTouchA = getAngle(target, ev);
+ }
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ if (mB == ev.getPointerId(ev.getActionIndex())) {
+ mB = INVALID_POINTER;
+ mInitialTargetA = (float) target.getRotation();
+ }
+ if (mA == ev.getPointerId(ev.getActionIndex())) {
+ mA = mB;
+ resetTouch(target);
+ mB = INVALID_POINTER;
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE: {
+ if (mA != INVALID_POINTER) {
+ int idx = ev.findPointerIndex(mA);
+ float x = pts[2 * idx];
+ float y = pts[2 * idx + 1];
+ if (mInitialTouchX == -1 && mInitialTouchY == -1) {
+ mInitialTouchX = x;
+ mInitialTouchY = y;
+ }
+ if (mTable.getSelected() != target) {
+ target.animate().cancel();
+
+ target.setX((int) (mInitialTargetX + x - mInitialTouchX));
+ target.setY((int) (mInitialTargetY + y - mInitialTouchY));
+ if (mTable.mManualImageRotation && mB != INVALID_POINTER) {
+ float a = getAngle(target, ev);
+ target.setRotation(
+ (int) (mInitialTargetA + a - mInitialTouchA));
+ }
+ }
+ }
+ }
+ break;
+
+ case MotionEvent.ACTION_UP: {
+ if (mA != INVALID_POINTER) {
+ int idx = ev.findPointerIndex(mA);
+ float x = pts[2 * idx];
+ float y = pts[2 * idx + 1];
+ if (mInitialTouchX == -1 && mInitialTouchY == -1) {
+ mInitialTouchX = x;
+ mInitialTouchY = y;
+ }
+ double distance = Math.hypot(x - mInitialTouchX,
+ y - mInitialTouchY);
+ if (mTable.getSelected() == target) {
+ mTable.dropOnTable(target);
+ mTable.clearSelection();
+ } else if ((ev.getEventTime() - mInitialTouchTime) < mTapTimeout &&
+ distance < mTouchSlop) {
+ // tap
+ target.animate().cancel();
+ mTable.setSelection(target);
+ }
+ mA = INVALID_POINTER;
+ mB = INVALID_POINTER;
+ }
+ }
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ break;
+ }
+ return true;
+ }
+ }
+
public static class Table extends FrameLayout {
class Launcher implements Runnable {
private final Table mTable;
@@ -89,12 +368,14 @@ public class PhotoTable extends Dream {
private final Dream mDream;
private final int mDropPeriod;
private final float mImageRatio;
+ private final float mTableRatio;
+ private final float mImageRotationLimit;
+ private final boolean mManualImageRotation;
+ private final boolean mTapToExit;
private final int mTableCapacity;
private final int mInset;
private final LocalSource mLocalSource;
private final Resources mResources;
- private final int mTouchSlop;
- private final int mTapTimeout;
private boolean mStarted;
private boolean mIsLandscape;
private BitmapFactory.Options mOptions;
@@ -109,20 +390,21 @@ public class PhotoTable extends Dream {
super(dream, as);
mDream = dream;
mResources = getResources();
- setBackgroundColor(mResources.getColor(R.color.tabletop));
+ setBackground(mResources.getDrawable(R.drawable.table));
mInset = mResources.getDimensionPixelSize(R.dimen.photo_inset);
mDropPeriod = mResources.getInteger(R.integer.drop_period);
mImageRatio = mResources.getInteger(R.integer.image_ratio) / 1000000f;
+ mTableRatio = mResources.getInteger(R.integer.table_ratio) / 1000000f;
+ mImageRotationLimit = (float) mResources.getInteger(R.integer.max_image_rotation);
mTableCapacity = mResources.getInteger(R.integer.table_capacity);
+ mManualImageRotation = mResources.getBoolean(R.bool.enable_manual_image_rotation);
+ mTapToExit = mResources.getBoolean(R.bool.enable_tap_to_exit);
mOnTable = new LinkedList<View>();
mOptions = new BitmapFactory.Options();
mOptions.inTempStorage = new byte[32768];
mLocalSource = new LocalSource(getContext());
mLauncher = new Launcher(this);
mStarted = false;
- final ViewConfiguration configuration = ViewConfiguration.get(getContext());
- mTouchSlop = configuration.getScaledTouchSlop();
- mTapTimeout = configuration.getTapTimeout();
}
public boolean hasSelection() {
@@ -186,7 +468,9 @@ public class PhotoTable extends Dream {
dropOnTable(getSelected());
clearSelection();
} else {
- mDream.finish();
+ if (mTapToExit) {
+ mDream.finish();
+ }
}
return true;
}
@@ -352,13 +636,13 @@ public class PhotoTable extends Dream {
int width = ((Integer) photo.getTag(R.id.photo_width));
int height = ((Integer) photo.getTag(R.id.photo_height));
photo.setRotation(-100.0f);
- photo.setX(-width);
- photo.setY(-height);
+ photo.setX(-mLongSide);
+ photo.setY(-mLongSide);
dropOnTable(photo);
}
private void dropOnTable(final View photo) {
- float angle = randfrange(-60, 60f);
+ float angle = randfrange(-mImageRotationLimit, mImageRotationLimit);
PointF p = randInCenter((float) sRNG.nextGaussian(), (float) sRNG.nextGaussian(),
mWidth, mHeight);
float x = p.x;
@@ -371,8 +655,8 @@ public class PhotoTable extends Dream {
float width = (float) ((Integer) photo.getTag(R.id.photo_width)).intValue();
float height = (float) ((Integer) photo.getTag(R.id.photo_height)).intValue();
- x -= width / 2f;
- y -= height / 2f;
+ x -= mTableRatio * mLongSide / 2f;
+ y -= mTableRatio * mLongSide / 2f;
log("fixed offset is " + x + ", " + y);
float dx = x - x0;
@@ -386,12 +670,13 @@ public class PhotoTable extends Dream {
// toss onto table
photo.animate()
.withLayer()
- .scaleX(0.5f / mImageRatio)
- .scaleY(0.5f / mImageRatio)
+ .scaleX(mTableRatio / mImageRatio)
+ .scaleY(mTableRatio / mImageRatio)
.rotation(angle)
.x(x)
- .y(y)
+ .y(y)
.setDuration(duration)
+ .setInterpolator(new DecelerateInterpolator())
.withEndAction(new Runnable() {
@Override
public void run() {
@@ -401,141 +686,7 @@ public class PhotoTable extends Dream {
}
});
- photo.setOnTouchListener(new OnTouchListener() {
- private static final int INVALID_POINTER = -1;
- private static final int MAX_POINTER_COUNT = 10;
- private float mInitialTouchX;
- private float mInitialTouchY;
- private float mInitialTouchA;
- private long mInitialTouchTime;
- private float mInitialTargetX;
- private float mInitialTargetY;
- private float mInitialTargetA;
- private int mA = INVALID_POINTER;
- private int mB = INVALID_POINTER;
- private float[] pts = new float[MAX_POINTER_COUNT];
-
- /** Get angle defined by first two touches, in degrees */
- private float getAngle(View target, MotionEvent ev) {
- float alpha = 0f;
- int a = ev.findPointerIndex(mA);
- int b = ev.findPointerIndex(mB);
- if (a >=0 && b >=0) {
- alpha = (float) (Math.atan2(pts[2*a + 1] - pts[2*b + 1],
- pts[2*a] - pts[2*b]) *
- 180f / Math.PI);
- }
- return alpha;
- }
-
- private void resetTouch(View target) {
- mInitialTouchX = -1;
- mInitialTouchY = -1;
- mInitialTouchA = 0f;
- mInitialTargetX = (float) target.getX();
- mInitialTargetY = (float) target.getY();
- mInitialTargetA = (float) target.getRotation();
- }
-
- @Override
- public boolean onTouch(View target, MotionEvent ev) {
- final int action = ev.getActionMasked();
-
- // compute raw coordinates
- for(int i = 0; i < 10 && i < ev.getPointerCount(); i++) {
- pts[i*2] = ev.getX(i);
- pts[i*2 + 1] = ev.getY(i);
- }
- target.getMatrix().mapPoints(pts);
-
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- moveToBackOfQueue(target);
- mInitialTouchTime = ev.getEventTime();
- mA = ev.getPointerId(ev.getActionIndex());
- resetTouch(target);
- log("action down: " + mA + ", " + mB);
- break;
-
- case MotionEvent.ACTION_POINTER_DOWN:
- if (mB == INVALID_POINTER) {
- mB = ev.getPointerId(ev.getActionIndex());
- mInitialTouchA = getAngle(target, ev);
- }
- log("action pointer down: " + mA + ", " + mB);
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- if (mB == ev.getPointerId(ev.getActionIndex())) {
- mB = INVALID_POINTER;
- mInitialTargetA = (float) target.getRotation();
- }
- if (mA == ev.getPointerId(ev.getActionIndex())) {
- mA = mB;
- resetTouch(target);
- mB = INVALID_POINTER;
- }
- log("action pointer up: " + mA + ", " + mB);
- break;
-
- case MotionEvent.ACTION_MOVE: {
- if (mA != INVALID_POINTER) {
- log("action move: " + mA + ", " + mB);
- int idx = ev.findPointerIndex(mA);
- float x = pts[2 * idx];
- float y = pts[2 * idx + 1];
- if (mInitialTouchX == -1 && mInitialTouchY == -1) {
- mInitialTouchX = x;
- mInitialTouchY = y;
- }
- if (getSelected() != target) {
- target.animate().cancel();
-
- target.setX((int) (mInitialTargetX + x - mInitialTouchX));
- target.setY((int) (mInitialTargetY + y - mInitialTouchY));
- if (mB != INVALID_POINTER) {
- float a = getAngle(target, ev);
- target.setRotation(
- (int) (mInitialTargetA + a - mInitialTouchA));
- }
- }
- }
- }
- break;
-
- case MotionEvent.ACTION_UP: {
- if (mA != INVALID_POINTER) {
- int idx = ev.findPointerIndex(mA);
- float x = pts[2 * idx];
- float y = pts[2 * idx + 1];
- if (mInitialTouchX == -1 && mInitialTouchY == -1) {
- mInitialTouchX = x;
- mInitialTouchY = y;
- }
- double distance = Math.hypot(x - mInitialTouchX,
- y - mInitialTouchY);
- if (getSelected() == target) {
- dropOnTable(target);
- clearSelection();
- } else if ((ev.getEventTime() - mInitialTouchTime) < mTapTimeout &&
- distance < mTouchSlop) {
- // tap
- target.animate().cancel();
- setSelection(target);
- }
- mA = INVALID_POINTER;
- mB = INVALID_POINTER;
- log("action up: " + mA + ", " + mB);
- }
- }
- break;
-
- case MotionEvent.ACTION_CANCEL:
- break;
- }
- return true;
- }
- });
+ photo.setOnTouchListener(new PhotoTouchListener(getContext(), this));
}
/** wrap all orientations to the interval [-180, 180). */