X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=src%2Fcom%2Fandroid%2Fgallery3d%2Fui%2FPhotoView.java;h=6aace393f715ac13ac518a109a73f405235e1326;hb=6b891c6a3739f8c49d42f9db6fc76cb92c7c5f25;hp=a7ecd062d963f88737ca4ffb8802f3de160b165b;hpb=6dd670997a88fe7502f9ce0e4f9c872b7352027f;p=android-x86%2Fpackages-apps-Gallery2.git diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java index a7ecd062d..6aace393f 100644 --- a/src/com/android/gallery3d/ui/PhotoView.java +++ b/src/com/android/gallery3d/ui/PhotoView.java @@ -22,7 +22,9 @@ import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.os.Message; +import android.util.FloatMath; import android.view.MotionEvent; +import android.view.View.MeasureSpec; import android.view.animation.AccelerateInterpolator; import com.android.gallery3d.R; @@ -30,6 +32,8 @@ import com.android.gallery3d.app.GalleryActivity; import com.android.gallery3d.common.Utils; import com.android.gallery3d.data.MediaItem; import com.android.gallery3d.data.MediaObject; +import com.android.gallery3d.data.Path; +import com.android.gallery3d.util.GalleryUtils; import com.android.gallery3d.util.RangeArray; public class PhotoView extends GLView { @@ -77,11 +81,31 @@ public class PhotoView extends GLView { // Returns true if the item is a Video. public boolean isVideo(int offset); + // Returns true if the item can be deleted. + public boolean isDeletable(int offset); + public static final int LOADING_INIT = 0; public static final int LOADING_COMPLETE = 1; public static final int LOADING_FAIL = 2; public int getLoadingState(int offset); + + // When data change happens, we need to decide which MediaItem to focus + // on. + // + // 1. If focus hint path != null, we try to focus on it if we can find + // it. This is used for undo a deletion, so we can focus on the + // undeleted item. + // + // 2. Otherwise try to focus on the MediaItem that is currently focused, + // if we can find it. + // + // 3. Otherwise try to focus on the previous MediaItem or the next + // MediaItem, depending on the value of focus hint direction. + public static final int FOCUS_HINT_NEXT = 0; + public static final int FOCUS_HINT_PREVIOUS = 1; + public void setFocusHintDirection(int direction); + public void setFocusHintPath(Path path); } public interface Listener { @@ -92,6 +116,9 @@ public class PhotoView extends GLView { public void onActionBarAllowed(boolean allowed); public void onActionBarWanted(); public void onCurrentImageUpdated(); + public void onDeleteImage(Path path, int offset); + public void onUndoDeleteImage(); + public void onCommitDeleteImage(); } // The rules about orientation locking: @@ -112,6 +139,8 @@ public class PhotoView extends GLView { private static final int MSG_CANCEL_EXTRA_SCALING = 2; private static final int MSG_SWITCH_FOCUS = 3; private static final int MSG_CAPTURE_ANIMATION_DONE = 4; + private static final int MSG_DELETE_ANIMATION_DONE = 5; + private static final int MSG_DELETE_DONE = 6; private static final int MOVE_THRESHOLD = 256; private static final float SWIPE_THRESHOLD = 300f; @@ -123,7 +152,10 @@ public class PhotoView extends GLView { // whether we want to apply card deck effect in page mode. private static final boolean CARD_EFFECT = true; - // Used to calculate the scaling factor for the fading animation. + // whether we want to apply offset effect in film mode. + private static final boolean OFFSET_EFFECT = true; + + // Used to calculate the scaling factor for the card deck effect. private ZInterpolator mScaleInterpolator = new ZInterpolator(0.5f); // Used to calculate the alpha factor for the fading animation. @@ -133,10 +165,15 @@ public class PhotoView extends GLView { // We keep this many previous ScreenNails. (also this many next ScreenNails) public static final int SCREEN_NAIL_MAX = 3; + // These are constants for the delete gesture. + private static final int SWIPE_ESCAPE_VELOCITY = 500; // dp/sec + private static final int MAX_DISMISS_VELOCITY = 2000; // dp/sec + // The picture entries, the valid index is from -SCREEN_NAIL_MAX to // SCREEN_NAIL_MAX. private final RangeArray mPictures = new RangeArray(-SCREEN_NAIL_MAX, SCREEN_NAIL_MAX); + private Size[] mSizes = new Size[2 * SCREEN_NAIL_MAX + 1]; private final MyGestureListener mGestureListener; private final GestureRecognizer mGestureRecognizer; @@ -148,6 +185,7 @@ public class PhotoView extends GLView { private StringTexture mNoThumbnailText; private TileImageView mTileView; private EdgeView mEdgeView; + private UndoBarView mUndoBar; private Texture mVideoPlayIcon; private SynchronizedHandler mHandler; @@ -174,6 +212,15 @@ public class PhotoView extends GLView { private int mHolding; private static final int HOLD_TOUCH_DOWN = 1; private static final int HOLD_CAPTURE_ANIMATION = 2; + private static final int HOLD_DELETE = 4; + + // mTouchBoxIndex is the index of the box that is touched by the down + // gesture in film mode. The value Integer.MAX_VALUE means no box was + // touched. + private int mTouchBoxIndex = Integer.MAX_VALUE; + // Whether the box indicated by mTouchBoxIndex is deletable. Only meaningful + // if mTouchBoxIndex is not Integer.MAX_VALUE. + private boolean mTouchBoxDeletable; public PhotoView(GalleryActivity activity) { mTileView = new TileImageView(activity); @@ -181,6 +228,15 @@ public class PhotoView extends GLView { Context context = activity.getAndroidContext(); mEdgeView = new EdgeView(context); addComponent(mEdgeView); + mUndoBar = new UndoBarView(context); + addComponent(mUndoBar); + mUndoBar.setVisibility(GLView.INVISIBLE); + mUndoBar.setOnClickListener(new OnClickListener() { + @Override + public void onClick(GLView v) { + mListener.onUndoDeleteImage(); + } + }); mLoadingText = StringTexture.newInstance( context.getString(R.string.loading), DEFAULT_TEXT_SIZE, Color.WHITE); @@ -198,8 +254,11 @@ public class PhotoView extends GLView { public void invalidate() { PhotoView.this.invalidate(); } - public boolean isHolding() { - return mHolding != 0; + public boolean isHoldingDown() { + return (mHolding & HOLD_TOUCH_DOWN) != 0; + } + public boolean isHoldingDelete() { + return (mHolding & HOLD_DELETE) != 0; } public void onPull(int offset, int direction) { mEdgeView.onPull(offset, direction); @@ -250,6 +309,31 @@ public class PhotoView extends GLView { captureAnimationDone(message.arg1); break; } + case MSG_DELETE_ANIMATION_DONE: { + // message.obj is the Path of the MediaItem which should be + // deleted. message.arg1 is the offset of the image. + mListener.onDeleteImage((Path) message.obj, message.arg1); + // Normally a box which finishes delete animation will hold + // position until the underlying MediaItem is actually + // deleted, and HOLD_DELETE will be cancelled that time. In + // case the MediaItem didn't actually get deleted in 2 + // seconds, we will cancel HOLD_DELETE and make it bounce + // back. + + // We make sure there is at most one MSG_DELETE_DONE + // in the handler. + mHandler.removeMessages(MSG_DELETE_DONE); + Message m = mHandler.obtainMessage(MSG_DELETE_DONE); + mHandler.sendMessageDelayed(m, 2000); + break; + } + case MSG_DELETE_DONE: { + if (!mHandler.hasMessages(MSG_DELETE_ANIMATION_DONE)) { + mHolding &= ~HOLD_DELETE; + snapback(); + } + break; + } default: throw new AssertionError(message.what); } } @@ -263,26 +347,69 @@ public class PhotoView extends GLView { mPrevBound = prevBound; mNextBound = nextBound; + // Update mTouchBoxIndex + if (mTouchBoxIndex != Integer.MAX_VALUE) { + int k = mTouchBoxIndex; + mTouchBoxIndex = Integer.MAX_VALUE; + for (int i = 0; i < 2 * SCREEN_NAIL_MAX + 1; i++) { + if (fromIndex[i] == k) { + mTouchBoxIndex = i - SCREEN_NAIL_MAX; + break; + } + } + } + + // Update the ScreenNails. + for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) { + Picture p = mPictures.get(i); + p.reload(); + mSizes[i + SCREEN_NAIL_MAX] = p.getSize(); + } + + boolean wasDeleting = mPositionController.hasDeletingBox(); + // Move the boxes mPositionController.moveBox(fromIndex, mPrevBound < 0, mNextBound > 0, - mModel.isCamera(0)); + mModel.isCamera(0), mSizes); - // Update the ScreenNails. for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; i++) { - mPictures.get(i).reload(); + setPictureSize(i); + } + + boolean isDeleting = mPositionController.hasDeletingBox(); + + // If the deletion is done, make HOLD_DELETE persist for only the time + // needed for a snapback animation. + if (wasDeleting && !isDeleting) { + mHandler.removeMessages(MSG_DELETE_DONE); + Message m = mHandler.obtainMessage(MSG_DELETE_DONE); + mHandler.sendMessageDelayed( + m, PositionController.SNAPBACK_ANIMATION_TIME); } invalidate(); } + public boolean isDeleting() { + return (mHolding & HOLD_DELETE) != 0 + && mPositionController.hasDeletingBox(); + } + public void notifyImageChange(int index) { if (index == 0) { mListener.onCurrentImageUpdated(); } mPictures.get(index).reload(); + setPictureSize(index); invalidate(); } + private void setPictureSize(int index) { + Picture p = mPictures.get(index); + mPositionController.setImageSize(index, p.getSize(), + index == 0 && p.isCamera() ? mCameraRect : null); + } + @Override protected void onLayout( boolean changeSize, int left, int top, int right, int bottom) { @@ -290,6 +417,8 @@ public class PhotoView extends GLView { int h = bottom - top; mTileView.layout(0, 0, w, h); mEdgeView.layout(0, 0, w, h); + mUndoBar.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + mUndoBar.layout(0, h - mUndoBar.getMeasuredHeight(), w, h); GLRoot root = getGLRoot(); int displayRotation = root.getDisplayRotation(); @@ -376,7 +505,9 @@ public class PhotoView extends GLView { void draw(GLCanvas canvas, Rect r); void setScreenNail(ScreenNail s); boolean isCamera(); // whether the picture is a camera preview + boolean isDeletable(); // whether the picture can be deleted void forceSize(); // called when mCompensation changes + Size getSize(); }; class FullPicture implements Picture { @@ -384,10 +515,10 @@ public class PhotoView extends GLView { private boolean mIsCamera; private boolean mIsPanorama; private boolean mIsVideo; + private boolean mIsDeletable; private int mLoadingState = Model.LOADING_INIT; + private Size mSize = new Size(); private boolean mWasCameraCenter; - private int mWidth, mHeight; - public void FullPicture(TileImageView tileView) { mTileView = tileView; } @@ -400,21 +531,21 @@ public class PhotoView extends GLView { mIsCamera = mModel.isCamera(0); mIsPanorama = mModel.isPanorama(0); mIsVideo = mModel.isVideo(0); + mIsDeletable = mModel.isDeletable(0); mLoadingState = mModel.getLoadingState(0); setScreenNail(mModel.getScreenNail(0)); - setSize(); + updateSize(); } - private void setSize() { - updateSize(); - mPositionController.setImageSize(0, mWidth, mHeight, - mIsCamera ? mCameraRect : null); + @Override + public Size getSize() { + return mSize; } @Override public void forceSize() { updateSize(); - mPositionController.forceImageSize(0, mWidth, mHeight); + mPositionController.forceImageSize(0, mSize); } private void updateSize() { @@ -428,8 +559,8 @@ public class PhotoView extends GLView { int w = mTileView.mImageWidth; int h = mTileView.mImageHeight; - mWidth = getRotated(mRotation, w, h); - mHeight = getRotated(mRotation, h, w); + mSize.width = getRotated(mRotation, w, h); + mSize.height = getRotated(mRotation, h, w); } @Override @@ -473,6 +604,11 @@ public class PhotoView extends GLView { return mIsCamera; } + @Override + public boolean isDeletable() { + return mIsDeletable; + } + private void drawTileView(GLCanvas canvas, Rect r) { float imageScale = mPositionController.getImageScale(); int viewW = getWidth(); @@ -486,6 +622,8 @@ public class PhotoView extends GLView { boolean wantsCardEffect = CARD_EFFECT && !mIsCamera && filmRatio != 1f && !mPictures.get(-1).isCamera() && !mPositionController.inOpeningAnimation(); + boolean wantsOffsetEffect = OFFSET_EFFECT && mIsDeletable + && filmRatio == 1f && r.centerY() != viewH / 2; if (wantsCardEffect) { // Calculate the move-out progress value. int left = r.left; @@ -517,11 +655,15 @@ public class PhotoView extends GLView { } cx = interpolate(filmRatio, cxPage, cx); } + } else if (wantsOffsetEffect) { + float offset = (float) (r.centerY() - viewH / 2) / viewH; + float alpha = getOffsetAlpha(offset); + canvas.multiplyAlpha(alpha); } // Draw the tile view. setTileViewPosition(cx, cy, viewW, viewH, imageScale); - PhotoView.super.render(canvas); + renderChild(canvas, mTileView); // Draw the play video icon and the message. canvas.translate((int) (cx + 0.5f), (int) (cy + 0.5f)); @@ -566,12 +708,12 @@ public class PhotoView extends GLView { private int mIndex; private int mRotation; private ScreenNail mScreenNail; - private Size mSize = new Size(); private boolean mIsCamera; private boolean mIsPanorama; private boolean mIsVideo; + private boolean mIsDeletable; private int mLoadingState = Model.LOADING_INIT; - private int mWidth, mHeight; + private Size mSize = new Size(); public ScreenNailPicture(int index) { mIndex = index; @@ -582,9 +724,15 @@ public class PhotoView extends GLView { mIsCamera = mModel.isCamera(mIndex); mIsPanorama = mModel.isPanorama(mIndex); mIsVideo = mModel.isVideo(mIndex); + mIsDeletable = mModel.isDeletable(mIndex); mLoadingState = mModel.getLoadingState(mIndex); setScreenNail(mModel.getScreenNail(mIndex)); - setSize(); + updateSize(); + } + + @Override + public Size getSize() { + return mSize; } @Override @@ -597,8 +745,9 @@ public class PhotoView extends GLView { } return; } - if (r.left >= getWidth() || r.right <= 0 || - r.top >= getHeight() || r.bottom <= 0) { + int w = getWidth(); + int h = getHeight(); + if (r.left >= w || r.right <= 0 || r.top >= h || r.bottom <= 0) { mScreenNail.noDraw(); return; } @@ -606,7 +755,8 @@ public class PhotoView extends GLView { float filmRatio = mPositionController.getFilmRatio(); boolean wantsCardEffect = CARD_EFFECT && mIndex > 0 && filmRatio != 1f && !mPictures.get(0).isCamera(); - int w = getWidth(); + boolean wantsOffsetEffect = OFFSET_EFFECT && mIsDeletable + && filmRatio == 1f && r.centerY() != h / 2; int cx = wantsCardEffect ? (int) (interpolate(filmRatio, w / 2, r.centerX()) + 0.5f) : r.centerX(); @@ -622,6 +772,10 @@ public class PhotoView extends GLView { scale = interpolate(filmRatio, scale, 1f); canvas.multiplyAlpha(alpha); canvas.scale(scale, scale, 1); + } else if (wantsOffsetEffect) { + float offset = (float) (r.centerY() - h / 2) / h; + float alpha = getOffsetAlpha(offset); + canvas.multiplyAlpha(alpha); } if (mRotation != 0) { canvas.rotate(mRotation, 0, 0, 1); @@ -650,15 +804,10 @@ public class PhotoView extends GLView { mScreenNail = s; } - private void setSize() { - updateSize(); - mPositionController.setImageSize(mIndex, mWidth, mHeight, null); - } - @Override public void forceSize() { updateSize(); - mPositionController.forceImageSize(mIndex, mWidth, mHeight); + mPositionController.forceImageSize(mIndex, mSize); } private void updateSize() { @@ -670,26 +819,30 @@ public class PhotoView extends GLView { mRotation = mModel.getImageRotation(mIndex); } - int w = 0, h = 0; if (mScreenNail != null) { - w = mScreenNail.getWidth(); - h = mScreenNail.getHeight(); - } else if (mModel != null) { + mSize.width = mScreenNail.getWidth(); + mSize.height = mScreenNail.getHeight(); + } else { // If we don't have ScreenNail available, we can still try to // get the size information of it. mModel.getImageSize(mIndex, mSize); - w = mSize.width; - h = mSize.height; } - mWidth = getRotated(mRotation, w, h); - mHeight = getRotated(mRotation, h, w); + int w = mSize.width; + int h = mSize.height; + mSize.width = getRotated(mRotation, w, h); + mSize.height = getRotated(mRotation, h, w); } @Override public boolean isCamera() { return mIsCamera; } + + @Override + public boolean isDeletable() { + return mIsDeletable; + } } // Draw a gray placeholder in the specified rectangle. @@ -736,6 +889,14 @@ public class PhotoView extends GLView { private boolean mDownInScrolling; // If we should ignore all gestures other than onSingleTapUp. private boolean mIgnoreSwipingGesture; + // If a scrolling has happened after a down gesture. + private boolean mScrolledAfterDown; + // If the first scrolling move is in X direction. In the film mode, X + // direction scrolling is normal scrolling. but Y direction scrolling is + // a delete gesture. + private boolean mFirstScrollX; + // The accumulated Y delta that has been sent to mPositionController. + private int mDeltaY; @Override public boolean onSingleTapUp(float x, float y) { @@ -780,23 +941,108 @@ public class PhotoView extends GLView { } @Override - public boolean onScroll(float dx, float dy) { + public boolean onScroll(float dx, float dy, float totalX, float totalY) { if (mIgnoreSwipingGesture) return true; - mPositionController.startScroll(-dx, -dy); + if (!mScrolledAfterDown) { + mScrolledAfterDown = true; + mFirstScrollX = (Math.abs(dx) > Math.abs(dy)); + } + + int dxi = (int) (-dx + 0.5f); + int dyi = (int) (-dy + 0.5f); + if (mFilmMode) { + if (mFirstScrollX) { + mPositionController.scrollFilmX(dxi); + } else { + if (mTouchBoxIndex == Integer.MAX_VALUE) return true; + int newDeltaY = calculateDeltaY(totalY); + int d = newDeltaY - mDeltaY; + if (d != 0) { + mPositionController.scrollFilmY(mTouchBoxIndex, d); + mDeltaY = newDeltaY; + } + } + } else { + mPositionController.scrollPage(dxi, dyi); + } return true; } + private int calculateDeltaY(float delta) { + if (mTouchBoxDeletable) return (int) (delta + 0.5f); + + // don't let items that can't be deleted be dragged more than + // maxScrollDistance, and make it harder and harder to drag. + int size = getHeight(); + float maxScrollDistance = 0.15f * size; + if (Math.abs(delta) >= size) { + delta = delta > 0 ? maxScrollDistance : -maxScrollDistance; + } else { + delta = maxScrollDistance * + FloatMath.sin((delta / size) * (float) (Math.PI / 2)); + } + return (int) (delta + 0.5f); + } + @Override public boolean onFling(float velocityX, float velocityY) { if (mIgnoreSwipingGesture) return true; if (swipeImages(velocityX, velocityY)) { mIgnoreUpEvent = true; - } else if (mPositionController.fling(velocityX, velocityY)) { - mIgnoreUpEvent = true; + } else { + flingImages(velocityX, velocityY); } return true; } + private boolean flingImages(float velocityX, float velocityY) { + int vx = (int) (velocityX + 0.5f); + int vy = (int) (velocityY + 0.5f); + if (!mFilmMode) { + return mPositionController.flingPage(vx, vy); + } + if (Math.abs(velocityX) > Math.abs(velocityY)) { + return mPositionController.flingFilmX(vx); + } + // If we scrolled in Y direction fast enough, treat it as a delete + // gesture. + if (!mFilmMode || mTouchBoxIndex == Integer.MAX_VALUE + || !mTouchBoxDeletable) { + return false; + } + int maxVelocity = (int) GalleryUtils.dpToPixel(MAX_DISMISS_VELOCITY); + int escapeVelocity = + (int) GalleryUtils.dpToPixel(SWIPE_ESCAPE_VELOCITY); + int centerY = mPositionController.getPosition(mTouchBoxIndex) + .centerY(); + boolean fastEnough = (Math.abs(vy) > escapeVelocity) + && (Math.abs(vy) > Math.abs(vx)) + && ((vy > 0) == (centerY > getHeight() / 2)); + if (fastEnough) { + vy = Math.min(vy, maxVelocity); + int duration = mPositionController.flingFilmY(mTouchBoxIndex, vy); + if (duration >= 0) { + mPositionController.setPopFromTop(vy < 0); + deleteAfterAnimation(duration); + // We reset mTouchBoxIndex, so up() won't check if Y + // scrolled far enough to be a delete gesture. + mTouchBoxIndex = Integer.MAX_VALUE; + return true; + } + } + return false; + } + + private void deleteAfterAnimation(int duration) { + MediaItem item = mModel.getMediaItem(mTouchBoxIndex); + if (item == null) return; + mHolding |= HOLD_DELETE; + Message m = mHandler.obtainMessage(MSG_DELETE_ANIMATION_DONE); + m.obj = item.getPath(); + m.arg1 = mTouchBoxIndex; + mHandler.sendMessageDelayed(m, duration); + } + @Override public boolean onScaleBegin(float focusX, float focusY) { if (mIgnoreSwipingGesture) return true; @@ -881,7 +1127,10 @@ public class PhotoView extends GLView { } @Override - public void onDown() { + public void onDown(float x, float y) { + mDeltaY = 0; + mListener.onCommitDeleteImage(); + if (mIgnoreSwipingGesture) return; mHolding |= HOLD_TOUCH_DOWN; @@ -892,6 +1141,21 @@ public class PhotoView extends GLView { } else { mDownInScrolling = false; } + + mScrolledAfterDown = false; + if (mFilmMode) { + int xi = (int) (x + 0.5f); + int yi = (int) (y + 0.5f); + mTouchBoxIndex = mPositionController.hitTest(xi, yi); + if (mTouchBoxIndex < mPrevBound || mTouchBoxIndex > mNextBound) { + mTouchBoxIndex = Integer.MAX_VALUE; + } else { + mTouchBoxDeletable = + mPictures.get(mTouchBoxIndex).isDeletable(); + } + } else { + mTouchBoxIndex = Integer.MAX_VALUE; + } } @Override @@ -901,6 +1165,22 @@ public class PhotoView extends GLView { mHolding &= ~HOLD_TOUCH_DOWN; mEdgeView.onRelease(); + // If we scrolled in Y direction far enough, treat it as a delete + // gesture. + if (mFilmMode && mScrolledAfterDown && !mFirstScrollX + && mTouchBoxIndex != Integer.MAX_VALUE) { + Rect r = mPositionController.getPosition(mTouchBoxIndex); + int h = getHeight(); + if (Math.abs(r.centerY() - h * 0.5f) > 0.4f * h) { + int duration = mPositionController + .flingFilmY(mTouchBoxIndex, 0); + if (duration >= 0) { + mPositionController.setPopFromTop(r.centerY() < h * 0.5f); + deleteAfterAnimation(duration); + } + } + } + if (mIgnoreUpEvent) { mIgnoreUpEvent = false; return; @@ -923,6 +1203,8 @@ public class PhotoView extends GLView { mFilmMode = enabled; mPositionController.setFilmMode(mFilmMode); mModel.setNeedFullImage(!enabled); + mModel.setFocusHintDirection( + mFilmMode ? Model.FOCUS_HINT_PREVIOUS : Model.FOCUS_HINT_NEXT); mListener.onActionBarAllowed(!enabled); // Move into camera in page mode, lock @@ -957,6 +1239,10 @@ public class PhotoView extends GLView { setFilmMode(false); } + public void showUndoButton(boolean show) { + mUndoBar.setVisibility(show ? GLView.VISIBLE : GLView.INVISIBLE); + } + //////////////////////////////////////////////////////////////////////////// // Rendering //////////////////////////////////////////////////////////////////////////// @@ -996,6 +1282,9 @@ public class PhotoView extends GLView { mPictures.get(i).draw(canvas, r); } + renderChild(canvas, mEdgeView); + renderChild(canvas, mUndoBar); + mPositionController.advanceAnimation(); checkFocusSwitching(); } @@ -1106,7 +1395,7 @@ public class PhotoView extends GLView { } private void snapback() { - if (mHolding != 0) return; + if ((mHolding & ~HOLD_DELETE) != 0) return; if (!snapToNeighborImage()) { mPositionController.snapback(); } @@ -1319,6 +1608,14 @@ public class PhotoView extends GLView { return from + (to - from) * ratio * ratio; } + // Returns the alpha factor in film mode if a picture is not in the center. + // The 0.03 lower bound is to make the item always visible a bit. + private float getOffsetAlpha(float offset) { + offset /= 0.5f; + float alpha = (offset > 0) ? (1 - offset) : (1 + offset); + return Utils.clamp(alpha, 0.03f, 1f); + } + //////////////////////////////////////////////////////////////////////////// // Simple public utilities ////////////////////////////////////////////////////////////////////////////