Home | History | Annotate | Download | only in imageshow
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.gallery3d.filtershow.imageshow;
     18 
     19 import android.content.Context;
     20 import android.graphics.Bitmap;
     21 import android.graphics.Canvas;
     22 import android.graphics.Color;
     23 import android.graphics.Matrix;
     24 import android.graphics.Paint;
     25 import android.graphics.Point;
     26 import android.graphics.Rect;
     27 import android.graphics.RectF;
     28 import android.net.Uri;
     29 import android.os.Handler;
     30 import android.util.AttributeSet;
     31 import android.view.GestureDetector;
     32 import android.view.GestureDetector.OnDoubleTapListener;
     33 import android.view.GestureDetector.OnGestureListener;
     34 import android.view.MotionEvent;
     35 import android.view.ScaleGestureDetector;
     36 import android.view.View;
     37 import android.widget.LinearLayout;
     38 
     39 import com.android.gallery3d.filtershow.FilterShowActivity;
     40 import com.android.gallery3d.filtershow.cache.ImageLoader;
     41 import com.android.gallery3d.filtershow.filters.ImageFilter;
     42 import com.android.gallery3d.filtershow.presets.ImagePreset;
     43 
     44 import java.io.File;
     45 
     46 public class ImageShow extends View implements OnGestureListener,
     47         ScaleGestureDetector.OnScaleGestureListener,
     48         OnDoubleTapListener {
     49 
     50     private static final String LOGTAG = "ImageShow";
     51     private static final boolean ENABLE_ZOOMED_COMPARISON = false;
     52 
     53     protected Paint mPaint = new Paint();
     54     protected static int mTextSize = 24;
     55     protected static int mTextPadding = 20;
     56 
     57     protected ImageLoader mImageLoader = null;
     58     private boolean mDirtyGeometry = false;
     59 
     60     private Bitmap mBackgroundImage = null;
     61     private final boolean USE_BACKGROUND_IMAGE = false;
     62     private static int mBackgroundColor = Color.RED;
     63 
     64     private GestureDetector mGestureDetector = null;
     65     private ScaleGestureDetector mScaleGestureDetector = null;
     66 
     67     protected Rect mImageBounds = new Rect();
     68     private boolean mOriginalDisabled = false;
     69     private boolean mTouchShowOriginal = false;
     70     private long mTouchShowOriginalDate = 0;
     71     private final long mTouchShowOriginalDelayMin = 200; // 200ms
     72     private final long mTouchShowOriginalDelayMax = 300; // 300ms
     73     private int mShowOriginalDirection = 0;
     74     private static int UNVEIL_HORIZONTAL = 1;
     75     private static int UNVEIL_VERTICAL = 2;
     76 
     77     private Point mTouchDown = new Point();
     78     private Point mTouch = new Point();
     79     private boolean mFinishedScalingOperation = false;
     80 
     81     private static int mOriginalTextMargin = 8;
     82     private static int mOriginalTextSize = 26;
     83     private static String mOriginalText = "Original";
     84     private boolean mZoomIn = false;
     85     Point mOriginalTranslation = new Point();
     86     float mOriginalScale;
     87     float mStartFocusX, mStartFocusY;
     88     private enum InteractionMode {
     89         NONE,
     90         SCALE,
     91         MOVE
     92     }
     93     private String mToast = null;
     94     private boolean mShowToast = false;
     95     private boolean mImportantToast = false;
     96     InteractionMode mInteractionMode = InteractionMode.NONE;
     97 
     98     protected GeometryMetadata getGeometry() {
     99         return new GeometryMetadata(getImagePreset().mGeoData);
    100     }
    101 
    102     private FilterShowActivity mActivity = null;
    103 
    104     public static void setDefaultBackgroundColor(int value) {
    105         mBackgroundColor = value;
    106     }
    107 
    108     public FilterShowActivity getActivity() {
    109         return mActivity;
    110     }
    111 
    112     public int getDefaultBackgroundColor() {
    113         return mBackgroundColor;
    114     }
    115 
    116     public static void setTextSize(int value) {
    117         mTextSize = value;
    118     }
    119 
    120     public static void setTextPadding(int value) {
    121         mTextPadding = value;
    122     }
    123 
    124     public static void setOriginalTextMargin(int value) {
    125         mOriginalTextMargin = value;
    126     }
    127 
    128     public static void setOriginalTextSize(int value) {
    129         mOriginalTextSize = value;
    130     }
    131 
    132     public static void setOriginalText(String text) {
    133         mOriginalText = text;
    134     }
    135 
    136     private final Handler mHandler = new Handler();
    137 
    138     public void select() {
    139     }
    140 
    141     public void unselect() {
    142     }
    143 
    144     public boolean hasModifications() {
    145         if (getImagePreset() == null) {
    146             return false;
    147         }
    148         return getImagePreset().hasModifications();
    149     }
    150 
    151     public void resetParameter() {
    152         // TODO: implement reset
    153     }
    154 
    155     public void onNewValue(int parameter) {
    156         invalidate();
    157         mActivity.enableSave(hasModifications());
    158     }
    159 
    160     public Point getTouchPoint() {
    161         return mTouch;
    162     }
    163 
    164     public ImageShow(Context context, AttributeSet attrs) {
    165         super(context, attrs);
    166 
    167         setupGestureDetector(context);
    168         mActivity = (FilterShowActivity) context;
    169         MasterImage.getImage().addObserver(this);
    170     }
    171 
    172     public ImageShow(Context context) {
    173         super(context);
    174 
    175         setupGestureDetector(context);
    176         mActivity = (FilterShowActivity) context;
    177         MasterImage.getImage().addObserver(this);
    178     }
    179 
    180     public void setupGestureDetector(Context context) {
    181         mGestureDetector = new GestureDetector(context, this);
    182         mScaleGestureDetector = new ScaleGestureDetector(context, this);
    183     }
    184 
    185     @Override
    186     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    187         int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
    188         int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
    189         setMeasuredDimension(parentWidth, parentHeight);
    190     }
    191 
    192     public ImageFilter getCurrentFilter() {
    193         return MasterImage.getImage().getCurrentFilter();
    194     }
    195 
    196     public void showToast(String text) {
    197         showToast(text, false);
    198     }
    199 
    200     public void showToast(String text, boolean important) {
    201         mToast = text;
    202         mShowToast = true;
    203         mImportantToast = important;
    204         invalidate();
    205 
    206         mHandler.postDelayed(new Runnable() {
    207             @Override
    208             public void run() {
    209                 mShowToast = false;
    210                 invalidate();
    211             }
    212         }, 400);
    213     }
    214 
    215     public Rect getImageBounds() {
    216         Rect dst = new Rect();
    217         getImagePreset().mGeoData.getPhotoBounds().roundOut(dst);
    218         return dst;
    219     }
    220 
    221     public Rect getImageCropBounds() {
    222         return GeometryMath.roundNearest(getImagePreset().mGeoData.getPreviewCropBounds());
    223     }
    224 
    225     /* consider moving the following 2 methods into a subclass */
    226     /**
    227      * This function calculates a Image to Screen Transformation matrix
    228      *
    229      * @param reflectRotation set true if you want the rotation encoded
    230      * @return Image to Screen transformation matrix
    231      */
    232     protected Matrix getImageToScreenMatrix(boolean reflectRotation) {
    233         GeometryMetadata geo = getImagePreset().mGeoData;
    234         if (geo == null || mImageLoader == null
    235                 || mImageLoader.getOriginalBounds() == null) {
    236             return new Matrix();
    237         }
    238         Matrix m = geo.getOriginalToScreen(reflectRotation,
    239                 mImageLoader.getOriginalBounds().width(),
    240                 mImageLoader.getOriginalBounds().height(), getWidth(), getHeight());
    241         Point translate = MasterImage.getImage().getTranslation();
    242         float scaleFactor = MasterImage.getImage().getScaleFactor();
    243         m.postTranslate(translate.x, translate.y);
    244         m.postScale(scaleFactor, scaleFactor, getWidth() / 2.0f, getHeight() / 2.0f);
    245         return m;
    246     }
    247 
    248     /**
    249      * This function calculates a to Screen Image Transformation matrix
    250      *
    251      * @param reflectRotation set true if you want the rotation encoded
    252      * @return Screen to Image transformation matrix
    253      */
    254     protected Matrix getScreenToImageMatrix(boolean reflectRotation) {
    255         Matrix m = getImageToScreenMatrix(reflectRotation);
    256         Matrix invert = new Matrix();
    257         m.invert(invert);
    258         return invert;
    259     }
    260 
    261     public Rect getDisplayedImageBounds() {
    262         return mImageBounds;
    263     }
    264 
    265     public ImagePreset getImagePreset() {
    266         return MasterImage.getImage().getPreset();
    267     }
    268 
    269     public void drawToast(Canvas canvas) {
    270         if (mShowToast && mToast != null) {
    271             Paint paint = new Paint();
    272             paint.setTextSize(128);
    273             float textWidth = paint.measureText(mToast);
    274             int toastX = (int) ((getWidth() - textWidth) / 2.0f);
    275             int toastY = (int) (getHeight() / 3.0f);
    276 
    277             paint.setARGB(255, 0, 0, 0);
    278             canvas.drawText(mToast, toastX - 2, toastY - 2, paint);
    279             canvas.drawText(mToast, toastX - 2, toastY, paint);
    280             canvas.drawText(mToast, toastX, toastY - 2, paint);
    281             canvas.drawText(mToast, toastX + 2, toastY + 2, paint);
    282             canvas.drawText(mToast, toastX + 2, toastY, paint);
    283             canvas.drawText(mToast, toastX, toastY + 2, paint);
    284             if (mImportantToast) {
    285                 paint.setARGB(255, 200, 0, 0);
    286             } else {
    287                 paint.setARGB(255, 255, 255, 255);
    288             }
    289             canvas.drawText(mToast, toastX, toastY, paint);
    290         }
    291     }
    292 
    293     @Override
    294     public void onDraw(Canvas canvas) {
    295         MasterImage.getImage().setImageShowSize(getWidth(), getHeight());
    296 
    297         float cx = canvas.getWidth()/2.0f;
    298         float cy = canvas.getHeight()/2.0f;
    299         float scaleFactor = MasterImage.getImage().getScaleFactor();
    300         Point translation = MasterImage.getImage().getTranslation();
    301 
    302         Matrix scalingMatrix = new Matrix();
    303         scalingMatrix.postScale(scaleFactor, scaleFactor, cx, cy);
    304         scalingMatrix.preTranslate(translation.x, translation.y);
    305 
    306         RectF unscaledClipRect = new RectF(mImageBounds);
    307         scalingMatrix.mapRect(unscaledClipRect, unscaledClipRect);
    308 
    309         canvas.save();
    310 
    311         boolean enablePartialRendering = false;
    312 
    313         // For now, partial rendering is disabled for all filters,
    314         // so no need to clip.
    315         if (enablePartialRendering && !unscaledClipRect.isEmpty()) {
    316             canvas.clipRect(unscaledClipRect);
    317         }
    318 
    319         canvas.save();
    320         // TODO: center scale on gesture
    321         canvas.scale(scaleFactor, scaleFactor, cx, cy);
    322         canvas.translate(translation.x, translation.y);
    323         drawBackground(canvas);
    324         drawImage(canvas, getFilteredImage(), true);
    325         Bitmap highresPreview = MasterImage.getImage().getHighresImage();
    326         if (highresPreview != null) {
    327             drawImage(canvas, highresPreview, false);
    328         }
    329         canvas.restore();
    330 
    331         if (showTitle() && getImagePreset() != null) {
    332             mPaint.setARGB(200, 0, 0, 0);
    333             mPaint.setTextSize(mTextSize);
    334 
    335             Rect textRect = new Rect(0, 0, getWidth(), mTextSize + mTextPadding);
    336             canvas.drawRect(textRect, mPaint);
    337             mPaint.setARGB(255, 200, 200, 200);
    338             canvas.drawText(getImagePreset().name(), mTextPadding,
    339                     1.5f * mTextPadding, mPaint);
    340         }
    341 
    342         Bitmap partialPreview = MasterImage.getImage().getPartialImage();
    343         if (partialPreview != null) {
    344             Rect src = new Rect(0, 0, partialPreview.getWidth(), partialPreview.getHeight());
    345             Rect dest = new Rect(0, 0, getWidth(), getHeight());
    346             canvas.drawBitmap(partialPreview, src, dest, mPaint);
    347         }
    348 
    349         canvas.save();
    350         canvas.scale(scaleFactor, scaleFactor, cx, cy);
    351         canvas.translate(translation.x, translation.y);
    352         drawPartialImage(canvas, getGeometryOnlyImage());
    353         canvas.restore();
    354 
    355         canvas.restore();
    356 
    357         drawToast(canvas);
    358     }
    359 
    360     public void resetImageCaches(ImageShow caller) {
    361         if (mImageLoader == null) {
    362             return;
    363         }
    364         MasterImage.getImage().updatePresets(true);
    365     }
    366 
    367     public Bitmap getFiltersOnlyImage() {
    368         return MasterImage.getImage().getFiltersOnlyImage();
    369     }
    370 
    371     public Bitmap getGeometryOnlyImage() {
    372         return MasterImage.getImage().getGeometryOnlyImage();
    373     }
    374 
    375     public Bitmap getFilteredImage() {
    376         return MasterImage.getImage().getFilteredImage();
    377     }
    378 
    379     public void drawImage(Canvas canvas, Bitmap image, boolean updateBounds) {
    380         if (image != null) {
    381             Rect s = new Rect(0, 0, image.getWidth(),
    382                     image.getHeight());
    383 
    384             float scale = GeometryMath.scale(image.getWidth(), image.getHeight(), getWidth(),
    385                     getHeight());
    386 
    387             float w = image.getWidth() * scale;
    388             float h = image.getHeight() * scale;
    389             float ty = (getHeight() - h) / 2.0f;
    390             float tx = (getWidth() - w) / 2.0f;
    391 
    392             Rect d = new Rect((int) tx, (int) ty, (int) (w + tx),
    393                     (int) (h + ty));
    394             if (updateBounds) {
    395                 mImageBounds = d;
    396             }
    397             canvas.drawBitmap(image, s, d, mPaint);
    398         }
    399     }
    400 
    401     public void drawPartialImage(Canvas canvas, Bitmap image) {
    402         boolean showsOriginal = MasterImage.getImage().showsOriginal();
    403         if (!showsOriginal && !mTouchShowOriginal)
    404             return;
    405         canvas.save();
    406         if (image != null) {
    407             if (mShowOriginalDirection == 0) {
    408                 if (Math.abs(mTouch.y - mTouchDown.y) > Math.abs(mTouch.x - mTouchDown.x)) {
    409                     mShowOriginalDirection = UNVEIL_VERTICAL;
    410                 } else {
    411                     mShowOriginalDirection = UNVEIL_HORIZONTAL;
    412                 }
    413             }
    414 
    415             int px = 0;
    416             int py = 0;
    417             if (mShowOriginalDirection == UNVEIL_VERTICAL) {
    418                 px = mImageBounds.width();
    419                 py = (int) (mTouch.y - mImageBounds.top);
    420             } else {
    421                 px = (int) (mTouch.x - mImageBounds.left);
    422                 py = mImageBounds.height();
    423                 if (showsOriginal) {
    424                     px = mImageBounds.width();
    425                 }
    426             }
    427 
    428             Rect d = new Rect(mImageBounds.left, mImageBounds.top,
    429                     mImageBounds.left + px, mImageBounds.top + py);
    430             canvas.clipRect(d);
    431             drawImage(canvas, image, false);
    432             Paint paint = new Paint();
    433             paint.setColor(Color.BLACK);
    434             paint.setStrokeWidth(3);
    435 
    436             if (mShowOriginalDirection == UNVEIL_VERTICAL) {
    437                 canvas.drawLine(mImageBounds.left, mTouch.y,
    438                         mImageBounds.right, mTouch.y, paint);
    439             } else {
    440                 canvas.drawLine(mTouch.x, mImageBounds.top,
    441                         mTouch.x, mImageBounds.bottom, paint);
    442             }
    443 
    444             Rect bounds = new Rect();
    445             paint.setAntiAlias(true);
    446             paint.setTextSize(mOriginalTextSize);
    447             paint.getTextBounds(mOriginalText, 0, mOriginalText.length(), bounds);
    448             paint.setColor(Color.BLACK);
    449             paint.setStyle(Paint.Style.STROKE);
    450             paint.setStrokeWidth(3);
    451             canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin,
    452                     mImageBounds.top + bounds.height() + mOriginalTextMargin, paint);
    453             paint.setStyle(Paint.Style.FILL);
    454             paint.setStrokeWidth(1);
    455             paint.setColor(Color.WHITE);
    456             canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin,
    457                     mImageBounds.top + bounds.height() + mOriginalTextMargin, paint);
    458         }
    459         canvas.restore();
    460     }
    461 
    462     public void drawBackground(Canvas canvas) {
    463         if (USE_BACKGROUND_IMAGE) {
    464             if (mBackgroundImage == null) {
    465                 mBackgroundImage = mImageLoader.getBackgroundBitmap(getResources());
    466             }
    467             if (mBackgroundImage != null) {
    468                 Rect s = new Rect(0, 0, mBackgroundImage.getWidth(),
    469                         mBackgroundImage.getHeight());
    470                 Rect d = new Rect(0, 0, getWidth(), getHeight());
    471                 canvas.drawBitmap(mBackgroundImage, s, d, mPaint);
    472             }
    473         } else {
    474             canvas.drawARGB(0, 0, 0, 0);
    475         }
    476     }
    477 
    478     public boolean showTitle() {
    479         return false;
    480     }
    481 
    482     public void setImageLoader(ImageLoader loader) {
    483         mImageLoader = loader;
    484         if (mImageLoader != null) {
    485             mImageLoader.addListener(this);
    486             MasterImage.getImage().setImageLoader(mImageLoader);
    487         }
    488     }
    489 
    490     private void setDirtyGeometryFlag() {
    491         mDirtyGeometry = true;
    492     }
    493 
    494     protected void clearDirtyGeometryFlag() {
    495         mDirtyGeometry = false;
    496     }
    497 
    498     protected boolean getDirtyGeometryFlag() {
    499         return mDirtyGeometry;
    500     }
    501 
    502     private void imageSizeChanged(Bitmap image) {
    503         if (image == null || getImagePreset() == null)
    504             return;
    505         float w = image.getWidth();
    506         float h = image.getHeight();
    507         GeometryMetadata geo = getImagePreset().mGeoData;
    508         RectF pb = geo.getPhotoBounds();
    509         if (w == pb.width() && h == pb.height()) {
    510             return;
    511         }
    512         RectF r = new RectF(0, 0, w, h);
    513         getImagePreset().mGeoData.setPhotoBounds(r);
    514         getImagePreset().mGeoData.setCropBounds(r);
    515 
    516     }
    517 
    518     public boolean updateGeometryFlags() {
    519         return true;
    520     }
    521 
    522     public void updateImage() {
    523         invalidate();
    524         if (!updateGeometryFlags()) {
    525             return;
    526         }
    527         Bitmap bitmap = mImageLoader.getOriginalBitmapLarge();
    528         if (bitmap != null) {
    529             imageSizeChanged(bitmap);
    530         }
    531     }
    532 
    533     public void imageLoaded() {
    534         updateImage();
    535         invalidate();
    536     }
    537 
    538     public void saveImage(FilterShowActivity filterShowActivity, File file) {
    539         mImageLoader.saveImage(getImagePreset(), filterShowActivity, file);
    540     }
    541 
    542     public void saveToUri(Bitmap f, Uri u, String m, FilterShowActivity filterShowActivity) {
    543         mImageLoader.saveToUri(f, u, m, filterShowActivity);
    544     }
    545 
    546     public void returnFilteredResult(FilterShowActivity filterShowActivity) {
    547         mImageLoader.returnFilteredResult(getImagePreset(), filterShowActivity);
    548     }
    549 
    550     public boolean scaleInProgress() {
    551         return mScaleGestureDetector.isInProgress();
    552     }
    553 
    554     protected boolean isOriginalDisabled() {
    555         return mOriginalDisabled;
    556     }
    557 
    558     protected void setOriginalDisabled(boolean originalDisabled) {
    559         mOriginalDisabled = originalDisabled;
    560     }
    561 
    562     @Override
    563     public boolean onTouchEvent(MotionEvent event) {
    564         super.onTouchEvent(event);
    565         int action = event.getAction();
    566         action = action & MotionEvent.ACTION_MASK;
    567 
    568         mGestureDetector.onTouchEvent(event);
    569         boolean scaleInProgress = scaleInProgress();
    570         mScaleGestureDetector.onTouchEvent(event);
    571         if (mInteractionMode == InteractionMode.SCALE) {
    572             return true;
    573         }
    574         if (!scaleInProgress() && scaleInProgress) {
    575             // If we were scaling, the scale will stop but we will
    576             // still issue an ACTION_UP. Let the subclasses know.
    577             mFinishedScalingOperation = true;
    578         }
    579 
    580         int ex = (int) event.getX();
    581         int ey = (int) event.getY();
    582         if (action == MotionEvent.ACTION_DOWN) {
    583             mInteractionMode = InteractionMode.MOVE;
    584             mTouchDown.x = ex;
    585             mTouchDown.y = ey;
    586             mTouchShowOriginalDate = System.currentTimeMillis();
    587             mShowOriginalDirection = 0;
    588             MasterImage.getImage().setOriginalTranslation(MasterImage.getImage().getTranslation());
    589         }
    590 
    591         if (action == MotionEvent.ACTION_MOVE && mInteractionMode == InteractionMode.MOVE) {
    592             mTouch.x = ex;
    593             mTouch.y = ey;
    594 
    595             float scaleFactor = MasterImage.getImage().getScaleFactor();
    596             if (scaleFactor > 1 && (!ENABLE_ZOOMED_COMPARISON || event.getPointerCount() == 2)) {
    597                 float translateX = (mTouch.x - mTouchDown.x) / scaleFactor;
    598                 float translateY = (mTouch.y - mTouchDown.y) / scaleFactor;
    599                 Point originalTranslation = MasterImage.getImage().getOriginalTranslation();
    600                 Point translation = MasterImage.getImage().getTranslation();
    601                 translation.x = (int) (originalTranslation.x + translateX);
    602                 translation.y = (int) (originalTranslation.y + translateY);
    603                 MasterImage.getImage().setTranslation(translation);
    604                 mTouchShowOriginal = false;
    605             } else if (enableComparison() && !mOriginalDisabled
    606                     && (System.currentTimeMillis() - mTouchShowOriginalDate
    607                             > mTouchShowOriginalDelayMin)
    608                     && event.getPointerCount() == 1) {
    609                 mTouchShowOriginal = true;
    610             }
    611         }
    612 
    613         if (action == MotionEvent.ACTION_UP) {
    614             mInteractionMode = InteractionMode.NONE;
    615             mTouchShowOriginal = false;
    616             mTouchDown.x = 0;
    617             mTouchDown.y = 0;
    618             mTouch.x = 0;
    619             mTouch.y = 0;
    620             if (MasterImage.getImage().getScaleFactor() <= 1) {
    621                 MasterImage.getImage().setScaleFactor(1);
    622                 MasterImage.getImage().resetTranslation();
    623             }
    624         }
    625         invalidate();
    626         return true;
    627     }
    628 
    629     protected boolean enableComparison() {
    630         return true;
    631     }
    632 
    633     // listview stuff
    634     public void showOriginal(boolean show) {
    635         invalidate();
    636     }
    637 
    638     @Override
    639     public boolean onDoubleTap(MotionEvent arg0) {
    640         mZoomIn = !mZoomIn;
    641         float scale = 1.0f;
    642         if (mZoomIn) {
    643             scale = MasterImage.getImage().getMaxScaleFactor();
    644         }
    645         if (scale != MasterImage.getImage().getScaleFactor()) {
    646             MasterImage.getImage().setScaleFactor(scale);
    647             invalidate();
    648         }
    649         return true;
    650     }
    651 
    652     @Override
    653     public boolean onDoubleTapEvent(MotionEvent arg0) {
    654         // TODO Auto-generated method stub
    655         return false;
    656     }
    657 
    658     @Override
    659     public boolean onSingleTapConfirmed(MotionEvent arg0) {
    660         // TODO Auto-generated method stub
    661         return false;
    662     }
    663 
    664     @Override
    665     public boolean onDown(MotionEvent arg0) {
    666         // TODO Auto-generated method stub
    667         return false;
    668     }
    669 
    670     @Override
    671     public boolean onFling(MotionEvent startEvent, MotionEvent endEvent, float arg2, float arg3) {
    672         if (mActivity == null) {
    673             return false;
    674         }
    675         if (endEvent.getPointerCount() == 2) {
    676             return false;
    677         }
    678         return true;
    679     }
    680 
    681     @Override
    682     public void onLongPress(MotionEvent arg0) {
    683         // TODO Auto-generated method stub
    684     }
    685 
    686     @Override
    687     public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
    688         // TODO Auto-generated method stub
    689         return false;
    690     }
    691 
    692     @Override
    693     public void onShowPress(MotionEvent arg0) {
    694         // TODO Auto-generated method stub
    695     }
    696 
    697     @Override
    698     public boolean onSingleTapUp(MotionEvent arg0) {
    699         // TODO Auto-generated method stub
    700         return false;
    701     }
    702 
    703     public boolean useUtilityPanel() {
    704         return false;
    705     }
    706 
    707     public void openUtilityPanel(final LinearLayout accessoryViewList) {
    708         // TODO Auto-generated method stub
    709     }
    710 
    711     @Override
    712     public boolean onScale(ScaleGestureDetector detector) {
    713         MasterImage img = MasterImage.getImage();
    714         float scaleFactor = img.getScaleFactor();
    715         Point pos = img.getTranslation();
    716 
    717         scaleFactor = scaleFactor * detector.getScaleFactor();
    718         if (scaleFactor > MasterImage.getImage().getMaxScaleFactor()) {
    719             scaleFactor = MasterImage.getImage().getMaxScaleFactor();
    720         }
    721         if (scaleFactor < 0.5) {
    722             scaleFactor = 0.5f;
    723         }
    724         MasterImage.getImage().setScaleFactor(scaleFactor);
    725         scaleFactor = img.getScaleFactor();
    726         pos = img.getTranslation();
    727         float focusx = detector.getFocusX();
    728         float focusy = detector.getFocusY();
    729         float translateX = (focusx - mStartFocusX) / scaleFactor;
    730         float translateY = (focusy - mStartFocusY) / scaleFactor;
    731         Point translation = MasterImage.getImage().getTranslation();
    732         translation.x = (int) (mOriginalTranslation.x + translateX);
    733         translation.y = (int) (mOriginalTranslation.y + translateY);
    734         MasterImage.getImage().setTranslation(translation);
    735 
    736         invalidate();
    737         return true;
    738     }
    739 
    740     @Override
    741     public boolean onScaleBegin(ScaleGestureDetector detector) {
    742         Point pos = MasterImage.getImage().getTranslation();
    743         mOriginalTranslation.x = pos.x;
    744         mOriginalTranslation.y = pos.y;
    745         mOriginalScale = MasterImage.getImage().getScaleFactor();
    746         mStartFocusX = detector.getFocusX();
    747         mStartFocusY = detector.getFocusY();
    748         mInteractionMode = InteractionMode.SCALE;
    749         return true;
    750     }
    751 
    752     @Override
    753     public void onScaleEnd(ScaleGestureDetector detector) {
    754         mInteractionMode = InteractionMode.NONE;
    755         if (MasterImage.getImage().getScaleFactor() < 1) {
    756             MasterImage.getImage().setScaleFactor(1);
    757             invalidate();
    758         }
    759     }
    760 
    761     public boolean didFinishScalingOperation() {
    762         if (mFinishedScalingOperation) {
    763             mFinishedScalingOperation = false;
    764             return true;
    765         }
    766         return false;
    767     }
    768 
    769 }
    770