16a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang/*
26a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * Copyright (C) 2010 The Android Open Source Project
36a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang *
46a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * Licensed under the Apache License, Version 2.0 (the "License");
56a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * you may not use this file except in compliance with the License.
66a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * You may obtain a copy of the License at
76a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang *
86a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang *      http://www.apache.org/licenses/LICENSE-2.0
96a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang *
106a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * Unless required by applicable law or agreed to in writing, software
116a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * distributed under the License is distributed on an "AS IS" BASIS,
126a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * See the License for the specific language governing permissions and
146a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * limitations under the License.
156a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang */
166a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
176a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangpackage com.android.gallery3d.photoeditor.actions;
186a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
196a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.content.Context;
206a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.graphics.PointF;
216a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.util.AttributeSet;
226a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.view.GestureDetector;
236a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangimport android.view.MotionEvent;
246a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
256a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang/**
266a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang * A view that detects user gestures and touch motions.
276a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang */
286a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huangclass TouchView extends FullscreenToolView {
296a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
306a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    /**
316a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang     * Listener of swipes.
326a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang     */
336a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    public interface SwipeListener {
346a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
356a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang        void onSwipeLeft();
366a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
376a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang        void onSwipeRight();
386a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
396a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang        void onSwipeUp();
406a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
416a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang        void onSwipeDown();
426a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    }
436a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
446a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    /**
456a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang     * Listener of single tap on a point (relative to photo coordinates).
466a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang     */
476a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    public interface SingleTapListener {
486a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
496a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang        void onSingleTap(PointF point);
506a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    }
516a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
526a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    private final GestureDetector gestureDetector;
536a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
546a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    private SwipeListener swipeListener;
556a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    private SingleTapListener singleTapListener;
566a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
576a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    public TouchView(Context context, AttributeSet attrs) {
586a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang        super(context, attrs);
596a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
606a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang        final int swipeThreshold = (int) (500 * getResources().getDisplayMetrics().density);
616a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang        gestureDetector = new GestureDetector(
626a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                context, new GestureDetector.SimpleOnGestureListener() {
636a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
646a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang            @Override
656a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang            public boolean onDown(MotionEvent e) {
666a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                // GestureDetector onTouchEvent returns true for fling events only when their
676a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                // preceding down events are consumed.
686a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                return true;
696a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang            }
706a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
716a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang            @Override
726a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang            public boolean onSingleTapUp(MotionEvent e) {
736a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                if (singleTapListener != null) {
746a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    PointF point = new PointF();
756a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    mapPhotoPoint(e.getX(), e.getY(), point);
766a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    singleTapListener.onSingleTap(point);
776a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                }
786a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                return true;
796a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang            }
806a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
816a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang            @Override
826a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang            public boolean onFling(
836a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) {
846a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                if (swipeListener != null) {
856a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    float absX = Math.abs(velocityX);
866a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    float absY = Math.abs(velocityY);
876a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    float deltaX = me2.getX() - me1.getX();
886a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    float deltaY = me2.getY() - me1.getY();
896a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    int travelX = getWidth() / 4;
906a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    int travelY = getHeight() / 4;
916a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    if (velocityX > swipeThreshold && absY < absX && deltaX > travelX) {
926a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                        swipeListener.onSwipeRight();
936a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    } else if (velocityX < -swipeThreshold && absY < absX && deltaX < -travelX) {
946a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                        swipeListener.onSwipeLeft();
956a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    } else if (velocityY < -swipeThreshold && absX < absY && deltaY < -travelY) {
966a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                        swipeListener.onSwipeUp();
976a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    } else if (velocityY > swipeThreshold && absX < absY / 2 && deltaY > travelY) {
986a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                        swipeListener.onSwipeDown();
996a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                    }
1006a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                }
1016a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang                return true;
1026a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang            }
1036a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang        });
1046a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang        gestureDetector.setIsLongpressEnabled(false);
1056a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    }
1066a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
1076a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    public void setSwipeListener(SwipeListener listener) {
1086a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang        swipeListener = listener;
1096a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    }
1106a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
1116a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    public void setSingleTapListener(SingleTapListener listener) {
1126a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang        singleTapListener = listener;
1136a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    }
1146a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang
1156a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    @Override
1166a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    public boolean onTouchEvent(MotionEvent event) {
1176a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang        return isEnabled() && gestureDetector.onTouchEvent(event);
1186a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang    }
1196a12ad70df5da5052a8381e2cdda79e6a314cee9Yuli Huang}
120