PreviewGestures.java revision 4d628bcbaf4e6f2330dc9619f2129770863666dc
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
17package com.android.camera;
18
19import android.os.Handler;
20import android.os.Message;
21import android.view.MotionEvent;
22import android.view.ScaleGestureDetector;
23import android.view.ViewConfiguration;
24
25import com.android.camera.ui.PieRenderer;
26import com.android.camera.ui.RenderOverlay;
27import com.android.camera.ui.ZoomRenderer;
28
29public class PreviewGestures
30        implements ScaleGestureDetector.OnScaleGestureListener {
31
32    private static final String TAG = "CAM_gestures";
33
34    private static final long TIMEOUT_PIE = 200;
35    private static final int MSG_PIE = 1;
36    private static final int MODE_NONE = 0;
37    private static final int MODE_PIE = 1;
38    private static final int MODE_ZOOM = 2;
39    private static final int MODE_ALL = 4;
40
41    private CameraActivity mActivity;
42    private RenderOverlay mOverlay;
43    private PieRenderer mPie;
44    private ZoomRenderer mZoom;
45    private FocusOverlayManager mFocus;
46    private MotionEvent mDown;
47    private ScaleGestureDetector mScale;
48    private int mMode;
49    private int mSlop;
50    private int mTapTimeout;
51    private boolean mEnabled;
52    private boolean mZoomOnly;
53    private Handler mHandler = new Handler() {
54        public void handleMessage(Message msg) {
55            if (msg.what == MSG_PIE) {
56                mMode = MODE_PIE;
57                openPie();
58                cancelActivityTouchHandling(mDown);
59            }
60        }
61    };
62
63    public PreviewGestures(CameraActivity ctx, RenderOverlay overlay, ZoomRenderer zoom,
64            PieRenderer pie, FocusOverlayManager focus) {
65        mActivity = ctx;
66        mOverlay = overlay;
67        mPie = pie;
68        mZoom = zoom;
69        mFocus = focus;
70        mMode = MODE_ALL;
71        mScale = new ScaleGestureDetector(ctx, this);
72        mSlop = (int) ctx.getResources().getDimension(R.dimen.pie_touch_slop);
73        mTapTimeout = ViewConfiguration.getTapTimeout();
74        mEnabled = true;
75    }
76
77    public void setEnabled(boolean enabled) {
78        mEnabled = enabled;
79        if (!enabled) {
80            cancelPie();
81        }
82    }
83
84    public void setZoomOnly(boolean zoom) {
85        mZoomOnly = true;
86    }
87
88    public boolean dispatchTouch(MotionEvent m) {
89        if (!mEnabled) {
90            return mActivity.superDispatchTouchEvent(m);
91        }
92        if (MotionEvent.ACTION_DOWN == m.getActionMasked()) {
93            mMode = MODE_ALL;
94            mDown = MotionEvent.obtain(m);
95            if (mPie != null && !mZoomOnly && m.getPointerCount() < 2) {
96                mHandler.sendEmptyMessageDelayed(MSG_PIE, TIMEOUT_PIE);
97            }
98            if (mZoom != null) {
99                mScale.onTouchEvent(m);
100            }
101            // make sure this is ok
102            return mActivity.superDispatchTouchEvent(m);
103        } else if (mMode == MODE_NONE) {
104            return mActivity.superDispatchTouchEvent(m);
105        } else if (mMode == MODE_PIE) {
106            return sendToPie(m);
107        } else if (mMode == MODE_ZOOM) {
108            return mScale.onTouchEvent(m);
109        } else {
110            if (MotionEvent.ACTION_POINTER_DOWN == m.getActionMasked()) {
111                if (!mZoomOnly && !mScale.isInProgress()) {
112                    cancelPie();
113                }
114            }             // not zoom or pie mode and no timeout yet
115            if (mZoom != null) {
116                boolean res = mScale.onTouchEvent(m);
117                if (mScale.isInProgress()) {
118                    cancelPie();
119                    cancelActivityTouchHandling(m);
120                    return res;
121                }
122            }
123            if (MotionEvent.ACTION_UP == m.getActionMasked()) {
124                cancelPie();
125                cancelActivityTouchHandling(m);
126                // must have been tap to focus
127                if (mFocus != null && !mZoomOnly
128                        && (m.getEventTime() - mDown.getEventTime() < mTapTimeout)) {
129                    mDown.offsetLocation(-mOverlay.getWindowPositionX(),
130                            -mOverlay.getWindowPositionY());
131                    mFocus.onSingleTapUp((int) mDown.getX(), (int) mDown.getY());
132                    return true;
133                } else {
134                    return mActivity.superDispatchTouchEvent(m);
135                }
136            } else if (MotionEvent.ACTION_MOVE == m.getActionMasked()) {
137                if ((Math.abs(m.getX() - mDown.getX()) > mSlop)
138                        || Math.abs(m.getY() - mDown.getY()) > mSlop) {
139                    // moved to far and no timeout yet, no focus or pie
140                    cancelPie();
141                    mMode = MODE_NONE;
142                }
143                return mActivity.superDispatchTouchEvent(m);
144            }
145            return false;
146        }
147    }
148
149    public void cancelActivityTouchHandling(MotionEvent m) {
150        MotionEvent c = MotionEvent.obtain(m);
151        c.setAction(MotionEvent.ACTION_CANCEL);
152        mActivity.superDispatchTouchEvent(c);
153    }
154
155    private void openPie() {
156        mDown.offsetLocation(-mOverlay.getWindowPositionX(),
157                -mOverlay.getWindowPositionY());
158        mOverlay.directDispatchTouch(mDown, mPie);
159    }
160
161    private void cancelPie() {
162        mHandler.removeMessages(MSG_PIE);
163    }
164
165    private boolean sendToPie(MotionEvent m) {
166        m.offsetLocation(-mOverlay.getWindowPositionX(),
167                -mOverlay.getWindowPositionY());
168        return mOverlay.directDispatchTouch(m, mPie);
169    }
170
171    @Override
172    public boolean onScale(ScaleGestureDetector detector) {
173        return mZoom.onScale(detector);
174    }
175
176    @Override
177    public boolean onScaleBegin(ScaleGestureDetector detector) {
178        mMode = MODE_ZOOM;
179        return mZoom.onScaleBegin(detector);
180    }
181
182    @Override
183    public void onScaleEnd(ScaleGestureDetector detector) {
184        mZoom.onScaleEnd(detector);
185    }
186}