OSDN Git Service

Merge "zoom to center of double tap" into gb-ub-photos-carlsbad
[android-x86/packages-apps-Gallery2.git] / src / com / android / gallery3d / filtershow / imageshow / ImageShow.java
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
543     public boolean scaleInProgress() {
544         return mScaleGestureDetector.isInProgress();
545     }
546
547     protected boolean isOriginalDisabled() {
548         return mOriginalDisabled;
549     }
550
551     protected void setOriginalDisabled(boolean originalDisabled) {
552         mOriginalDisabled = originalDisabled;
553     }
554
555     @Override
556     public boolean onTouchEvent(MotionEvent event) {
557         super.onTouchEvent(event);
558         int action = event.getAction();
559         action = action & MotionEvent.ACTION_MASK;
560
561         mGestureDetector.onTouchEvent(event);
562         boolean scaleInProgress = scaleInProgress();
563         mScaleGestureDetector.onTouchEvent(event);
564         if (mInteractionMode == InteractionMode.SCALE) {
565             return true;
566         }
567         if (!scaleInProgress() && scaleInProgress) {
568             // If we were scaling, the scale will stop but we will
569             // still issue an ACTION_UP. Let the subclasses know.
570             mFinishedScalingOperation = true;
571         }
572
573         int ex = (int) event.getX();
574         int ey = (int) event.getY();
575         if (action == MotionEvent.ACTION_DOWN) {
576             mInteractionMode = InteractionMode.MOVE;
577             mTouchDown.x = ex;
578             mTouchDown.y = ey;
579             mTouchShowOriginalDate = System.currentTimeMillis();
580             mShowOriginalDirection = 0;
581             MasterImage.getImage().setOriginalTranslation(MasterImage.getImage().getTranslation());
582         }
583
584         if (action == MotionEvent.ACTION_MOVE && mInteractionMode == InteractionMode.MOVE) {
585             mTouch.x = ex;
586             mTouch.y = ey;
587
588             float scaleFactor = MasterImage.getImage().getScaleFactor();
589             if (scaleFactor > 1 && (!ENABLE_ZOOMED_COMPARISON || event.getPointerCount() == 2)) {
590                 float translateX = (mTouch.x - mTouchDown.x) / scaleFactor;
591                 float translateY = (mTouch.y - mTouchDown.y) / scaleFactor;
592                 Point originalTranslation = MasterImage.getImage().getOriginalTranslation();
593                 Point translation = MasterImage.getImage().getTranslation();
594                 translation.x = (int) (originalTranslation.x + translateX);
595                 translation.y = (int) (originalTranslation.y + translateY);
596                 constrainTranslation(translation, scaleFactor);
597                 MasterImage.getImage().setTranslation(translation);
598                 mTouchShowOriginal = false;
599             } else if (enableComparison() && !mOriginalDisabled
600                     && (System.currentTimeMillis() - mTouchShowOriginalDate
601                             > mTouchShowOriginalDelayMin)
602                     && event.getPointerCount() == 1) {
603                 mTouchShowOriginal = true;
604             }
605         }
606
607         if (action == MotionEvent.ACTION_UP) {
608             mInteractionMode = InteractionMode.NONE;
609             mTouchShowOriginal = false;
610             mTouchDown.x = 0;
611             mTouchDown.y = 0;
612             mTouch.x = 0;
613             mTouch.y = 0;
614             if (MasterImage.getImage().getScaleFactor() <= 1) {
615                 MasterImage.getImage().setScaleFactor(1);
616                 MasterImage.getImage().resetTranslation();
617             }
618         }
619         invalidate();
620         return true;
621     }
622
623     protected boolean enableComparison() {
624         return true;
625     }
626
627     // listview stuff
628     public void showOriginal(boolean show) {
629         invalidate();
630     }
631
632     @Override
633     public boolean onDoubleTap(MotionEvent arg0) {
634         mZoomIn = !mZoomIn;
635         float scale = 1.0f;
636         if (mZoomIn) {
637             scale = MasterImage.getImage().getMaxScaleFactor();
638         }
639         if (scale != MasterImage.getImage().getScaleFactor()) {
640             MasterImage.getImage().setScaleFactor(scale);
641             float translateX = (getWidth() / 2 - arg0.getX());
642             float translateY = (getHeight() / 2 - arg0.getY());
643             Point translation = MasterImage.getImage().getTranslation();
644             translation.x = (int) (mOriginalTranslation.x + translateX);
645             translation.y = (int) (mOriginalTranslation.y + translateY);
646             constrainTranslation(translation, scale);
647             MasterImage.getImage().setTranslation(translation);
648             invalidate();
649         }
650         return true;
651     }
652
653     private void constrainTranslation(Point translation, float scale) {
654         float maxTranslationX = getWidth() / scale;
655         float maxTranslationY = getHeight() / scale;
656         if (Math.abs(translation.x) > maxTranslationX) {
657             translation.x = (int) (Math.signum(translation.x) *
658                     maxTranslationX);
659             if (Math.abs(translation.y) > maxTranslationY) {
660                 translation.y = (int) (Math.signum(translation.y) *
661                         maxTranslationY);
662             }
663
664         }
665     }
666
667     @Override
668     public boolean onDoubleTapEvent(MotionEvent arg0) {
669         // TODO Auto-generated method stub
670         return false;
671     }
672
673     @Override
674     public boolean onSingleTapConfirmed(MotionEvent arg0) {
675         // TODO Auto-generated method stub
676         return false;
677     }
678
679     @Override
680     public boolean onDown(MotionEvent arg0) {
681         // TODO Auto-generated method stub
682         return false;
683     }
684
685     @Override
686     public boolean onFling(MotionEvent startEvent, MotionEvent endEvent, float arg2, float arg3) {
687         if (mActivity == null) {
688             return false;
689         }
690         if (endEvent.getPointerCount() == 2) {
691             return false;
692         }
693         return true;
694     }
695
696     @Override
697     public void onLongPress(MotionEvent arg0) {
698         // TODO Auto-generated method stub
699     }
700
701     @Override
702     public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
703         // TODO Auto-generated method stub
704         return false;
705     }
706
707     @Override
708     public void onShowPress(MotionEvent arg0) {
709         // TODO Auto-generated method stub
710     }
711
712     @Override
713     public boolean onSingleTapUp(MotionEvent arg0) {
714         // TODO Auto-generated method stub
715         return false;
716     }
717
718     public boolean useUtilityPanel() {
719         return false;
720     }
721
722     public void openUtilityPanel(final LinearLayout accessoryViewList) {
723         // TODO Auto-generated method stub
724     }
725
726     @Override
727     public boolean onScale(ScaleGestureDetector detector) {
728         MasterImage img = MasterImage.getImage();
729         float scaleFactor = img.getScaleFactor();
730         Point pos = img.getTranslation();
731
732         scaleFactor = scaleFactor * detector.getScaleFactor();
733         if (scaleFactor > MasterImage.getImage().getMaxScaleFactor()) {
734             scaleFactor = MasterImage.getImage().getMaxScaleFactor();
735         }
736         if (scaleFactor < 0.5) {
737             scaleFactor = 0.5f;
738         }
739         MasterImage.getImage().setScaleFactor(scaleFactor);
740         scaleFactor = img.getScaleFactor();
741         pos = img.getTranslation();
742         float focusx = detector.getFocusX();
743         float focusy = detector.getFocusY();
744         float translateX = (focusx - mStartFocusX) / scaleFactor;
745         float translateY = (focusy - mStartFocusY) / scaleFactor;
746         Point translation = MasterImage.getImage().getTranslation();
747         translation.x = (int) (mOriginalTranslation.x + translateX);
748         translation.y = (int) (mOriginalTranslation.y + translateY);
749         constrainTranslation(translation, scaleFactor);
750         MasterImage.getImage().setTranslation(translation);
751
752         invalidate();
753         return true;
754     }
755
756     @Override
757     public boolean onScaleBegin(ScaleGestureDetector detector) {
758         Point pos = MasterImage.getImage().getTranslation();
759         mOriginalTranslation.x = pos.x;
760         mOriginalTranslation.y = pos.y;
761         mOriginalScale = MasterImage.getImage().getScaleFactor();
762         mStartFocusX = detector.getFocusX();
763         mStartFocusY = detector.getFocusY();
764         mInteractionMode = InteractionMode.SCALE;
765         return true;
766     }
767
768     @Override
769     public void onScaleEnd(ScaleGestureDetector detector) {
770         mInteractionMode = InteractionMode.NONE;
771         if (MasterImage.getImage().getScaleFactor() < 1) {
772             MasterImage.getImage().setScaleFactor(1);
773             invalidate();
774         }
775     }
776
777     public boolean didFinishScalingOperation() {
778         if (mFinishedScalingOperation) {
779             mFinishedScalingOperation = false;
780             return true;
781         }
782         return false;
783     }
784
785 }