11cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein/* 21cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Copyright (C) 2011 Google Inc. 31cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Licensed to The Android Open Source Project. 41cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * 51cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Licensed under the Apache License, Version 2.0 (the "License"); 61cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * you may not use this file except in compliance with the License. 71cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * You may obtain a copy of the License at 81cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * 91cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * http://www.apache.org/licenses/LICENSE-2.0 101cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * 111cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Unless required by applicable law or agreed to in writing, software 121cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * distributed under the License is distributed on an "AS IS" BASIS, 131cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 141cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * See the License for the specific language governing permissions and 151cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * limitations under the License. 161cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 171cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 181cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinpackage com.android.ex.photo.views; 191cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 201cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.content.Context; 211cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.content.pm.PackageManager; 221cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.content.res.Resources; 231cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.graphics.Bitmap; 241cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.graphics.Canvas; 251cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.graphics.Matrix; 261cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.graphics.Paint; 271cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.graphics.Paint.Style; 281cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.graphics.Rect; 291cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.graphics.RectF; 301cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.graphics.drawable.BitmapDrawable; 314054bd6d6c5d51d9492184b1881253c223ddbf7dPaul Westbrookimport android.support.v4.view.GestureDetectorCompat; 321cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.util.AttributeSet; 334054bd6d6c5d51d9492184b1881253c223ddbf7dPaul Westbrookimport android.view.GestureDetector.OnGestureListener; 344054bd6d6c5d51d9492184b1881253c223ddbf7dPaul Westbrookimport android.view.GestureDetector.OnDoubleTapListener; 351cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.view.MotionEvent; 361cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.view.ScaleGestureDetector; 371cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport android.view.View; 381cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 391cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport com.android.ex.photo.R; 401cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sappersteinimport com.android.ex.photo.fragments.PhotoViewFragment.HorizontallyScrollable; 411cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 421cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein/** 431cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Layout for the photo list view header. 441cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 454054bd6d6c5d51d9492184b1881253c223ddbf7dPaul Westbrookpublic class PhotoView extends View implements OnGestureListener, 464054bd6d6c5d51d9492184b1881253c223ddbf7dPaul Westbrook OnDoubleTapListener, ScaleGestureDetector.OnScaleGestureListener, 471cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein HorizontallyScrollable { 481cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Zoom animation duration; in milliseconds */ 491cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private final static long ZOOM_ANIMATION_DURATION = 300L; 501cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Rotate animation duration; in milliseconds */ 511cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private final static long ROTATE_ANIMATION_DURATION = 500L; 521cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Snap animation duration; in milliseconds */ 531cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static final long SNAP_DURATION = 100L; 541cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Amount of time to wait before starting snap animation; in milliseconds */ 551cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static final long SNAP_DELAY = 250L; 561cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** By how much to scale the image when double click occurs */ 571cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private final static float DOUBLE_TAP_SCALE_FACTOR = 1.5f; 581cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Amount of translation needed before starting a snap animation */ 591cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private final static float SNAP_THRESHOLD = 20.0f; 601cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** The width & height of the bitmap returned by {@link #getCroppedPhoto()} */ 611cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private final static float CROPPED_SIZE = 256.0f; 621cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 631cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** If {@code true}, the static values have been initialized */ 641cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static boolean sInitialized; 651cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 661cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Various dimensions 671cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Width & height of the crop region */ 681cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static int sCropSize; 691cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 701cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Bitmaps 711cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Video icon */ 721cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static Bitmap sVideoImage; 731cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Video icon */ 741cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static Bitmap sVideoNotReadyImage; 751cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 761cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Features 771cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static boolean sHasMultitouchDistinct; 781cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 791cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Paints 801cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Paint to partially dim the photo during crop */ 811cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static Paint sCropDimPaint; 821cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Paint to highlight the cropped portion of the photo */ 831cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static Paint sCropPaint; 841cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 851cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** The photo to display */ 861cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private BitmapDrawable mDrawable; 871cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** The matrix used for drawing; this may be {@code null} */ 881cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private Matrix mDrawMatrix; 891cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** A matrix to apply the scaling of the photo */ 901cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private Matrix mMatrix = new Matrix(); 911cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** The original matrix for this image; used to reset any transformations applied by the user */ 921cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private Matrix mOriginalMatrix = new Matrix(); 931cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 941cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** The fixed height of this view. If {@code -1}, calculate the height */ 951cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private int mFixedHeight = -1; 961cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** When {@code true}, the view has been laid out */ 971cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mHaveLayout; 981cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Whether or not the photo is full-screen */ 991cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mFullScreen; 1001cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Whether or not this is a still image of a video */ 1011cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private byte[] mVideoBlob; 1021cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Whether or not this is a still image of a video */ 1031cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mVideoReady; 1041cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 1051cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Whether or not crop is allowed */ 1061cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mAllowCrop; 1071cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** The crop region */ 1081cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private Rect mCropRect = new Rect(); 1091cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Actual crop size; may differ from {@link #sCropSize} if the screen is smaller */ 1101cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private int mCropSize; 1111cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 1121cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Gesture detector */ 1134054bd6d6c5d51d9492184b1881253c223ddbf7dPaul Westbrook private GestureDetectorCompat mGestureDetector; 1141cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Gesture detector that detects pinch gestures */ 1151cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private ScaleGestureDetector mScaleGetureDetector; 1161cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** An external click listener */ 1171cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private OnClickListener mExternalClickListener; 1181cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** When {@code true}, allows gestures to scale / pan the image */ 1191cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mTransformsEnabled; 1201cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 1211cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // To support zooming 1221cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** When {@code true}, a double tap scales the image by {@link #DOUBLE_TAP_SCALE_FACTOR} */ 1231cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mDoubleTapToZoomEnabled = true; 1241cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** When {@code true}, prevents scale end gesture from falsely triggering a double click. */ 1251cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mDoubleTapDebounce; 1261cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** When {@code false}, event is a scale gesture. Otherwise, event is a double touch. */ 1271cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mIsDoubleTouch; 1281cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Runnable that scales the image */ 1291cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private ScaleRunnable mScaleRunnable; 1301cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Minimum scale the image can have. */ 1311cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mMinScale; 1321cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Maximum scale to limit scaling to, 0 means no limit. */ 1331cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mMaxScale; 1341cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 1351cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // To support translation [i.e. panning] 1361cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Runnable that can move the image */ 1371cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private TranslateRunnable mTranslateRunnable; 1381cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private SnapRunnable mSnapRunnable; 1391cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 1401cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // To support rotation 1411cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** The rotate runnable used to animate rotations of the image */ 1421cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private RotateRunnable mRotateRunnable; 1431cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** The current rotation amount, in degrees */ 1441cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mRotation; 1451cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 1461cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Convenience fields 1471cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // These are declared here not because they are important properties of the view. Rather, we 1481cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // declare them here to avoid object allocation during critical graphics operations; such as 1491cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // layout or drawing. 1501cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Source (i.e. the photo size) bounds */ 1511cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private RectF mTempSrc = new RectF(); 1521cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Destination (i.e. the display) bounds. The image is scaled to this size. */ 1531cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private RectF mTempDst = new RectF(); 1541cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Rectangle to handle translations */ 1551cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private RectF mTranslateRect = new RectF(); 1561cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** Array to store a copy of the matrix values */ 1571cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float[] mValues = new float[9]; 1581cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 1591cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public PhotoView(Context context) { 1601cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein super(context); 1611cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein initialize(); 1621cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 1631cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 1641cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public PhotoView(Context context, AttributeSet attrs) { 1651cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein super(context, attrs); 1661cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein initialize(); 1671cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 1681cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 1691cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public PhotoView(Context context, AttributeSet attrs, int defStyle) { 1701cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein super(context, attrs, defStyle); 1711cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein initialize(); 1721cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 1731cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 1741cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 1751cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean onTouchEvent(MotionEvent event) { 1761cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mScaleGetureDetector == null || mGestureDetector == null) { 1771cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // We're being destroyed; ignore any touch events 1781cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 1791cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 1801cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 1811cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mScaleGetureDetector.onTouchEvent(event); 1821cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mGestureDetector.onTouchEvent(event); 1831cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int action = event.getAction(); 1841cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 1851cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein switch (action) { 1861cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein case MotionEvent.ACTION_UP: 1871cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein case MotionEvent.ACTION_CANCEL: 1881cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (!mTranslateRunnable.mRunning) { 1891cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein snap(); 1901cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 1911cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein break; 1921cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 1931cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 1941cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 1951cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 1961cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 1971cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 1981cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean onDoubleTap(MotionEvent e) { 1991cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mDoubleTapToZoomEnabled && mTransformsEnabled) { 2001cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (!mDoubleTapDebounce) { 2011cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float currentScale = getScale(); 2021cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float targetScale = currentScale * DOUBLE_TAP_SCALE_FACTOR; 2031cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 2041cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Ensure the target scale is within our bounds 2051cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein targetScale = Math.max(mMinScale, targetScale); 2061cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein targetScale = Math.min(mMaxScale, targetScale); 2071cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 2081cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mScaleRunnable.start(currentScale, targetScale, e.getX(), e.getY()); 2091cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2101cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mDoubleTapDebounce = false; 2111cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2121cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 2131cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2141cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 2151cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 2161cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean onDoubleTapEvent(MotionEvent e) { 2171cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 2181cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2191cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 2201cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 2211cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean onSingleTapConfirmed(MotionEvent e) { 2221cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mExternalClickListener != null && !mIsDoubleTouch) { 2231cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mExternalClickListener.onClick(this); 2241cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2251cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mIsDoubleTouch = false; 2261cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 2271cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2281cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 2291cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 2301cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean onSingleTapUp(MotionEvent e) { 2311cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return false; 2321cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2331cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 2341cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 2351cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void onLongPress(MotionEvent e) { 2361cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2371cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 2381cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 2391cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void onShowPress(MotionEvent e) { 2401cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2411cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 2421cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 2431cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 2441cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mTransformsEnabled) { 245bd64181d797dabd8b346b97ebb3e4083e53b864dPaul Westbrook translate(-distanceX, -distanceY); 2461cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2471cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 2481cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2491cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 2501cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 2511cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean onDown(MotionEvent e) { 2521cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mTransformsEnabled) { 2531cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTranslateRunnable.stop(); 2541cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mSnapRunnable.stop(); 2551cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2561cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 2571cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2581cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 2591cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 2601cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 2611cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mTransformsEnabled) { 2624054bd6d6c5d51d9492184b1881253c223ddbf7dPaul Westbrook mTranslateRunnable.start(velocityX, velocityY); 2631cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2641cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 2651cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2661cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 2671cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 2681cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean onScale(ScaleGestureDetector detector) { 2691cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mTransformsEnabled) { 2701cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mIsDoubleTouch = false; 2711cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float currentScale = getScale(); 2721cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float newScale = currentScale * detector.getScaleFactor(); 2731cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein scale(newScale, detector.getFocusX(), detector.getFocusY()); 2741cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2751cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 2761cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2771cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 2781cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 2791cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean onScaleBegin(ScaleGestureDetector detector) { 2801cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mTransformsEnabled) { 2811cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mScaleRunnable.stop(); 2821cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mIsDoubleTouch = true; 2831cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2841cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 2851cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2861cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 2871cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 2881cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void onScaleEnd(ScaleGestureDetector detector) { 2891cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mTransformsEnabled && mIsDoubleTouch) { 2901cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mDoubleTapDebounce = true; 2911cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein resetTransformations(); 2921cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2931cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2941cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 2951cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 2961cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void setOnClickListener(OnClickListener listener) { 2971cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mExternalClickListener = listener; 2981cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 2991cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 3001cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 3011cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean interceptMoveLeft(float origX, float origY) { 3021cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (!mTransformsEnabled) { 3031cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Allow intercept if we're not in transform mode 3041cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return false; 3051cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else if (mTranslateRunnable.mRunning) { 3061cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Don't allow touch intercept until we've stopped flinging 3071cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 3081cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 3091cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.getValues(mValues); 3101cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTranslateRect.set(mTempSrc); 3111cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.mapRect(mTranslateRect); 3121cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 3131cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final float viewWidth = getWidth(); 3141cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final float transX = mValues[Matrix.MTRANS_X]; 3151cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final float drawWidth = mTranslateRect.right - mTranslateRect.left; 3161cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 3171cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (!mTransformsEnabled || drawWidth <= viewWidth) { 3181cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Allow intercept if not in transform mode or the image is smaller than the view 3191cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return false; 3201cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else if (transX == 0) { 3211cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // We're at the left-side of the image; allow intercepting movements to the right 3221cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return false; 3231cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else if (viewWidth >= drawWidth + transX) { 3241cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // We're at the right-side of the image; allow intercepting movements to the left 3251cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 3261cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 3271cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // We're in the middle of the image; don't allow touch intercept 3281cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 3291cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 3301cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 3311cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 3321cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 3331cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 3341cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean interceptMoveRight(float origX, float origY) { 3351cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (!mTransformsEnabled) { 3361cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Allow intercept if we're not in transform mode 3371cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return false; 3381cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else if (mTranslateRunnable.mRunning) { 3391cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Don't allow touch intercept until we've stopped flinging 3401cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 3411cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 3421cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.getValues(mValues); 3431cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTranslateRect.set(mTempSrc); 3441cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.mapRect(mTranslateRect); 3451cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 3461cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final float viewWidth = getWidth(); 3471cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final float transX = mValues[Matrix.MTRANS_X]; 3481cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final float drawWidth = mTranslateRect.right - mTranslateRect.left; 3491cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 3501cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (!mTransformsEnabled || drawWidth <= viewWidth) { 3511cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Allow intercept if not in transform mode or the image is smaller than the view 3521cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return false; 3531cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else if (transX == 0) { 3541cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // We're at the left-side of the image; allow intercepting movements to the right 3551cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 3561cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else if (viewWidth >= drawWidth + transX) { 3571cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // We're at the right-side of the image; allow intercepting movements to the left 3581cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return false; 3591cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 3601cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // We're in the middle of the image; don't allow touch intercept 3611cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 3621cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 3631cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 3641cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 3651cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 3661cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 3671cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Free all resources held by this view. 3681cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * The view is on its way to be collected and will not be reused. 3691cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 3701cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void clear() { 3711cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mGestureDetector = null; 3721cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mScaleGetureDetector = null; 3731cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mDrawable = null; 3741cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mScaleRunnable.stop(); 3751cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mScaleRunnable = null; 3761cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTranslateRunnable.stop(); 3771cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTranslateRunnable = null; 3781cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mSnapRunnable.stop(); 3791cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mSnapRunnable = null; 3801cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mRotateRunnable.stop(); 3811cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mRotateRunnable = null; 3821cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein setOnClickListener(null); 3831cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mExternalClickListener = null; 3841cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 3851cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 3861cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 3871cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Binds a bitmap to the view. 3881cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * 3891cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * @param photoBitmap the bitmap to bind. 3901cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 3911cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void bindPhoto(Bitmap photoBitmap) { 3921cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein boolean changed = false; 3931cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mDrawable != null) { 3941cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final Bitmap drawableBitmap = mDrawable.getBitmap(); 3951cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (photoBitmap == drawableBitmap) { 3961cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // setting the same bitmap; do nothing 3971cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return; 3981cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 3991cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 4001cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein changed = photoBitmap != null && 4011cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein (mDrawable.getIntrinsicWidth() != photoBitmap.getWidth() || 4021cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mDrawable.getIntrinsicHeight() != photoBitmap.getHeight()); 4031cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 4041cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Reset mMinScale to ensure the bounds / matrix are recalculated 4051cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMinScale = 0f; 4061cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mDrawable = null; 4071cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4081cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 4091cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mDrawable == null && photoBitmap != null) { 4101cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mDrawable = new BitmapDrawable(getResources(), photoBitmap); 4111cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4121cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 4131cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein configureBounds(changed); 4141cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein invalidate(); 4151cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4161cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 4171cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 4181cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Returns the bound photo data if set. Otherwise, {@code null}. 4191cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 4201cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public Bitmap getPhoto() { 4211cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mDrawable != null) { 4221cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return mDrawable.getBitmap(); 4231cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4241cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return null; 4251cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4261cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 4271cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 4281cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Gets video data associated with this item. Returns {@code null} if this is not a video. 4291cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 4301cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public byte[] getVideoData() { 4311cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return mVideoBlob; 4321cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4331cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 4341cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 4351cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Returns {@code true} if the photo represents a video. Otherwise, {@code false}. 4361cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 4371cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean isVideo() { 4381cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return mVideoBlob != null; 4391cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4401cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 4411cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 4421cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Returns {@code true} if the video is ready to play. Otherwise, {@code false}. 4431cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 4441cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean isVideoReady() { 4451cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return mVideoBlob != null && mVideoReady; 4461cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4471cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 4481cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 4491cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Returns {@code true} if a photo has been bound. Otherwise, {@code false}. 4501cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 4511cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean isPhotoBound() { 4521cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return mDrawable != null; 4531cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4541cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 4551cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 4561cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Hides the photo info portion of the header. As a side effect, this automatically enables 4571cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * or disables image transformations [eg zoom, pan, etc...] depending upon the value of 4581cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * fullScreen. If this is not desirable, enable / disable image transformations manually. 4591cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 4601cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void setFullScreen(boolean fullScreen, boolean animate) { 4611cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (fullScreen != mFullScreen) { 4621cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mFullScreen = fullScreen; 4631cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein requestLayout(); 4641cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein invalidate(); 4651cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4661cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4671cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 4681cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 4691cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Enable or disable cropping of the displayed image. Cropping can only be enabled 4701cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * <em>before</em> the view has been laid out. Additionally, once cropping has been 4711cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * enabled, it cannot be disabled. 4721cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 4731cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void enableAllowCrop(boolean allowCrop) { 4741cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (allowCrop && mHaveLayout) { 4751cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein throw new IllegalArgumentException("Cannot set crop after view has been laid out"); 4761cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4771cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (!allowCrop && mAllowCrop) { 4781cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein throw new IllegalArgumentException("Cannot unset crop mode"); 4791cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4801cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mAllowCrop = allowCrop; 4811cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4821cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 4831cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 4841cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Gets a bitmap of the cropped region. If cropping is not enabled, returns {@code null}. 4851cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 4861cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public Bitmap getCroppedPhoto() { 4871cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (!mAllowCrop) { 4881cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return null; 4891cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 4901cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 4911cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final Bitmap croppedBitmap = Bitmap.createBitmap( 4921cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein (int) CROPPED_SIZE, (int) CROPPED_SIZE, Bitmap.Config.ARGB_8888); 4931cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final Canvas croppedCanvas = new Canvas(croppedBitmap); 4941cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 4951cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // scale for the final dimensions 4961cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int cropWidth = mCropRect.right - mCropRect.left; 4971cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final float scaleWidth = CROPPED_SIZE / cropWidth; 4981cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final float scaleHeight = CROPPED_SIZE / cropWidth; 4991cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5001cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // translate to the origin & scale 5011cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final Matrix matrix = new Matrix(mDrawMatrix); 5021cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein matrix.postTranslate(-mCropRect.left, -mCropRect.top); 5031cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein matrix.postScale(scaleWidth, scaleHeight); 5041cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5051cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // draw the photo 5061cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mDrawable != null) { 5071cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein croppedCanvas.concat(matrix); 5081cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mDrawable.draw(croppedCanvas); 5091cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 5101cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return croppedBitmap; 5111cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 5121cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5131cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 5141cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Resets the image transformation to its original value. 5151cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 5161cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void resetTransformations() { 5171cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // snap transformations; we don't animate 5181cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.set(mOriginalMatrix); 5191cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 520d19540fb7139fc0baba3b55260a6aabdb029c831Andrew Sapperstein // Invalidate the view because if you move off this PhotoView 5211cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // to another one and come back, you want it to draw from scratch 5221cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // in case you were zoomed in or translated (since those settings 5231cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // are not preserved and probably shouldn't be). 5241cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein invalidate(); 5251cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 5261cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5271cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 5281cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Rotates the image 90 degrees, clockwise. 5291cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 5301cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void rotateClockwise() { 5311cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein rotate(90, true); 5321cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 5331cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5341cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 5351cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Rotates the image 90 degrees, counter clockwise. 5361cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 5371cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void rotateCounterClockwise() { 5381cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein rotate(-90, true); 5391cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 5401cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5411cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 5421cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein protected void onDraw(Canvas canvas) { 5431cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein super.onDraw(canvas); 5441cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5451cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // draw the photo 5461cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mDrawable != null) { 5471cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein int saveCount = canvas.getSaveCount(); 5481cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein canvas.save(); 5491cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5501cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mDrawMatrix != null) { 5511cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein canvas.concat(mDrawMatrix); 5521cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 5531cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mDrawable.draw(canvas); 5541cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5551cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein canvas.restoreToCount(saveCount); 5561cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5571cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mVideoBlob != null) { 5581cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final Bitmap videoImage = (mVideoReady ? sVideoImage : sVideoNotReadyImage); 5591cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int drawLeft = (getWidth() - videoImage.getWidth()) / 2; 5601cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int drawTop = (getHeight() - videoImage.getHeight()) / 2; 5611cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein canvas.drawBitmap(videoImage, drawLeft, drawTop, null); 5621cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 5631cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5641cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Extract the drawable's bounds (in our own copy, to not alter the image) 5651cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTranslateRect.set(mDrawable.getBounds()); 5661cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mDrawMatrix != null) { 5671cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mDrawMatrix.mapRect(mTranslateRect); 5681cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 5691cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5701cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mAllowCrop) { 5711cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein int previousSaveCount = canvas.getSaveCount(); 5721cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein canvas.drawRect(0, 0, getWidth(), getHeight(), sCropDimPaint); 5731cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein canvas.save(); 5741cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein canvas.clipRect(mCropRect); 5751cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5761cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mDrawMatrix != null) { 5771cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein canvas.concat(mDrawMatrix); 5781cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 5791cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5801cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mDrawable.draw(canvas); 5811cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein canvas.restoreToCount(previousSaveCount); 5821cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein canvas.drawRect(mCropRect, sCropPaint); 5831cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 5841cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 5851cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 5861cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5871cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 5881cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 5891cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein super.onLayout(changed, left, top, right, bottom); 5901cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHaveLayout = true; 5911cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int layoutWidth = getWidth(); 5921cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int layoutHeight = getHeight(); 5931cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 5941cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mAllowCrop) { 5951cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mCropSize = Math.min(sCropSize, Math.min(layoutWidth, layoutHeight)); 5961cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int cropLeft = (layoutWidth - mCropSize) / 2; 5971cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int cropTop = (layoutHeight - mCropSize) / 2; 5981cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int cropRight = cropLeft + mCropSize; 5991cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int cropBottom = cropTop + mCropSize; 6001cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 6011cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Create a crop region overlay. We need a separate canvas to be able to "punch 6021cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // a hole" through to the underlying image. 6031cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mCropRect.set(cropLeft, cropTop, cropRight, cropBottom); 6041cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 6051cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein configureBounds(changed); 6061cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 6071cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 6081cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 6091cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 6101cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mFixedHeight != -1) { 6111cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(mFixedHeight, 6121cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein MeasureSpec.AT_MOST)); 6131cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein setMeasuredDimension(getMeasuredWidth(), mFixedHeight); 6141cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 6151cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein super.onMeasure(widthMeasureSpec, heightMeasureSpec); 6161cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 6171cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 6181cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 6191cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 6201cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Forces a fixed height for this view. 6211cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * 6221cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * @param fixedHeight The height. If {@code -1}, use the measured height. 6231cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 6241cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void setFixedHeight(int fixedHeight) { 6251cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final boolean adjustBounds = (fixedHeight != mFixedHeight); 6261cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mFixedHeight = fixedHeight; 6271cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein setMeasuredDimension(getMeasuredWidth(), mFixedHeight); 6281cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (adjustBounds) { 6291cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein configureBounds(true); 6301cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein requestLayout(); 6311cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 6321cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 6331cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 6341cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 6351cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Enable or disable image transformations. When transformations are enabled, this view 6361cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * consumes all touch events. 6371cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 6381cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void enableImageTransforms(boolean enable) { 6391cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTransformsEnabled = enable; 6401cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (!mTransformsEnabled) { 6411cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein resetTransformations(); 6421cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 6431cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 6441cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 6451cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 6461cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Configures the bounds of the photo. The photo will always be scaled to fit center. 6471cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 6481cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private void configureBounds(boolean changed) { 6491cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mDrawable == null || !mHaveLayout) { 6501cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return; 6511cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 6521cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int dwidth = mDrawable.getIntrinsicWidth(); 6531cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int dheight = mDrawable.getIntrinsicHeight(); 6541cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 6551cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int vwidth = getWidth(); 6561cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int vheight = getHeight(); 6571cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 6581cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final boolean fits = (dwidth < 0 || vwidth == dwidth) && 6591cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein (dheight < 0 || vheight == dheight); 6601cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 6611cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // We need to do the scaling ourself, so have the drawable use its native size. 6621cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mDrawable.setBounds(0, 0, dwidth, dheight); 6631cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 6641cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Create a matrix with the proper transforms 6651cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (changed || (mMinScale == 0 && mDrawable != null && mHaveLayout)) { 6661cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein generateMatrix(); 6671cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein generateScale(); 6681cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 6691cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 6701cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (fits || mMatrix.isIdentity()) { 6711cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // The bitmap fits exactly, no transform needed. 6721cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mDrawMatrix = null; 6731cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 6741cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mDrawMatrix = mMatrix; 6751cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 6761cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 6771cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 6781cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 6791cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Generates the initial transformation matrix for drawing. Additionally, it sets the 6801cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * minimum and maximum scale values. 6811cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 6821cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private void generateMatrix() { 6831cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int dwidth = mDrawable.getIntrinsicWidth(); 6841cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int dheight = mDrawable.getIntrinsicHeight(); 6851cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 6861cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int vwidth = mAllowCrop ? sCropSize : getWidth(); 6871cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int vheight = mAllowCrop ? sCropSize : getHeight(); 6881cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 6891cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final boolean fits = (dwidth < 0 || vwidth == dwidth) && 6901cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein (dheight < 0 || vheight == dheight); 6911cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 6921cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (fits && !mAllowCrop) { 6931cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.reset(); 6941cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 6951cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Generate the required transforms for the photo 6961cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTempSrc.set(0, 0, dwidth, dheight); 6971cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mAllowCrop) { 6981cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTempDst.set(mCropRect); 6991cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 7001cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTempDst.set(0, 0, vwidth, vheight); 7011cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 7024f38cc1673163848c7b843cb9d23b21945b0bf82Mark Wei 7034f38cc1673163848c7b843cb9d23b21945b0bf82Mark Wei if (dwidth < vwidth && dheight < vheight && !mAllowCrop) { 7044f38cc1673163848c7b843cb9d23b21945b0bf82Mark Wei mMatrix.setTranslate(vwidth / 2 - dwidth / 2, vheight / 2 - dheight / 2); 7054f38cc1673163848c7b843cb9d23b21945b0bf82Mark Wei } else { 7064f38cc1673163848c7b843cb9d23b21945b0bf82Mark Wei mMatrix.setRectToRect(mTempSrc, mTempDst, Matrix.ScaleToFit.CENTER); 7074f38cc1673163848c7b843cb9d23b21945b0bf82Mark Wei } 7081cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 7091cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mOriginalMatrix.set(mMatrix); 7101cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 7111cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7121cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private void generateScale() { 7131cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int dwidth = mDrawable.getIntrinsicWidth(); 7141cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int dheight = mDrawable.getIntrinsicHeight(); 7151cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7161cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int vwidth = mAllowCrop ? getCropSize() : getWidth(); 7171cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final int vheight = mAllowCrop ? getCropSize() : getHeight(); 7181cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7191cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (dwidth < vwidth && dheight < vheight && !mAllowCrop) { 7201cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMinScale = 1.0f; 7211cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 7221cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMinScale = getScale(); 7231cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 7241cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMaxScale = Math.max(mMinScale * 8, 8); 7251cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 7261cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7271cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 7281cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * @return the size of the crop regions 7291cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 7301cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private int getCropSize() { 7311cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return mCropSize > 0 ? mCropSize : sCropSize; 7321cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 7331cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7341cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 7351cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Returns the currently applied scale factor for the image. 7361cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * <p> 7371cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * NOTE: This method overwrites any values stored in {@link #mValues}. 7381cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 7391cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float getScale() { 7401cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.getValues(mValues); 7411cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return mValues[Matrix.MSCALE_X]; 7421cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 7431cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7441cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 7451cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Scales the image while keeping the aspect ratio. 7461cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * 7471cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * The given scale is capped so that the resulting scale of the image always remains 7481cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * between {@link #mMinScale} and {@link #mMaxScale}. 7491cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * 7501cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * The scaled image is never allowed to be outside of the viewable area. If the image 7511cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * is smaller than the viewable area, it will be centered. 7521cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * 7531cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * @param newScale the new scale 7541cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * @param centerX the center horizontal point around which to scale 7551cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * @param centerY the center vertical point around which to scale 7561cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 7571cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private void scale(float newScale, float centerX, float centerY) { 7581cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // rotate back to the original orientation 7591cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.postRotate(-mRotation, getWidth() / 2, getHeight() / 2); 7601cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7611cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // ensure that mMixScale <= newScale <= mMaxScale 7621cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein newScale = Math.max(newScale, mMinScale); 7631cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein newScale = Math.min(newScale, mMaxScale); 7641cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7651cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float currentScale = getScale(); 7661cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float factor = newScale / currentScale; 7671cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7681cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // apply the scale factor 7691cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.postScale(factor, factor, centerX, centerY); 7701cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7711cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // ensure the image is within the view bounds 7721cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein snap(); 7731cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7741cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // re-apply any rotation 7751cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.postRotate(mRotation, getWidth() / 2, getHeight() / 2); 7761cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7771cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein invalidate(); 7781cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 7791cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7801cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 7811cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Translates the image. 7821cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * 7831cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * This method will not allow the image to be translated outside of the visible area. 7841cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * 7851cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * @param tx how many pixels to translate horizontally 7861cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * @param ty how many pixels to translate vertically 7871cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * @return {@code true} if the translation was applied as specified. Otherwise, {@code false} 7881cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * if the translation was modified. 7891cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 7901cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean translate(float tx, float ty) { 7911cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTranslateRect.set(mTempSrc); 7921cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.mapRect(mTranslateRect); 7931cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7941cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final float maxLeft = mAllowCrop ? mCropRect.left : 0.0f; 7951cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final float maxRight = mAllowCrop ? mCropRect.right : getWidth(); 7961cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float l = mTranslateRect.left; 7971cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float r = mTranslateRect.right; 7981cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 7991cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final float translateX; 8001cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mAllowCrop) { 8011cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // If we're cropping, allow the image to scroll off the edge of the screen 8021cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein translateX = Math.max(maxLeft - mTranslateRect.right, 8031cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein Math.min(maxRight - mTranslateRect.left, tx)); 8041cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 8051cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Otherwise, ensure the image never leaves the screen 8061cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (r - l < maxRight - maxLeft) { 8071cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein translateX = maxLeft + ((maxRight - maxLeft) - (r + l)) / 2; 8081cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 8091cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein translateX = Math.max(maxRight - r, Math.min(maxLeft - l, tx)); 8101cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 8111cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 8121cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 8131cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float maxTop = mAllowCrop ? mCropRect.top: 0.0f; 8141cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float maxBottom = mAllowCrop ? mCropRect.bottom : getHeight(); 8151cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float t = mTranslateRect.top; 8161cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float b = mTranslateRect.bottom; 8171cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 8181cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final float translateY; 8191cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 8201cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mAllowCrop) { 8211cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // If we're cropping, allow the image to scroll off the edge of the screen 8221cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein translateY = Math.max(maxTop - mTranslateRect.bottom, 8231cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein Math.min(maxBottom - mTranslateRect.top, ty)); 8241cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 8251cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Otherwise, ensure the image never leaves the screen 8261cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (b - t < maxBottom - maxTop) { 8271cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein translateY = maxTop + ((maxBottom - maxTop) - (b + t)) / 2; 8281cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 8291cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein translateY = Math.max(maxBottom - b, Math.min(maxTop - t, ty)); 8301cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 8311cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 8321cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 8331cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Do the translation 8341cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.postTranslate(translateX, translateY); 8351cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein invalidate(); 8361cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 8371cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return (translateX == tx) && (translateY == ty); 8381cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 8391cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 8401cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 8411cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Snaps the image so it touches all edges of the view. 8421cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 8431cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private void snap() { 8441cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTranslateRect.set(mTempSrc); 8451cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.mapRect(mTranslateRect); 8461cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 8471cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Determine how much to snap in the horizontal direction [if any] 8481cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float maxLeft = mAllowCrop ? mCropRect.left : 0.0f; 8491cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float maxRight = mAllowCrop ? mCropRect.right : getWidth(); 8501cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float l = mTranslateRect.left; 8511cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float r = mTranslateRect.right; 8521cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 8531cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final float translateX; 8541cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (r - l < maxRight - maxLeft) { 8551cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Image is narrower than view; translate to the center of the view 8561cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein translateX = maxLeft + ((maxRight - maxLeft) - (r + l)) / 2; 8571cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else if (l > maxLeft) { 8581cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Image is off right-edge of screen; bring it into view 8591cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein translateX = maxLeft - l; 8601cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else if (r < maxRight) { 8611cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Image is off left-edge of screen; bring it into view 8621cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein translateX = maxRight - r; 8631cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 8641cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein translateX = 0.0f; 8651cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 8661cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 8671cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Determine how much to snap in the vertical direction [if any] 8681cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float maxTop = mAllowCrop ? mCropRect.top : 0.0f; 8691cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float maxBottom = mAllowCrop ? mCropRect.bottom : getHeight(); 8701cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float t = mTranslateRect.top; 8711cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float b = mTranslateRect.bottom; 8721cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 8731cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final float translateY; 8741cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (b - t < maxBottom - maxTop) { 8751cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Image is shorter than view; translate to the bottom edge of the view 8761cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein translateY = maxTop + ((maxBottom - maxTop) - (b + t)) / 2; 8771cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else if (t > maxTop) { 8781cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Image is off bottom-edge of screen; bring it into view 8791cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein translateY = maxTop - t; 8801cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else if (b < maxBottom) { 8811cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Image is off top-edge of screen; bring it into view 8821cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein translateY = maxBottom - b; 8831cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 8841cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein translateY = 0.0f; 8851cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 8861cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 8871cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (Math.abs(translateX) > SNAP_THRESHOLD || Math.abs(translateY) > SNAP_THRESHOLD) { 8881cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mSnapRunnable.start(translateX, translateY); 8891cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 8901cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.postTranslate(translateX, translateY); 8911cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein invalidate(); 8921cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 8931cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 8941cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 8951cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 8961cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Rotates the image, either instantly or gradually 8971cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * 8981cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * @param degrees how many degrees to rotate the image, positive rotates clockwise 8991cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * @param animate if {@code true}, animate during the rotation. Otherwise, snap rotate. 9001cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 9011cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private void rotate(float degrees, boolean animate) { 9021cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (animate) { 9031cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mRotateRunnable.start(degrees); 9041cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 9051cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mRotation += degrees; 9061cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mMatrix.postRotate(degrees, getWidth() / 2, getHeight() / 2); 9071cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein invalidate(); 9081cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 9091cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 9101cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9111cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 9121cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Initializes the header and any static values 9131cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 9141cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private void initialize() { 9151cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein Context context = getContext(); 9161cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9171cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (!sInitialized) { 9181cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein sInitialized = true; 9191cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9201cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein Resources resources = context.getApplicationContext().getResources(); 9211cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9221cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein sCropSize = resources.getDimensionPixelSize(R.dimen.photo_crop_width); 9231cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9241cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein sCropDimPaint = new Paint(); 9251cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein sCropDimPaint.setAntiAlias(true); 9261cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein sCropDimPaint.setColor(resources.getColor(R.color.photo_crop_dim_color)); 9271cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein sCropDimPaint.setStyle(Style.FILL); 9281cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9291cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein sCropPaint = new Paint(); 9301cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein sCropPaint.setAntiAlias(true); 9311cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein sCropPaint.setColor(resources.getColor(R.color.photo_crop_highlight_color)); 9321cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein sCropPaint.setStyle(Style.STROKE); 9331cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein sCropPaint.setStrokeWidth(resources.getDimension(R.dimen.photo_crop_stroke_width)); 9341cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 9351cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9364054bd6d6c5d51d9492184b1881253c223ddbf7dPaul Westbrook mGestureDetector = new GestureDetectorCompat(context, this, null); 9371cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mScaleGetureDetector = new ScaleGestureDetector(context, this); 9381cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mScaleRunnable = new ScaleRunnable(this); 9391cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTranslateRunnable = new TranslateRunnable(this); 9401cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mSnapRunnable = new SnapRunnable(this); 9411cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mRotateRunnable = new RotateRunnable(this); 9421cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 9431cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9441cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 9451cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Runnable that animates an image scale operation. 9461cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 9471cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static class ScaleRunnable implements Runnable { 9481cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9491cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private final PhotoView mHeader; 9501cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9511cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mCenterX; 9521cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mCenterY; 9531cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9541cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mZoomingIn; 9551cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9561cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mTargetScale; 9571cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mStartScale; 9581cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mVelocity; 9591cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private long mStartTime; 9601cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9611cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mRunning; 9621cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mStop; 9631cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9641cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public ScaleRunnable(PhotoView header) { 9651cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader = header; 9661cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 9671cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9681cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 9691cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Starts the animation. There is no target scale bounds check. 9701cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 9711cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean start(float startScale, float targetScale, float centerX, float centerY) { 9721cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mRunning) { 9731cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return false; 9741cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 9751cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9761cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mCenterX = centerX; 9771cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mCenterY = centerY; 9781cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9791cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Ensure the target scale is within the min/max bounds 9801cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTargetScale = targetScale; 9811cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mStartTime = System.currentTimeMillis(); 9821cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mStartScale = startScale; 9831cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mZoomingIn = mTargetScale > mStartScale; 9841cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mVelocity = (mTargetScale - mStartScale) / ZOOM_ANIMATION_DURATION; 9851cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mRunning = true; 9861cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mStop = false; 9871cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader.post(this); 9881cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 9891cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 9901cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9911cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 9921cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Stops the animation in place. It does not snap the image to its final zoom. 9931cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 9941cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void stop() { 9951cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mRunning = false; 9961cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mStop = true; 9971cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 9981cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 9991cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 10001cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void run() { 10011cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mStop) { 10021cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return; 10031cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 10041cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 10051cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Scale 10061cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein long now = System.currentTimeMillis(); 10071cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein long ellapsed = now - mStartTime; 10081cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float newScale = (mStartScale + mVelocity * ellapsed); 10091cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader.scale(newScale, mCenterX, mCenterY); 10101cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 10111cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Stop when done 10121cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (newScale == mTargetScale || (mZoomingIn == (newScale > mTargetScale))) { 10131cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader.scale(mTargetScale, mCenterX, mCenterY); 10141cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein stop(); 10151cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 10161cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 10171cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (!mStop) { 10181cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader.post(this); 10191cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 10201cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 10211cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 10221cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 10231cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 10241cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Runnable that animates an image translation operation. 10251cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 10261cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static class TranslateRunnable implements Runnable { 10271cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 10281cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static final float DECELERATION_RATE = 1000f; 10291cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static final long NEVER = -1L; 10301cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 10311cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private final PhotoView mHeader; 10321cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 10331cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mVelocityX; 10341cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mVelocityY; 10351cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 10361cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private long mLastRunTime; 10371cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mRunning; 10381cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mStop; 10391cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 10401cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public TranslateRunnable(PhotoView header) { 10411cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mLastRunTime = NEVER; 10421cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader = header; 10431cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 10441cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 10451cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 10461cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Starts the animation. 10471cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 10481cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean start(float velocityX, float velocityY) { 10491cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mRunning) { 10501cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return false; 10511cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 10521cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mLastRunTime = NEVER; 10531cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mVelocityX = velocityX; 10541cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mVelocityY = velocityY; 10551cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mStop = false; 10561cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mRunning = true; 10571cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader.post(this); 10581cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 10591cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 10601cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 10611cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 10621cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Stops the animation in place. It does not snap the image to its final translation. 10631cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 10641cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void stop() { 10651cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mRunning = false; 10661cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mStop = true; 10671cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 10681cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 10691cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 10701cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void run() { 10711cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // See if we were told to stop: 10721cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mStop) { 10731cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return; 10741cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 10751cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 10761cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Translate according to current velocities and time delta: 10771cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein long now = System.currentTimeMillis(); 10781cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float delta = (mLastRunTime != NEVER) ? (now - mLastRunTime) / 1000f : 0f; 10791cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein final boolean didTranslate = mHeader.translate(mVelocityX * delta, mVelocityY * delta); 10801cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mLastRunTime = now; 10811cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Slow down: 10821cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float slowDown = DECELERATION_RATE * delta; 10831cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mVelocityX > 0f) { 10841cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mVelocityX -= slowDown; 10851cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mVelocityX < 0f) { 10861cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mVelocityX = 0f; 10871cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 10881cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 10891cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mVelocityX += slowDown; 10901cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mVelocityX > 0f) { 10911cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mVelocityX = 0f; 10921cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 10931cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 10941cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mVelocityY > 0f) { 10951cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mVelocityY -= slowDown; 10961cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mVelocityY < 0f) { 10971cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mVelocityY = 0f; 10981cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 10991cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 11001cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mVelocityY += slowDown; 11011cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mVelocityY > 0f) { 11021cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mVelocityY = 0f; 11031cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11041cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11051cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11061cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Stop when done 11071cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if ((mVelocityX == 0f && mVelocityY == 0f) || !didTranslate) { 11081cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein stop(); 11091cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader.snap(); 11101cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11111cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11121cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // See if we need to continue flinging: 11131cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mStop) { 11141cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return; 11151cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11161cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader.post(this); 11171cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11181cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11191cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11201cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 11211cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Runnable that animates an image translation operation. 11221cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 11231cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static class SnapRunnable implements Runnable { 11241cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11251cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static final long NEVER = -1L; 11261cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11271cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private final PhotoView mHeader; 11281cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11291cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mTranslateX; 11301cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mTranslateY; 11311cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11321cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private long mStartRunTime; 11331cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mRunning; 11341cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mStop; 11351cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11361cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public SnapRunnable(PhotoView header) { 11371cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mStartRunTime = NEVER; 11381cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader = header; 11391cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11401cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11411cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 11421cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Starts the animation. 11431cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 11441cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public boolean start(float translateX, float translateY) { 11451cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mRunning) { 11461cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return false; 11471cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11481cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mStartRunTime = NEVER; 11491cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTranslateX = translateX; 11501cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTranslateY = translateY; 11511cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mStop = false; 11521cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mRunning = true; 11531cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader.postDelayed(this, SNAP_DELAY); 11541cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return true; 11551cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11561cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11571cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 11581cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Stops the animation in place. It does not snap the image to its final translation. 11591cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 11601cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void stop() { 11611cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mRunning = false; 11621cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mStop = true; 11631cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11641cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11651cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 11661cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void run() { 11671cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // See if we were told to stop: 11681cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mStop) { 11691cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return; 11701cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11711cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11721cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // Translate according to current velocities and time delta: 11731cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein long now = System.currentTimeMillis(); 11741cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float delta = (mStartRunTime != NEVER) ? (now - mStartRunTime) : 0f; 11751cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11761cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mStartRunTime == NEVER) { 11771cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mStartRunTime = now; 11781cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11791cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11801cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float transX; 11811cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float transY; 11821cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (delta >= SNAP_DURATION) { 11831cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein transX = mTranslateX; 11841cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein transY = mTranslateY; 11851cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } else { 11861cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein transX = (mTranslateX / (SNAP_DURATION - delta)) * 10f; 11871cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein transY = (mTranslateY / (SNAP_DURATION - delta)) * 10f; 11881cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (Math.abs(transX) > Math.abs(mTranslateX) || transX == Float.NaN) { 11891cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein transX = mTranslateX; 11901cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11911cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (Math.abs(transY) > Math.abs(mTranslateY) || transY == Float.NaN) { 11921cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein transY = mTranslateY; 11931cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11941cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 11951cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 11961cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader.translate(transX, transY); 11971cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTranslateX -= transX; 11981cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTranslateY -= transY; 11991cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 12001cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mTranslateX == 0 && mTranslateY == 0) { 12011cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein stop(); 12021cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12031cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 12041cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein // See if we need to continue flinging: 12051cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mStop) { 12061cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return; 12071cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12081cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader.post(this); 12091cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12101cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12111cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 12121cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 12131cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Runnable that animates an image rotation operation. 12141cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 12151cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static class RotateRunnable implements Runnable { 12161cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 12171cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private static final long NEVER = -1L; 12181cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 12191cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private final PhotoView mHeader; 12201cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 12211cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mTargetRotation; 12221cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mAppliedRotation; 12231cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private float mVelocity; 12241cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private long mLastRuntime; 12251cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 12261cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mRunning; 12271cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein private boolean mStop; 12281cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 12291cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public RotateRunnable(PhotoView header) { 12301cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader = header; 12311cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12321cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 12331cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 12341cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Starts the animation. 12351cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 12361cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void start(float rotation) { 12371cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mRunning) { 12381cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return; 12391cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12401cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 12411cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mTargetRotation = rotation; 12421cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mVelocity = mTargetRotation / ROTATE_ANIMATION_DURATION; 12431cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mAppliedRotation = 0f; 12441cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mLastRuntime = NEVER; 12451cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mStop = false; 12461cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mRunning = true; 12471cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader.post(this); 12481cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12491cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 12501cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein /** 12511cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein * Stops the animation in place. It does not snap the image to its final rotation. 12521cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein */ 12531cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void stop() { 12541cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mRunning = false; 12551cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mStop = true; 12561cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12571cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 12581cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein @Override 12591cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein public void run() { 12601cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mStop) { 12611cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return; 12621cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12631cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 12641cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mAppliedRotation != mTargetRotation) { 12651cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein long now = System.currentTimeMillis(); 12661cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein long delta = mLastRuntime != NEVER ? now - mLastRuntime : 0L; 12671cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein float rotationAmount = mVelocity * delta; 12681cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mAppliedRotation < mTargetRotation 12691cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein && mAppliedRotation + rotationAmount > mTargetRotation 12701cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein || mAppliedRotation > mTargetRotation 12711cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein && mAppliedRotation + rotationAmount < mTargetRotation) { 12721cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein rotationAmount = mTargetRotation - mAppliedRotation; 12731cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12741cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader.rotate(rotationAmount, false); 12751cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mAppliedRotation += rotationAmount; 12761cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mAppliedRotation == mTargetRotation) { 12771cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein stop(); 12781cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12791cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mLastRuntime = now; 12801cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12811cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein 12821cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein if (mStop) { 12831cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein return; 12841cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12851cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein mHeader.post(this); 12861cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12871cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein } 12881cc4b2144a45abb495c8b14f6cfc5a10fb5e8ba8Andrew Sapperstein} 1289