2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.gallery3d.filtershow.imageshow;
19 import android.content.Context;
20 import android.graphics.Bitmap;
21 import android.graphics.Canvas;
22 import android.graphics.Color;
23 import android.graphics.Paint;
24 import android.graphics.Rect;
25 import android.graphics.RectF;
26 import android.os.Handler;
27 import android.util.AttributeSet;
28 import android.view.GestureDetector;
29 import android.view.GestureDetector.OnDoubleTapListener;
30 import android.view.GestureDetector.OnGestureListener;
31 import android.view.MotionEvent;
32 import android.view.View;
33 import android.widget.ArrayAdapter;
34 import android.widget.SeekBar;
35 import android.widget.SeekBar.OnSeekBarChangeListener;
37 import com.android.gallery3d.R;
38 import com.android.gallery3d.filtershow.FilterShowActivity;
39 import com.android.gallery3d.filtershow.HistoryAdapter;
40 import com.android.gallery3d.filtershow.ImageStateAdapter;
41 import com.android.gallery3d.filtershow.PanelController;
42 import com.android.gallery3d.filtershow.cache.ImageLoader;
43 import com.android.gallery3d.filtershow.filters.ImageFilter;
44 import com.android.gallery3d.filtershow.presets.ImagePreset;
45 import com.android.gallery3d.filtershow.ui.SliderController;
46 import com.android.gallery3d.filtershow.ui.SliderListener;
50 public class ImageShow extends View implements OnGestureListener,
53 OnSeekBarChangeListener {
55 private static final String LOGTAG = "ImageShow";
57 protected Paint mPaint = new Paint();
58 protected static int mTextSize = 24;
59 protected static int mTextPadding = 20;
61 protected ImagePreset mImagePreset = null;
62 protected ImagePreset mImageGeometryOnlyPreset = null;
63 protected ImagePreset mImageFiltersOnlyPreset = null;
65 protected ImageLoader mImageLoader = null;
66 private ImageFilter mCurrentFilter = null;
67 private boolean mDirtyGeometry = false;
69 private Bitmap mBackgroundImage = null;
70 private final boolean USE_BACKGROUND_IMAGE = false;
71 private static int mBackgroundColor = Color.RED;
73 private Bitmap mGeometryOnlyImage = null;
74 private Bitmap mFiltersOnlyImage = null;
75 private Bitmap mFilteredImage = null;
77 private final boolean USE_SLIDER_GESTURE = false; // set to true to have
79 protected SliderController mSliderController = new SliderController();
81 private GestureDetector mGestureDetector = null;
83 private HistoryAdapter mHistoryAdapter = null;
84 private ImageStateAdapter mImageStateAdapter = null;
86 private Rect mImageBounds = new Rect();
88 private boolean mTouchShowOriginal = false;
89 private long mTouchShowOriginalDate = 0;
90 private final long mTouchShowOriginalDelayMin = 200; // 200ms
91 private final long mTouchShowOriginalDelayMax = 300; // 300ms
92 private int mShowOriginalDirection = 0;
93 private static int UNVEIL_HORIZONTAL = 1;
94 private static int UNVEIL_VERTICAL = 2;
96 private int mTouchDownX = 0;
97 private int mTouchDownY = 0;
98 protected float mTouchX = 0;
99 protected float mTouchY = 0;
101 private static int mOriginalTextMargin = 8;
102 private static int mOriginalTextSize = 26;
103 private static String mOriginalText = "Original";
105 protected GeometryMetadata getGeometry() {
106 return new GeometryMetadata(getImagePreset().mGeoData);
109 public void setGeometry(GeometryMetadata d) {
110 getImagePreset().mGeoData.set(d);
113 private boolean mShowControls = false;
114 private boolean mShowOriginal = false;
115 private String mToast = null;
116 private boolean mShowToast = false;
117 private boolean mImportantToast = false;
119 private SeekBar mSeekBar = null;
120 private PanelController mController = null;
122 private FilterShowActivity mActivity = null;
124 public static void setDefaultBackgroundColor(int value) {
125 mBackgroundColor = value;
128 public int getDefaultBackgroundColor() {
129 return mBackgroundColor;
132 public static void setTextSize(int value) {
136 public static void setTextPadding(int value) {
137 mTextPadding = value;
140 public static void setOriginalTextMargin(int value) {
141 mOriginalTextMargin = value;
144 public static void setOriginalTextSize(int value) {
145 mOriginalTextSize = value;
148 public static void setOriginalText(String text) {
149 mOriginalText = text;
152 private final Handler mHandler = new Handler();
154 public void select() {
155 if (getCurrentFilter() != null) {
156 int parameter = getCurrentFilter().getParameter();
157 int maxp = getCurrentFilter().getMaxParameter();
158 int minp = getCurrentFilter().getMinParameter();
159 updateSeekBar(parameter, minp, maxp);
161 if (mSeekBar != null) {
162 mSeekBar.setOnSeekBarChangeListener(this);
166 private int parameterToUI(int parameter, int minp, int maxp, int uimax) {
167 return (uimax * (parameter - minp)) / (maxp - minp);
170 private int uiToParameter(int ui, int minp, int maxp, int uimax) {
171 return ((maxp - minp) * ui) / uimax + minp;
174 public void updateSeekBar(int parameter, int minp, int maxp) {
175 if (mSeekBar == null) {
178 int seekMax = mSeekBar.getMax();
179 int progress = parameterToUI(parameter, minp, maxp, seekMax);
180 mSeekBar.setProgress(progress);
181 if (getPanelController() != null) {
182 getPanelController().onNewValue(parameter);
186 public void unselect() {
190 public boolean hasModifications() {
191 if (getImagePreset() == null) {
194 return getImagePreset().hasModifications();
197 public void resetParameter() {
198 ImageFilter currentFilter = getCurrentFilter();
199 if (currentFilter != null) {
200 onNewValue(currentFilter.getDefaultParameter());
202 if (USE_SLIDER_GESTURE) {
203 mSliderController.reset();
207 public void setPanelController(PanelController controller) {
208 mController = controller;
211 public PanelController getPanelController() {
216 public void onNewValue(int parameter) {
219 if (getCurrentFilter() != null) {
220 getCurrentFilter().setParameter(parameter);
221 maxp = getCurrentFilter().getMaxParameter();
222 minp = getCurrentFilter().getMinParameter();
224 if (getImagePreset() != null) {
225 mImageLoader.resetImageForPreset(getImagePreset(), this);
226 getImagePreset().fillImageStateAdapter(mImageStateAdapter);
228 if (getPanelController() != null) {
229 getPanelController().onNewValue(parameter);
231 updateSeekBar(parameter, minp, maxp);
233 mActivity.enableSave(hasModifications());
237 public void onTouchDown(float x, float y) {
244 public void onTouchUp() {
247 public ImageShow(Context context, AttributeSet attrs) {
248 super(context, attrs);
249 if (USE_SLIDER_GESTURE) {
250 mSliderController.setListener(this);
252 mHistoryAdapter = new HistoryAdapter(context, R.layout.filtershow_history_operation_row,
254 mImageStateAdapter = new ImageStateAdapter(context,
255 R.layout.filtershow_imagestate_row);
256 setupGestureDetector(context);
257 mActivity = (FilterShowActivity) context;
260 public ImageShow(Context context) {
262 if (USE_SLIDER_GESTURE) {
263 mSliderController.setListener(this);
265 mHistoryAdapter = new HistoryAdapter(context, R.layout.filtershow_history_operation_row,
267 setupGestureDetector(context);
268 mActivity = (FilterShowActivity) context;
271 public void setupGestureDetector(Context context) {
272 mGestureDetector = new GestureDetector(context, this);
276 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
277 int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
278 int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
279 setMeasuredDimension(parentWidth, parentHeight);
280 if (USE_SLIDER_GESTURE) {
281 mSliderController.setWidth(parentWidth);
282 mSliderController.setHeight(parentHeight);
286 public void setSeekBar(SeekBar seekBar) {
290 public void setCurrentFilter(ImageFilter filter) {
291 mCurrentFilter = filter;
294 public ImageFilter getCurrentFilter() {
295 return mCurrentFilter;
298 public void setAdapter(HistoryAdapter adapter) {
299 mHistoryAdapter = adapter;
302 public void showToast(String text) {
303 showToast(text, false);
306 public void showToast(String text, boolean important) {
309 mImportantToast = important;
312 mHandler.postDelayed(new Runnable() {
321 public Rect getImageBounds() {
322 Rect dst = new Rect();
323 getImagePreset().mGeoData.getPhotoBounds().roundOut(dst);
327 public Rect getDisplayedImageBounds() {
331 public ImagePreset getImagePreset() {
335 public void drawToast(Canvas canvas) {
336 if (mShowToast && mToast != null) {
337 Paint paint = new Paint();
338 paint.setTextSize(128);
339 float textWidth = paint.measureText(mToast);
340 int toastX = (int) ((getWidth() - textWidth) / 2.0f);
341 int toastY = (int) (getHeight() / 3.0f);
343 paint.setARGB(255, 0, 0, 0);
344 canvas.drawText(mToast, toastX - 2, toastY - 2, paint);
345 canvas.drawText(mToast, toastX - 2, toastY, paint);
346 canvas.drawText(mToast, toastX, toastY - 2, paint);
347 canvas.drawText(mToast, toastX + 2, toastY + 2, paint);
348 canvas.drawText(mToast, toastX + 2, toastY, paint);
349 canvas.drawText(mToast, toastX, toastY + 2, paint);
350 if (mImportantToast) {
351 paint.setARGB(255, 200, 0, 0);
353 paint.setARGB(255, 255, 255, 255);
355 canvas.drawText(mToast, toastX, toastY, paint);
359 public void defaultDrawImage(Canvas canvas) {
360 drawImage(canvas, getFilteredImage());
361 drawPartialImage(canvas, getGeometryOnlyImage());
365 public void onDraw(Canvas canvas) {
366 drawBackground(canvas);
367 requestFilteredImages();
368 defaultDrawImage(canvas);
370 if (showTitle() && getImagePreset() != null) {
371 mPaint.setARGB(200, 0, 0, 0);
372 mPaint.setTextSize(mTextSize);
374 Rect textRect = new Rect(0, 0, getWidth(), mTextSize + mTextPadding);
375 canvas.drawRect(textRect, mPaint);
376 mPaint.setARGB(255, 200, 200, 200);
377 canvas.drawText(getImagePreset().name(), mTextPadding,
378 1.5f * mTextPadding, mPaint);
381 if (showControls()) {
382 if (USE_SLIDER_GESTURE) {
383 mSliderController.onDraw(canvas);
390 public void resetImageCaches(ImageShow caller) {
391 if (mImageLoader == null) {
394 updateImagePresets(true);
397 public void updateImagePresets(boolean force) {
398 ImagePreset preset = getImagePreset();
399 if (preset == null) {
400 mActivity.enableSave(false);
404 mImageLoader.resetImageForPreset(getImagePreset(), this);
406 if (force || mImageGeometryOnlyPreset == null) {
407 ImagePreset newPreset = new ImagePreset(preset);
408 newPreset.setDoApplyFilters(false);
409 if (mImageGeometryOnlyPreset == null
410 || !newPreset.same(mImageGeometryOnlyPreset)) {
411 mImageGeometryOnlyPreset = newPreset;
412 mGeometryOnlyImage = null;
415 if (force || mImageFiltersOnlyPreset == null) {
416 ImagePreset newPreset = new ImagePreset(preset);
417 newPreset.setDoApplyGeometry(false);
418 if (mImageFiltersOnlyPreset == null
419 || !newPreset.same(mImageFiltersOnlyPreset)) {
420 mImageFiltersOnlyPreset = newPreset;
421 mFiltersOnlyImage = null;
424 mActivity.enableSave(hasModifications());
427 public void requestFilteredImages() {
428 if (mImageLoader != null) {
429 Bitmap bitmap = mImageLoader.getImageForPreset(this,
430 getImagePreset(), showHires());
432 if (bitmap != null) {
433 if (mFilteredImage == null) {
436 mFilteredImage = bitmap;
439 updateImagePresets(false);
440 if (mImageGeometryOnlyPreset != null) {
441 bitmap = mImageLoader.getImageForPreset(this, mImageGeometryOnlyPreset,
443 if (bitmap != null) {
444 mGeometryOnlyImage = bitmap;
447 if (mImageFiltersOnlyPreset != null) {
448 bitmap = mImageLoader.getImageForPreset(this, mImageFiltersOnlyPreset,
450 if (bitmap != null) {
451 mFiltersOnlyImage = bitmap;
457 mFilteredImage = mGeometryOnlyImage;
461 public Bitmap getFiltersOnlyImage() {
462 return mFiltersOnlyImage;
465 public Bitmap getGeometryOnlyImage() {
466 return mGeometryOnlyImage;
469 public Bitmap getFilteredImage() {
470 return mFilteredImage;
473 public void drawImage(Canvas canvas, Bitmap image) {
475 Rect s = new Rect(0, 0, image.getWidth(),
478 float scale = GeometryMath.scale(image.getWidth(), image.getHeight(), getWidth(),
481 float w = image.getWidth() * scale;
482 float h = image.getHeight() * scale;
483 float ty = (getHeight() - h) / 2.0f;
484 float tx = (getWidth() - w) / 2.0f;
486 Rect d = new Rect((int) tx, (int) ty, (int) (w + tx),
489 canvas.drawBitmap(image, s, d, mPaint);
493 public void drawPartialImage(Canvas canvas, Bitmap image) {
494 if (!mTouchShowOriginal)
498 if (mShowOriginalDirection == 0) {
499 if ((mTouchY - mTouchDownY) > (mTouchX - mTouchDownX)) {
500 mShowOriginalDirection = UNVEIL_VERTICAL;
502 mShowOriginalDirection = UNVEIL_HORIZONTAL;
508 if (mShowOriginalDirection == UNVEIL_VERTICAL) {
509 px = mImageBounds.width();
510 py = (int) (mTouchY - mImageBounds.top);
512 px = (int) (mTouchX - mImageBounds.left);
513 py = mImageBounds.height();
516 Rect d = new Rect(mImageBounds.left, mImageBounds.top,
517 mImageBounds.left + px, mImageBounds.top + py);
519 drawImage(canvas, image);
520 Paint paint = new Paint();
521 paint.setColor(Color.BLACK);
523 if (mShowOriginalDirection == UNVEIL_VERTICAL) {
524 canvas.drawLine(mImageBounds.left, mTouchY - 1,
525 mImageBounds.right, mTouchY - 1, paint);
527 canvas.drawLine(mTouchX - 1, mImageBounds.top,
528 mTouchX - 1, mImageBounds.bottom, paint);
531 Rect bounds = new Rect();
532 paint.setTextSize(mOriginalTextSize);
533 paint.getTextBounds(mOriginalText, 0, mOriginalText.length(), bounds);
534 paint.setColor(Color.BLACK);
535 canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin + 1,
536 mImageBounds.top + bounds.height() + mOriginalTextMargin + 1, paint);
537 paint.setColor(Color.WHITE);
538 canvas.drawText(mOriginalText, mImageBounds.left + mOriginalTextMargin,
539 mImageBounds.top + bounds.height() + mOriginalTextMargin, paint);
544 public void drawBackground(Canvas canvas) {
545 if (USE_BACKGROUND_IMAGE) {
546 if (mBackgroundImage == null) {
547 mBackgroundImage = mImageLoader.getBackgroundBitmap(getResources());
549 if (mBackgroundImage != null) {
550 Rect s = new Rect(0, 0, mBackgroundImage.getWidth(),
551 mBackgroundImage.getHeight());
552 Rect d = new Rect(0, 0, getWidth(), getHeight());
553 canvas.drawBitmap(mBackgroundImage, s, d, mPaint);
556 canvas.drawColor(mBackgroundColor);
560 public ImageShow setShowControls(boolean value) {
561 mShowControls = value;
563 if (mSeekBar != null) {
564 mSeekBar.setVisibility(View.VISIBLE);
567 if (mSeekBar != null) {
568 mSeekBar.setVisibility(View.INVISIBLE);
574 public boolean showControls() {
575 return mShowControls;
578 public boolean showHires() {
582 public boolean showTitle() {
586 public void setImagePreset(ImagePreset preset) {
587 setImagePreset(preset, true);
590 public void setImagePreset(ImagePreset preset, boolean addToHistory) {
591 if (preset == null) {
594 mImagePreset = preset;
595 getImagePreset().setImageLoader(mImageLoader);
596 updateImagePresets(true);
598 mHistoryAdapter.addHistoryItem(getImagePreset());
600 getImagePreset().setEndpoint(this);
602 mImagePreset.fillImageStateAdapter(mImageStateAdapter);
606 public void setImageLoader(ImageLoader loader) {
607 mImageLoader = loader;
608 if (mImageLoader != null) {
609 mImageLoader.addListener(this);
610 if (mImagePreset != null) {
611 mImagePreset.setImageLoader(mImageLoader);
616 private void setDirtyGeometryFlag() {
617 mDirtyGeometry = true;
620 protected void clearDirtyGeometryFlag() {
621 mDirtyGeometry = false;
624 protected boolean getDirtyGeometryFlag() {
625 return mDirtyGeometry;
628 private void imageSizeChanged(Bitmap image) {
629 if (image == null || getImagePreset() == null)
631 float w = image.getWidth();
632 float h = image.getHeight();
633 GeometryMetadata geo = getImagePreset().mGeoData;
634 RectF pb = geo.getPhotoBounds();
635 if (w == pb.width() && h == pb.height()) {
638 RectF r = new RectF(0, 0, w, h);
639 getImagePreset().mGeoData.setPhotoBounds(r);
640 getImagePreset().mGeoData.setCropBounds(r);
641 setDirtyGeometryFlag();
644 public boolean updateGeometryFlags() {
648 public void updateImage() {
649 if (!updateGeometryFlags()) {
652 Bitmap bitmap = mImageLoader.getOriginalBitmapLarge();
653 if (bitmap != null) {
654 imageSizeChanged(bitmap);
659 public void imageLoaded() {
664 public void updateFilteredImage(Bitmap bitmap) {
665 mFilteredImage = bitmap;
668 public void saveImage(FilterShowActivity filterShowActivity, File file) {
669 mImageLoader.saveImage(getImagePreset(), filterShowActivity, file);
673 public boolean onTouchEvent(MotionEvent event) {
674 super.onTouchEvent(event);
675 if (USE_SLIDER_GESTURE) {
676 mSliderController.onTouchEvent(event);
678 if (mGestureDetector != null) {
679 mGestureDetector.onTouchEvent(event);
681 int ex = (int) event.getX();
682 int ey = (int) event.getY();
683 if (event.getAction() == MotionEvent.ACTION_DOWN) {
686 mTouchShowOriginalDate = System.currentTimeMillis();
687 mShowOriginalDirection = 0;
689 if (event.getAction() == MotionEvent.ACTION_MOVE) {
692 if (!mActivity.isShowingHistoryPanel()
693 && (System.currentTimeMillis() - mTouchShowOriginalDate
694 > mTouchShowOriginalDelayMin)) {
695 mTouchShowOriginal = true;
698 if (event.getAction() == MotionEvent.ACTION_UP) {
699 mTouchShowOriginal = false;
711 public HistoryAdapter getHistory() {
712 return mHistoryAdapter;
715 public ArrayAdapter getImageStateAdapter() {
716 return mImageStateAdapter;
719 public void onItemClick(int position) {
720 setImagePreset(new ImagePreset(mHistoryAdapter.getItem(position)), false);
721 // we need a copy from the history
722 mHistoryAdapter.setCurrentPreset(position);
725 public void showOriginal(boolean show) {
726 mShowOriginal = show;
730 public float getImageRotation() {
731 return getImagePreset().mGeoData.getRotation();
734 public float getImageRotationZoomFactor() {
735 return getImagePreset().mGeoData.getScaleFactor();
738 public void setImageRotation(float r) {
739 getImagePreset().mGeoData.setRotation(r);
742 public void setImageRotationZoomFactor(float f) {
743 getImagePreset().mGeoData.setScaleFactor(f);
746 public void setImageRotation(float imageRotation,
747 float imageRotationZoomFactor) {
748 float r = getImageRotation();
749 if (imageRotation != r) {
752 setImageRotation(imageRotation);
753 setImageRotationZoomFactor(imageRotationZoomFactor);
757 public void onProgressChanged(SeekBar arg0, int progress, boolean arg2) {
758 int parameter = progress;
759 if (getCurrentFilter() != null) {
760 int maxp = getCurrentFilter().getMaxParameter();
761 int minp = getCurrentFilter().getMinParameter();
762 parameter = uiToParameter(progress, minp, maxp, arg0.getMax());
765 onNewValue(parameter);
769 public void onStartTrackingTouch(SeekBar arg0) {
770 // TODO Auto-generated method stub
775 public void onStopTrackingTouch(SeekBar arg0) {
776 // TODO Auto-generated method stub
781 public boolean onDoubleTap(MotionEvent arg0) {
782 // TODO Auto-generated method stub
787 public boolean onDoubleTapEvent(MotionEvent arg0) {
788 // TODO Auto-generated method stub
793 public boolean onSingleTapConfirmed(MotionEvent arg0) {
794 // TODO Auto-generated method stub
799 public boolean onDown(MotionEvent arg0) {
800 // TODO Auto-generated method stub
805 public boolean onFling(MotionEvent startEvent, MotionEvent endEvent, float arg2, float arg3) {
806 if ((!mActivity.isShowingHistoryPanel() && startEvent.getX() > endEvent.getX())
807 || (mActivity.isShowingHistoryPanel() && endEvent.getX() > startEvent.getX())) {
808 if (!mTouchShowOriginal
809 || (mTouchShowOriginal &&
810 (System.currentTimeMillis() - mTouchShowOriginalDate
811 < mTouchShowOriginalDelayMax))) {
812 mActivity.toggleHistoryPanel();
819 public void onLongPress(MotionEvent arg0) {
820 // TODO Auto-generated method stub
825 public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
826 // TODO Auto-generated method stub
831 public void onShowPress(MotionEvent arg0) {
832 // TODO Auto-generated method stub
837 public boolean onSingleTapUp(MotionEvent arg0) {
838 // TODO Auto-generated method stub