summaryrefslogtreecommitdiffstats
path: root/src/com/android/camera/ui/FilmStripView.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/camera/ui/FilmStripView.java')
-rw-r--r--src/com/android/camera/ui/FilmStripView.java164
1 files changed, 134 insertions, 30 deletions
diff --git a/src/com/android/camera/ui/FilmStripView.java b/src/com/android/camera/ui/FilmStripView.java
index bc56333d2..1805177e2 100644
--- a/src/com/android/camera/ui/FilmStripView.java
+++ b/src/com/android/camera/ui/FilmStripView.java
@@ -48,8 +48,11 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener {
private static final int BUFFER_SIZE = 5;
private static final int GEOMETRY_ADJUST_TIME_MS = 400;
private static final int SNAP_IN_CENTER_TIME_MS = 600;
+ private static final int ZOOM_ANIMATION_DURATION_MS = 200;
private static final float FILM_STRIP_SCALE = 0.6f;
private static final float FULL_SCREEN_SCALE = 1f;
+
+ private static final float TOLERANCE = 0.1f;
// Only check for intercepting touch events within first 500ms
private static final int SWIPE_TIME_OUT = 500;
@@ -548,15 +551,32 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener {
mView.setTranslationY(v.getTranslationY());
mView.setTranslationX(v.getTranslationX());
}
-
+ /**
+ * Apply a scale factor (i.e. {@param postScale}) on top of current scale at
+ * pivot point ({@param focusX}, {@param focusY}). Visually it should be the
+ * same as post concatenating current view's matrix with specified scale.
+ */
+ void postScale(float focusX, float focusY, float postScale, int viewportWidth,
+ int viewportHeight) {
+ float transX = getTranslationX();
+ float transY = getTranslationY();
+ // Pivot point is top left of the view, so we need to translate
+ // to scale around focus point
+ transX -= (focusX - getX()) * (postScale - 1f);
+ transY -= (focusY - getY()) * (postScale - 1f);
+ float scaleX = mView.getScaleX() * postScale;
+ float scaleY = mView.getScaleY() * postScale;
+ updateTransform(transX, transY, scaleX, scaleY, viewportWidth,
+ viewportHeight);
+ }
void updateTransform(float transX, float transY, float scaleX, float scaleY,
int viewportWidth, int viewportHeight) {
- int left = (int) transX + mView.getLeft();
- int top = (int) transY + mView.getTop();
- Rect r = ZoomView.adjustToFitInBounds(new Rect(left, top,
- left + (int) (mView.getWidth() * scaleX),
- top + (int) (mView.getHeight() * scaleY)),
+ float left = transX + mView.getLeft();
+ float top = transY + mView.getTop();
+ RectF r = ZoomView.adjustToFitInBounds(new RectF(left, top,
+ left + mView.getWidth() * scaleX,
+ top + mView.getHeight() * scaleY),
viewportWidth, viewportHeight);
mView.setScaleX(scaleX);
mView.setScaleY(scaleY);
@@ -1178,9 +1198,10 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener {
mDrawArea.top = t;
mDrawArea.right = r;
mDrawArea.bottom = b;
-
- resetZoomView();
- layoutChildren();
+ if (changed) {
+ resetZoomView();
+ layoutChildren();
+ }
}
/**
@@ -1661,6 +1682,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener {
Animator.AnimatorListener {
private final ValueAnimator mScaleAnimator;
+ private ValueAnimator mZoomAnimator;
private boolean mHasNewScale;
private float mNewScale;
@@ -1748,6 +1770,74 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener {
* (viewWidth + mViewGap);
}
+ /** Zoom all the way in or out on the image at the given pivot point. */
+ private void zoomAt(final ViewItem current, final float focusX, final float focusY) {
+ // End previous zoom animation, if any
+ if (mZoomAnimator != null) {
+ mZoomAnimator.end();
+ }
+ // Calculate end scale
+ final float maxScale = getCurrentDataMaxScale();
+ final float endScale = mScale < maxScale - maxScale * TOLERANCE
+ ? maxScale : FULL_SCREEN_SCALE;
+
+ mZoomAnimator = new ValueAnimator();
+ mZoomAnimator.setFloatValues(mScale, endScale);
+ mZoomAnimator.setDuration(ZOOM_ANIMATION_DURATION_MS);
+ mZoomAnimator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ if (mScale == FULL_SCREEN_SCALE) {
+ enterFullScreen();
+ setSurroundingViewsVisible(false);
+ }
+ cancelLoadingZoomedImage();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Make sure animation ends up having the correct scale even
+ // if it is cancelled before it finishes
+ if (mScale != endScale) {
+ current.postScale(focusX, focusY, endScale/mScale, mDrawArea.width(),
+ mDrawArea.height());
+ mScale = endScale;
+ }
+
+ if (mScale == FULL_SCREEN_SCALE) {
+ setSurroundingViewsVisible(true);
+ mZoomView.setVisibility(GONE);
+ current.resetTransform();
+ } else {
+ mController.loadZoomedImage();
+ }
+ mZoomAnimator = null;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ // Do nothing.
+ }
+ });
+
+ mZoomAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float newScale = (Float) animation.getAnimatedValue();
+ float postScale = newScale / mScale;
+ mScale = newScale;
+ current.postScale(focusX, focusY, postScale, mDrawArea.width(),
+ mDrawArea.height());
+ }
+ });
+ mZoomAnimator.start();
+ }
+
@Override
public void scroll(float deltaX) {
if (mController.isScrolling()) {
@@ -1985,6 +2075,11 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener {
public boolean isZoomStarted() {
return mScale > FULL_SCREEN_SCALE;
}
+
+ public boolean isZoomAnimationRunning() {
+ return mZoomAnimator != null && mZoomAnimator.isRunning();
+ }
+
}
private class MyGestureReceiver implements FilmStripGestureRecognizer.Listener {
@@ -2010,6 +2105,14 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener {
@Override
public boolean onDoubleTap(float x, float y) {
+ if (mScale < FULL_SCREEN_SCALE || inCameraFullscreen()) {
+ return false;
+ }
+ ViewItem current = mViewItem[mCurrentItem];
+ if (current == null) {
+ return false;
+ }
+ mController.zoomAt(current, x, y);
return false;
}
@@ -2018,11 +2121,20 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener {
if (mController.isScrolling()) {
mController.stopScrolling();
}
+ // A down event is usually followed by a gesture, we apply gesture on
+ // the lower-res image during a gesture to ensure a responsive experience.
+ // TODO: Delay this until gesture starts.
+ if (mController.isZoomStarted()) {
+ mZoomView.setVisibility(GONE);
+ }
return true;
}
@Override
public boolean onUp(float x, float y) {
+ if (mController.isZoomAnimationRunning()) {
+ return false;
+ }
if (mController.isZoomStarted()) {
mController.loadZoomedImage();
return true;
@@ -2158,23 +2270,24 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener {
}
mScaleTrend = mScaleTrend * 0.3f + scale * 0.7f;
- if ((mScale < FULL_SCREEN_SCALE) && (mScale * scale < FULL_SCREEN_SCALE)) {
+ float newScale = mScale * scale;
+ if (mScale < FULL_SCREEN_SCALE && newScale < FULL_SCREEN_SCALE) {
// Scaled view is smaller than or equal to screen size both before
// and after scaling
- mScale *= scale;
+ mScale = newScale;
if (mScale <= FILM_STRIP_SCALE) {
mScale = FILM_STRIP_SCALE;
}
layoutChildren();
- } else if ((mScale < FULL_SCREEN_SCALE) && (mScale * scale >= FULL_SCREEN_SCALE)) {
+ } else if (mScale < FULL_SCREEN_SCALE && newScale >= FULL_SCREEN_SCALE) {
// Going from smaller than screen size to bigger than or equal to screen size
mScale = FULL_SCREEN_SCALE;
mController.enterFullScreen();
layoutChildren();
mController.setSurroundingViewsVisible(false);
- } else if ((mScale >= FULL_SCREEN_SCALE) && (mScale * scale < FULL_SCREEN_SCALE)) {
+ } else if (mScale >= FULL_SCREEN_SCALE && newScale < FULL_SCREEN_SCALE) {
// Going from bigger than or equal to screen size to smaller than screen size
- mScale *= scale;
+ mScale = newScale;
mController.leaveFullScreen();
layoutChildren();
mController.setSurroundingViewsVisible(true);
@@ -2186,18 +2299,10 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener {
}
ViewItem curr = mViewItem[mCurrentItem];
// Make sure the image is not overly scaled
- if (mScale * scale > mMaxScale) {
- scale = mMaxScale / mScale;
- }
- mScale *= scale;
- float transX = curr.getTranslationX();
- float transY = curr.getTranslationY();
- // Pivot point is top left of the view, so we need to translate
- // to scale around focus point
- transX -= (focusX - curr.getX()) * (scale - 1f);
- transY -= (focusY - curr.getY()) * (scale - 1f);
- curr.updateTransform(transX, transY, mScale, mScale, mDrawArea.width(),
- mDrawArea.height());
+ newScale = Math.min(newScale, mMaxScale);
+ float postScale = newScale / mScale;
+ curr.postScale(focusX, focusY, postScale, mDrawArea.width(), mDrawArea.height());
+ mScale = newScale;
}
return true;
}
@@ -2205,14 +2310,13 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener {
@Override
public void onScaleEnd() {
- float tolerance = 0.1f;
- if (mScale > FULL_SCREEN_SCALE + tolerance) {
+ if (mScale > FULL_SCREEN_SCALE + TOLERANCE) {
return;
}
mController.setSurroundingViewsVisible(true);
- if (mScale <= FILM_STRIP_SCALE + tolerance) {
+ if (mScale <= FILM_STRIP_SCALE + TOLERANCE) {
mController.goToFilmStrip();
- } else if (mScaleTrend > 1f || mScale > FULL_SCREEN_SCALE - tolerance) {
+ } else if (mScaleTrend > 1f || mScale > FULL_SCREEN_SCALE - TOLERANCE) {
if (mController.isZoomStarted()) {
mScale = FULL_SCREEN_SCALE;
mViewItem[mCurrentItem].updateTransform(0f, 0f, 1f, 1f, mDrawArea.width(),