PreviewGestures.java revision 571a6f842d972e4ac5eccfd16ed639b6779ebf98
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.util.Log;
22import android.view.MotionEvent;
23import android.view.ScaleGestureDetector;
24import android.view.View;
25import android.view.ViewConfiguration;
26
27import com.android.camera.ui.PieRenderer;
28import com.android.camera.ui.RenderOverlay;
29import com.android.camera.ui.ZoomRenderer;
30
31import java.util.ArrayList;
32import java.util.List;
33
34public class PreviewGestures
35        implements ScaleGestureDetector.OnScaleGestureListener {
36
37    private static final String TAG = "CAM_gestures";
38
39    private static final long TIMEOUT_PIE = 200;
40    private static final int MSG_PIE = 1;
41    private static final int MODE_NONE = 0;
42    private static final int MODE_PIE = 1;
43    private static final int MODE_ZOOM = 2;
44    private static final int MODE_MODULE = 3;
45    private static final int MODE_ALL = 4;
46
47    private CameraActivity mActivity;
48    private CameraModule mModule;
49    private RenderOverlay mOverlay;
50    private PieRenderer mPie;
51    private ZoomRenderer mZoom;
52    private MotionEvent mDown;
53    private ScaleGestureDetector mScale;
54    private List<View> mReceivers;
55    private int mMode;
56    private int mSlop;
57    private int mTapTimeout;
58    private boolean mEnabled;
59    private boolean mZoomOnly;
60    private Handler mHandler = new Handler() {
61        public void handleMessage(Message msg) {
62            if (msg.what == MSG_PIE) {
63                mMode = MODE_PIE;
64                openPie();
65                cancelActivityTouchHandling(mDown);
66            }
67        }
68    };
69
70    public PreviewGestures(CameraActivity ctx, CameraModule module,
71            RenderOverlay overlay, ZoomRenderer zoom,
72            PieRenderer pie) {
73        mActivity = ctx;
74        mModule = module;
75        mOverlay = overlay;
76        mPie = pie;
77        mZoom = zoom;
78        mMode = MODE_ALL;
79        mScale = new ScaleGestureDetector(ctx, this);
80        mSlop = (int) ctx.getResources().getDimension(R.dimen.pie_touch_slop);
81        mTapTimeout = ViewConfiguration.getTapTimeout();
82        mEnabled = true;
83    }
84
85    public void setEnabled(boolean enabled) {
86        mEnabled = enabled;
87        if (!enabled) {
88            cancelPie();
89        }
90    }
91
92    public void setZoomOnly(boolean zoom) {
93        mZoomOnly = true;
94    }
95
96    public void addTouchReceiver(View v) {
97        if (mReceivers == null) {
98            mReceivers = new ArrayList<View>();
99        }
100        mReceivers.add(v);
101    }
102
103    public boolean dispatchTouch(MotionEvent m) {
104        if (!mEnabled) {
105            return mActivity.superDispatchTouchEvent(m);
106        }
107        if (MotionEvent.ACTION_DOWN == m.getActionMasked()) {
108            if (checkReceivers(m)) {
109                mMode = MODE_MODULE;
110                return mActivity.superDispatchTouchEvent(m);
111            } else {
112                mMode = MODE_ALL;
113                mDown = MotionEvent.obtain(m);
114                if (mPie != null && !mZoomOnly) {
115                    mHandler.sendEmptyMessageDelayed(MSG_PIE, TIMEOUT_PIE);
116                }
117                if (mZoom != null) {
118                    mScale.onTouchEvent(m);
119                }
120                // make sure this is ok
121                return mActivity.superDispatchTouchEvent(m);
122            }
123        } else if (mMode == MODE_NONE) {
124            return false;
125        } else if (mMode == MODE_PIE) {
126            return sendToPie(m);
127        } else if (mMode == MODE_ZOOM) {
128            return mScale.onTouchEvent(m);
129        } else if (mMode == MODE_MODULE) {
130            return mActivity.superDispatchTouchEvent(m);
131        } else {
132            if (MotionEvent.ACTION_POINTER_DOWN == m.getActionMasked()) {
133                if (!mZoomOnly) {
134                    cancelPie();
135                }
136            }
137            // not zoom or pie mode and no timeout yet
138            if (mZoom != null) {
139                boolean res = mScale.onTouchEvent(m);
140                if (mScale.isInProgress()) {
141                    cancelPie();
142                    cancelActivityTouchHandling(m);
143                    return res;
144                }
145            }
146            if (MotionEvent.ACTION_UP == m.getActionMasked()) {
147                cancelPie();
148                cancelActivityTouchHandling(m);
149                // must have been tap
150                if (m.getEventTime() - mDown.getEventTime() < mTapTimeout) {
151                    mModule.onSingleTapUp(null, (int) mDown.getX(), (int) mDown.getY());
152                    return true;
153                } else {
154                    return mActivity.superDispatchTouchEvent(m);
155                }
156            } else if (MotionEvent.ACTION_MOVE == m.getActionMasked()) {
157                if ((Math.abs(m.getX() - mDown.getX()) > mSlop)
158                        || Math.abs(m.getY() - mDown.getY()) > mSlop) {
159                    // moved too far and no timeout yet, no focus or pie
160                    cancelPie();
161                    float dx = m.getX() - mDown.getX();
162                    if (dx < 0
163                            && Math.abs(m.getY() - mDown.getY()) / -dx < 0.6f) {
164                        // within about 37 degrees of x-axis
165                        mMode = MODE_MODULE;
166                        return mActivity.superDispatchTouchEvent(m);
167                    } else {
168                        mMode = MODE_NONE;
169                        cancelActivityTouchHandling(m);
170                    }
171                }
172            }
173            return false;
174        }
175    }
176
177    private boolean checkReceivers(MotionEvent m) {
178        if (mReceivers != null) {
179            for (View receiver : mReceivers) {
180                if (isInside(m, receiver)) {
181                    return true;
182                }
183            }
184        }
185        return false;
186    }
187
188    private boolean isInside(MotionEvent evt, View v) {
189        return (v.getVisibility() == View.VISIBLE
190                && evt.getX() >= v.getLeft() && evt.getX() < v.getRight()
191                && evt.getY() >= v.getTop() && evt.getY() < v.getBottom());
192    }
193
194    public void cancelActivityTouchHandling(MotionEvent m) {
195        MotionEvent c = MotionEvent.obtain(m);
196        c.setAction(MotionEvent.ACTION_CANCEL);
197        mActivity.superDispatchTouchEvent(c);
198    }
199
200    private void openPie() {
201        mDown.offsetLocation(-mOverlay.getWindowPositionX(),
202                -mOverlay.getWindowPositionY());
203        mOverlay.directDispatchTouch(mDown, mPie);
204    }
205
206    private void cancelPie() {
207        mHandler.removeMessages(MSG_PIE);
208    }
209
210    private boolean sendToPie(MotionEvent m) {
211        m.offsetLocation(-mOverlay.getWindowPositionX(),
212                -mOverlay.getWindowPositionY());
213        return mOverlay.directDispatchTouch(m, mPie);
214    }
215
216    @Override
217    public boolean onScale(ScaleGestureDetector detector) {
218        return mZoom.onScale(detector);
219    }
220
221    @Override
222    public boolean onScaleBegin(ScaleGestureDetector detector) {
223        mMode = MODE_ZOOM;
224        return mZoom.onScaleBegin(detector);
225    }
226
227    @Override
228    public void onScaleEnd(ScaleGestureDetector detector) {
229        mZoom.onScaleEnd(detector);
230    }
231}