135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li/*
2d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy * Copyright (C) 2009 The Android Open Source Project
335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li *
435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li * Licensed under the Apache License, Version 2.0 (the "License");
535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li * you may not use this file except in compliance with the License.
635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li * You may obtain a copy of the License at
735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li *
835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li *      http://www.apache.org/licenses/LICENSE-2.0
935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li *
1035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li * Unless required by applicable law or agreed to in writing, software
1135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li * distributed under the License is distributed on an "AS IS" BASIS,
1235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li * See the License for the specific language governing permissions and
1435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li * limitations under the License.
1535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li */
1635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
17db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guypackage android.gesture;
1835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
1935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport android.content.Context;
20d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guyimport android.content.res.TypedArray;
2135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport android.graphics.Canvas;
2235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport android.graphics.Paint;
2335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport android.graphics.Path;
2435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport android.graphics.Rect;
2582f3495b146b267f3786997752cef25310176349Romain Guyimport android.graphics.RectF;
2635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport android.util.AttributeSet;
2735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport android.view.MotionEvent;
28d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guyimport android.view.animation.AnimationUtils;
29d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guyimport android.view.animation.AccelerateDecelerateInterpolator;
3082f3495b146b267f3786997752cef25310176349Romain Guyimport android.widget.FrameLayout;
3182f3495b146b267f3786997752cef25310176349Romain Guyimport android.os.SystemClock;
3251099c5aa63c8349bb7de6d1720480f2c84f404eRomain Guyimport android.annotation.Widget;
33d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guyimport com.android.internal.R;
3435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
3535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport java.util.ArrayList;
3635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
3735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li/**
3882f3495b146b267f3786997752cef25310176349Romain Guy * A transparent overlay for gesture input that can be placed on top of other
3982f3495b146b267f3786997752cef25310176349Romain Guy * widgets or contain other widgets.
40d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy *
4182f3495b146b267f3786997752cef25310176349Romain Guy * @attr ref android.R.styleable#GestureOverlayView_eventsInterceptionEnabled
4282f3495b146b267f3786997752cef25310176349Romain Guy * @attr ref android.R.styleable#GestureOverlayView_fadeDuration
4382f3495b146b267f3786997752cef25310176349Romain Guy * @attr ref android.R.styleable#GestureOverlayView_fadeOffset
44ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy * @attr ref android.R.styleable#GestureOverlayView_fadeEnabled
45d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeWidth
4682f3495b146b267f3786997752cef25310176349Romain Guy * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeAngleThreshold
4782f3495b146b267f3786997752cef25310176349Romain Guy * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeLengthThreshold
4882f3495b146b267f3786997752cef25310176349Romain Guy * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeSquarenessThreshold
498d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeType
50d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy * @attr ref android.R.styleable#GestureOverlayView_gestureColor
518d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy * @attr ref android.R.styleable#GestureOverlayView_orientation
52d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy * @attr ref android.R.styleable#GestureOverlayView_uncertainGestureColor
5335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li */
5451099c5aa63c8349bb7de6d1720480f2c84f404eRomain Guy@Widget
5582f3495b146b267f3786997752cef25310176349Romain Guypublic class GestureOverlayView extends FrameLayout {
5682f3495b146b267f3786997752cef25310176349Romain Guy    public static final int GESTURE_STROKE_TYPE_SINGLE = 0;
5782f3495b146b267f3786997752cef25310176349Romain Guy    public static final int GESTURE_STROKE_TYPE_MULTIPLE = 1;
5882f3495b146b267f3786997752cef25310176349Romain Guy
59e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    public static final int ORIENTATION_HORIZONTAL = 0;
60e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    public static final int ORIENTATION_VERTICAL = 1;
61e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy
6282f3495b146b267f3786997752cef25310176349Romain Guy    private static final int FADE_ANIMATION_RATE = 16;
6335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private static final boolean GESTURE_RENDERING_ANTIALIAS = true;
6435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private static final boolean DITHER_FLAG = true;
6535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
6682f3495b146b267f3786997752cef25310176349Romain Guy    private final Paint mGesturePaint = new Paint();
6735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
6882f3495b146b267f3786997752cef25310176349Romain Guy    private long mFadeDuration = 150;
6982f3495b146b267f3786997752cef25310176349Romain Guy    private long mFadeOffset = 420;
70d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private long mFadingStart;
7182f3495b146b267f3786997752cef25310176349Romain Guy    private boolean mFadingHasStarted;
72ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy    private boolean mFadeEnabled = true;
73db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy
7482f3495b146b267f3786997752cef25310176349Romain Guy    private int mCurrentColor;
75d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private int mCertainGestureColor = 0xFFFFFF00;
7682f3495b146b267f3786997752cef25310176349Romain Guy    private int mUncertainGestureColor = 0x48FFFF00;
7782f3495b146b267f3786997752cef25310176349Romain Guy    private float mGestureStrokeWidth = 12.0f;
78d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private int mInvalidateExtraBorder = 10;
7935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
8082f3495b146b267f3786997752cef25310176349Romain Guy    private int mGestureStrokeType = GESTURE_STROKE_TYPE_SINGLE;
81d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy    private float mGestureStrokeLengthThreshold = 50.0f;
8282f3495b146b267f3786997752cef25310176349Romain Guy    private float mGestureStrokeSquarenessTreshold = 0.275f;
8382f3495b146b267f3786997752cef25310176349Romain Guy    private float mGestureStrokeAngleThreshold = 40.0f;
8482f3495b146b267f3786997752cef25310176349Romain Guy
85e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    private int mOrientation = ORIENTATION_VERTICAL;
86e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy
87d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private final Rect mInvalidRect = new Rect();
88d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private final Path mPath = new Path();
89e705f2c52e683bdad2e845b50df6b6cbed660614Romain Guy    private boolean mGestureVisible = true;
9035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
9135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private float mX;
9235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private float mY;
938d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy
9435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private float mCurveEndX;
9535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private float mCurveEndY;
9635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
9782f3495b146b267f3786997752cef25310176349Romain Guy    private float mTotalLength;
9882f3495b146b267f3786997752cef25310176349Romain Guy    private boolean mIsGesturing = false;
99834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy    private boolean mPreviousWasGesturing = false;
10082f3495b146b267f3786997752cef25310176349Romain Guy    private boolean mInterceptEvents = true;
10182f3495b146b267f3786997752cef25310176349Romain Guy    private boolean mIsListeningForGestures;
10273d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy    private boolean mResetGesture;
10382f3495b146b267f3786997752cef25310176349Romain Guy
10435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    // current gesture
10582f3495b146b267f3786997752cef25310176349Romain Guy    private Gesture mCurrentGesture;
10682f3495b146b267f3786997752cef25310176349Romain Guy    private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
10735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
108c534727972c3835ed997e84a349f259915ef2cddRomain Guy    // TODO: Make this a list of WeakReferences
109d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private final ArrayList<OnGestureListener> mOnGestureListeners =
110d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy            new ArrayList<OnGestureListener>();
11182f3495b146b267f3786997752cef25310176349Romain Guy    // TODO: Make this a list of WeakReferences
11282f3495b146b267f3786997752cef25310176349Romain Guy    private final ArrayList<OnGesturePerformedListener> mOnGesturePerformedListeners =
11382f3495b146b267f3786997752cef25310176349Romain Guy            new ArrayList<OnGesturePerformedListener>();
1149af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    // TODO: Make this a list of WeakReferences
1159af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    private final ArrayList<OnGesturingListener> mOnGesturingListeners =
1169af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy            new ArrayList<OnGesturingListener>();
11782f3495b146b267f3786997752cef25310176349Romain Guy
11882f3495b146b267f3786997752cef25310176349Romain Guy    private boolean mHandleGestureActions;
11935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
12035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    // fading out effect
12135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private boolean mIsFadingOut = false;
12282f3495b146b267f3786997752cef25310176349Romain Guy    private float mFadingAlpha = 1.0f;
123d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private final AccelerateDecelerateInterpolator mInterpolator =
124d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy            new AccelerateDecelerateInterpolator();
12535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
12682f3495b146b267f3786997752cef25310176349Romain Guy    private final FadeOutRunnable mFadingOut = new FadeOutRunnable();
12735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
128db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public GestureOverlayView(Context context) {
12935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        super(context);
13035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        init();
13135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
13235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
133db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public GestureOverlayView(Context context, AttributeSet attrs) {
134d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        this(context, attrs, com.android.internal.R.attr.gestureOverlayViewStyle);
135d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    }
136d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
137d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    public GestureOverlayView(Context context, AttributeSet attrs, int defStyle) {
138d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        super(context, attrs, defStyle);
139d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
140d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        TypedArray a = context.obtainStyledAttributes(attrs,
141d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy                R.styleable.GestureOverlayView, defStyle, 0);
142d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
14382f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeWidth = a.getFloat(R.styleable.GestureOverlayView_gestureStrokeWidth,
14482f3495b146b267f3786997752cef25310176349Romain Guy                mGestureStrokeWidth);
14582f3495b146b267f3786997752cef25310176349Romain Guy        mInvalidateExtraBorder = Math.max(1, ((int) mGestureStrokeWidth) - 1);
146d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        mCertainGestureColor = a.getColor(R.styleable.GestureOverlayView_gestureColor,
147d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy                mCertainGestureColor);
148d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        mUncertainGestureColor = a.getColor(R.styleable.GestureOverlayView_uncertainGestureColor,
149d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy                mUncertainGestureColor);
150d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        mFadeDuration = a.getInt(R.styleable.GestureOverlayView_fadeDuration, (int) mFadeDuration);
151d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        mFadeOffset = a.getInt(R.styleable.GestureOverlayView_fadeOffset, (int) mFadeOffset);
15282f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeType = a.getInt(R.styleable.GestureOverlayView_gestureStrokeType,
15382f3495b146b267f3786997752cef25310176349Romain Guy                mGestureStrokeType);
15482f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeLengthThreshold = a.getFloat(
15582f3495b146b267f3786997752cef25310176349Romain Guy                R.styleable.GestureOverlayView_gestureStrokeLengthThreshold,
15682f3495b146b267f3786997752cef25310176349Romain Guy                mGestureStrokeLengthThreshold);
15782f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeAngleThreshold = a.getFloat(
15882f3495b146b267f3786997752cef25310176349Romain Guy                R.styleable.GestureOverlayView_gestureStrokeAngleThreshold,
15982f3495b146b267f3786997752cef25310176349Romain Guy                mGestureStrokeAngleThreshold);
16082f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeSquarenessTreshold = a.getFloat(
16182f3495b146b267f3786997752cef25310176349Romain Guy                R.styleable.GestureOverlayView_gestureStrokeSquarenessThreshold,
16282f3495b146b267f3786997752cef25310176349Romain Guy                mGestureStrokeSquarenessTreshold);
16382f3495b146b267f3786997752cef25310176349Romain Guy        mInterceptEvents = a.getBoolean(R.styleable.GestureOverlayView_eventsInterceptionEnabled,
16482f3495b146b267f3786997752cef25310176349Romain Guy                mInterceptEvents);
165ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy        mFadeEnabled = a.getBoolean(R.styleable.GestureOverlayView_fadeEnabled,
166ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy                mFadeEnabled);
167e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy        mOrientation = a.getInt(R.styleable.GestureOverlayView_orientation, mOrientation);
168d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
169d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        a.recycle();
170d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
17135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        init();
17235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
17335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
17482f3495b146b267f3786997752cef25310176349Romain Guy    private void init() {
17582f3495b146b267f3786997752cef25310176349Romain Guy        setWillNotDraw(false);
17635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
17782f3495b146b267f3786997752cef25310176349Romain Guy        final Paint gesturePaint = mGesturePaint;
17882f3495b146b267f3786997752cef25310176349Romain Guy        gesturePaint.setAntiAlias(GESTURE_RENDERING_ANTIALIAS);
17982f3495b146b267f3786997752cef25310176349Romain Guy        gesturePaint.setColor(mCertainGestureColor);
18082f3495b146b267f3786997752cef25310176349Romain Guy        gesturePaint.setStyle(Paint.Style.STROKE);
18182f3495b146b267f3786997752cef25310176349Romain Guy        gesturePaint.setStrokeJoin(Paint.Join.ROUND);
18282f3495b146b267f3786997752cef25310176349Romain Guy        gesturePaint.setStrokeCap(Paint.Cap.ROUND);
18382f3495b146b267f3786997752cef25310176349Romain Guy        gesturePaint.setStrokeWidth(mGestureStrokeWidth);
18482f3495b146b267f3786997752cef25310176349Romain Guy        gesturePaint.setDither(DITHER_FLAG);
18582f3495b146b267f3786997752cef25310176349Romain Guy
18682f3495b146b267f3786997752cef25310176349Romain Guy        mCurrentColor = mCertainGestureColor;
18782f3495b146b267f3786997752cef25310176349Romain Guy        setPaintAlpha(255);
18835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
18935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
19082f3495b146b267f3786997752cef25310176349Romain Guy    public ArrayList<GesturePoint> getCurrentStroke() {
19182f3495b146b267f3786997752cef25310176349Romain Guy        return mStrokeBuffer;
19235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
19335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
194e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    public int getOrientation() {
195e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy        return mOrientation;
196e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    }
197e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy
198e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    public void setOrientation(int orientation) {
199e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy        mOrientation = orientation;
200e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    }
201e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy
202db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public void setGestureColor(int color) {
203db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy        mCertainGestureColor = color;
204db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    }
205db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy
206db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public void setUncertainGestureColor(int color) {
207db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy        mUncertainGestureColor = color;
208db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    }
209db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy
210db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public int getUncertainGestureColor() {
211db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy        return mUncertainGestureColor;
212db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    }
213db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy
214db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public int getGestureColor() {
215db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy        return mCertainGestureColor;
216db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    }
217db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy
21882f3495b146b267f3786997752cef25310176349Romain Guy    public float getGestureStrokeWidth() {
21982f3495b146b267f3786997752cef25310176349Romain Guy        return mGestureStrokeWidth;
220d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    }
221d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
22282f3495b146b267f3786997752cef25310176349Romain Guy    public void setGestureStrokeWidth(float gestureStrokeWidth) {
22382f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeWidth = gestureStrokeWidth;
22482f3495b146b267f3786997752cef25310176349Romain Guy        mInvalidateExtraBorder = Math.max(1, ((int) gestureStrokeWidth) - 1);
22582f3495b146b267f3786997752cef25310176349Romain Guy        mGesturePaint.setStrokeWidth(gestureStrokeWidth);
226d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    }
227d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
22882f3495b146b267f3786997752cef25310176349Romain Guy    public int getGestureStrokeType() {
22982f3495b146b267f3786997752cef25310176349Romain Guy        return mGestureStrokeType;
23082f3495b146b267f3786997752cef25310176349Romain Guy    }
23135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
23282f3495b146b267f3786997752cef25310176349Romain Guy    public void setGestureStrokeType(int gestureStrokeType) {
23382f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeType = gestureStrokeType;
23482f3495b146b267f3786997752cef25310176349Romain Guy    }
23535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
23682f3495b146b267f3786997752cef25310176349Romain Guy    public float getGestureStrokeLengthThreshold() {
23782f3495b146b267f3786997752cef25310176349Romain Guy        return mGestureStrokeLengthThreshold;
23835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
23935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
24082f3495b146b267f3786997752cef25310176349Romain Guy    public void setGestureStrokeLengthThreshold(float gestureStrokeLengthThreshold) {
24182f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeLengthThreshold = gestureStrokeLengthThreshold;
24282f3495b146b267f3786997752cef25310176349Romain Guy    }
243c534727972c3835ed997e84a349f259915ef2cddRomain Guy
24482f3495b146b267f3786997752cef25310176349Romain Guy    public float getGestureStrokeSquarenessTreshold() {
24582f3495b146b267f3786997752cef25310176349Romain Guy        return mGestureStrokeSquarenessTreshold;
24635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
24735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
24882f3495b146b267f3786997752cef25310176349Romain Guy    public void setGestureStrokeSquarenessTreshold(float gestureStrokeSquarenessTreshold) {
24982f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeSquarenessTreshold = gestureStrokeSquarenessTreshold;
25082f3495b146b267f3786997752cef25310176349Romain Guy    }
251c534727972c3835ed997e84a349f259915ef2cddRomain Guy
25282f3495b146b267f3786997752cef25310176349Romain Guy    public float getGestureStrokeAngleThreshold() {
25382f3495b146b267f3786997752cef25310176349Romain Guy        return mGestureStrokeAngleThreshold;
25482f3495b146b267f3786997752cef25310176349Romain Guy    }
255c534727972c3835ed997e84a349f259915ef2cddRomain Guy
25682f3495b146b267f3786997752cef25310176349Romain Guy    public void setGestureStrokeAngleThreshold(float gestureStrokeAngleThreshold) {
25782f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeAngleThreshold = gestureStrokeAngleThreshold;
25882f3495b146b267f3786997752cef25310176349Romain Guy    }
259c534727972c3835ed997e84a349f259915ef2cddRomain Guy
26082f3495b146b267f3786997752cef25310176349Romain Guy    public boolean isEventsInterceptionEnabled() {
26182f3495b146b267f3786997752cef25310176349Romain Guy        return mInterceptEvents;
26282f3495b146b267f3786997752cef25310176349Romain Guy    }
263c534727972c3835ed997e84a349f259915ef2cddRomain Guy
26482f3495b146b267f3786997752cef25310176349Romain Guy    public void setEventsInterceptionEnabled(boolean enabled) {
26582f3495b146b267f3786997752cef25310176349Romain Guy        mInterceptEvents = enabled;
26682f3495b146b267f3786997752cef25310176349Romain Guy    }
26782f3495b146b267f3786997752cef25310176349Romain Guy
268ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy    public boolean isFadeEnabled() {
269ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy        return mFadeEnabled;
270ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy    }
271ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy
272ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy    public void setFadeEnabled(boolean fadeEnabled) {
273ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy        mFadeEnabled = fadeEnabled;
274ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy    }
275ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy
27682f3495b146b267f3786997752cef25310176349Romain Guy    public Gesture getGesture() {
27782f3495b146b267f3786997752cef25310176349Romain Guy        return mCurrentGesture;
27882f3495b146b267f3786997752cef25310176349Romain Guy    }
279c534727972c3835ed997e84a349f259915ef2cddRomain Guy
28082f3495b146b267f3786997752cef25310176349Romain Guy    public void setGesture(Gesture gesture) {
28135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        if (mCurrentGesture != null) {
28282f3495b146b267f3786997752cef25310176349Romain Guy            clear(false);
28335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
28482f3495b146b267f3786997752cef25310176349Romain Guy
28582f3495b146b267f3786997752cef25310176349Romain Guy        setCurrentColor(mCertainGestureColor);
28682f3495b146b267f3786997752cef25310176349Romain Guy        mCurrentGesture = gesture;
28782f3495b146b267f3786997752cef25310176349Romain Guy
28882f3495b146b267f3786997752cef25310176349Romain Guy        final Path path = mCurrentGesture.toPath();
28982f3495b146b267f3786997752cef25310176349Romain Guy        final RectF bounds = new RectF();
29082f3495b146b267f3786997752cef25310176349Romain Guy        path.computeBounds(bounds, true);
29182f3495b146b267f3786997752cef25310176349Romain Guy
292cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy        // TODO: The path should also be scaled to fit inside this view
29382f3495b146b267f3786997752cef25310176349Romain Guy        mPath.rewind();
29403f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy        mPath.addPath(path, -bounds.left + (getWidth() - bounds.width()) / 2.0f,
29503f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy                -bounds.top + (getHeight() - bounds.height()) / 2.0f);
29603f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy
29703f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy        mResetGesture = true;
29882f3495b146b267f3786997752cef25310176349Romain Guy
29982f3495b146b267f3786997752cef25310176349Romain Guy        invalidate();
30035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
30135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
302b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    public Path getGesturePath() {
303b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        return mPath;
304b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    }
305b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy
306b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    public Path getGesturePath(Path path) {
307b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        path.set(mPath);
308b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        return path;
309b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    }
310b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy
311b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    public boolean isGestureVisible() {
312b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        return mGestureVisible;
313b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    }
314b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy
315b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    public void setGestureVisible(boolean visible) {
316b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        mGestureVisible = visible;
317b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    }
318b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy
319b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    public long getFadeOffset() {
320b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        return mFadeOffset;
321b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    }
322b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy
323b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    public void setFadeOffset(long fadeOffset) {
324b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        mFadeOffset = fadeOffset;
325b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    }
326b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy
327db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public void addOnGestureListener(OnGestureListener listener) {
328db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy        mOnGestureListeners.add(listener);
32935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
33035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
331db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public void removeOnGestureListener(OnGestureListener listener) {
332db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy        mOnGestureListeners.remove(listener);
33335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
33435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
335d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    public void removeAllOnGestureListeners() {
336d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        mOnGestureListeners.clear();
337d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    }
338d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
33982f3495b146b267f3786997752cef25310176349Romain Guy    public void addOnGesturePerformedListener(OnGesturePerformedListener listener) {
34082f3495b146b267f3786997752cef25310176349Romain Guy        mOnGesturePerformedListeners.add(listener);
34182f3495b146b267f3786997752cef25310176349Romain Guy        if (mOnGesturePerformedListeners.size() > 0) {
34282f3495b146b267f3786997752cef25310176349Romain Guy            mHandleGestureActions = true;
34382f3495b146b267f3786997752cef25310176349Romain Guy        }
34482f3495b146b267f3786997752cef25310176349Romain Guy    }
34582f3495b146b267f3786997752cef25310176349Romain Guy
34682f3495b146b267f3786997752cef25310176349Romain Guy    public void removeOnGesturePerformedListener(OnGesturePerformedListener listener) {
34782f3495b146b267f3786997752cef25310176349Romain Guy        mOnGesturePerformedListeners.remove(listener);
34882f3495b146b267f3786997752cef25310176349Romain Guy        if (mOnGesturePerformedListeners.size() <= 0) {
34982f3495b146b267f3786997752cef25310176349Romain Guy            mHandleGestureActions = false;
35082f3495b146b267f3786997752cef25310176349Romain Guy        }
35182f3495b146b267f3786997752cef25310176349Romain Guy    }
35282f3495b146b267f3786997752cef25310176349Romain Guy
35382f3495b146b267f3786997752cef25310176349Romain Guy    public void removeAllOnGesturePerformedListeners() {
35482f3495b146b267f3786997752cef25310176349Romain Guy        mOnGesturePerformedListeners.clear();
35582f3495b146b267f3786997752cef25310176349Romain Guy        mHandleGestureActions = false;
35682f3495b146b267f3786997752cef25310176349Romain Guy    }
35735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
3589af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    public void addOnGesturingListener(OnGesturingListener listener) {
3599af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        mOnGesturingListeners.add(listener);
3609af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    }
3619af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
3629af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    public void removeOnGesturingListener(OnGesturingListener listener) {
3639af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        mOnGesturingListeners.remove(listener);
3649af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    }
3659af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
3669af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    public void removeAllOnGesturingListeners() {
3679af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        mOnGesturingListeners.clear();
3689af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    }
3699af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
37082f3495b146b267f3786997752cef25310176349Romain Guy    public boolean isGesturing() {
37182f3495b146b267f3786997752cef25310176349Romain Guy        return mIsGesturing;
37282f3495b146b267f3786997752cef25310176349Romain Guy    }
37382f3495b146b267f3786997752cef25310176349Romain Guy
37482f3495b146b267f3786997752cef25310176349Romain Guy    private void setCurrentColor(int color) {
37582f3495b146b267f3786997752cef25310176349Romain Guy        mCurrentColor = color;
37682f3495b146b267f3786997752cef25310176349Romain Guy        if (mFadingHasStarted) {
37782f3495b146b267f3786997752cef25310176349Romain Guy            setPaintAlpha((int) (255 * mFadingAlpha));
37835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        } else {
37982f3495b146b267f3786997752cef25310176349Romain Guy            setPaintAlpha(255);
38082f3495b146b267f3786997752cef25310176349Romain Guy        }
38182f3495b146b267f3786997752cef25310176349Romain Guy        invalidate();
38282f3495b146b267f3786997752cef25310176349Romain Guy    }
38382f3495b146b267f3786997752cef25310176349Romain Guy
38427a2b50dedd4315b921d502a30ac0fda51fb6a7cRomain Guy    /**
38527a2b50dedd4315b921d502a30ac0fda51fb6a7cRomain Guy     * @hide
38627a2b50dedd4315b921d502a30ac0fda51fb6a7cRomain Guy     */
38727a2b50dedd4315b921d502a30ac0fda51fb6a7cRomain Guy    public Paint getGesturePaint() {
38827a2b50dedd4315b921d502a30ac0fda51fb6a7cRomain Guy        return mGesturePaint;
38927a2b50dedd4315b921d502a30ac0fda51fb6a7cRomain Guy    }
39027a2b50dedd4315b921d502a30ac0fda51fb6a7cRomain Guy
39182f3495b146b267f3786997752cef25310176349Romain Guy    @Override
39282f3495b146b267f3786997752cef25310176349Romain Guy    public void draw(Canvas canvas) {
39382f3495b146b267f3786997752cef25310176349Romain Guy        super.draw(canvas);
39482f3495b146b267f3786997752cef25310176349Romain Guy
395b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        if (mCurrentGesture != null && mGestureVisible) {
39682f3495b146b267f3786997752cef25310176349Romain Guy            canvas.drawPath(mPath, mGesturePaint);
39735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
39882f3495b146b267f3786997752cef25310176349Romain Guy    }
39982f3495b146b267f3786997752cef25310176349Romain Guy
40082f3495b146b267f3786997752cef25310176349Romain Guy    private void setPaintAlpha(int alpha) {
40182f3495b146b267f3786997752cef25310176349Romain Guy        alpha += alpha >> 7;
40282f3495b146b267f3786997752cef25310176349Romain Guy        final int baseAlpha = mCurrentColor >>> 24;
40382f3495b146b267f3786997752cef25310176349Romain Guy        final int useAlpha = baseAlpha * alpha >> 8;
40482f3495b146b267f3786997752cef25310176349Romain Guy        mGesturePaint.setColor((mCurrentColor << 8 >>> 8) | (useAlpha << 24));
40582f3495b146b267f3786997752cef25310176349Romain Guy    }
40635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
40782f3495b146b267f3786997752cef25310176349Romain Guy    public void clear(boolean animated) {
40803f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy        clear(animated, false, true);
40935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
41035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
41103f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy    private void clear(boolean animated, boolean fireActionPerformed, boolean immediate) {
41282f3495b146b267f3786997752cef25310176349Romain Guy        setPaintAlpha(255);
4139edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy        removeCallbacks(mFadingOut);
41473d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy        mResetGesture = false;
4159edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy        mFadingOut.fireActionPerformed = fireActionPerformed;
41673d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy        mFadingOut.resetMultipleStrokes = false;
4179edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy
41882f3495b146b267f3786997752cef25310176349Romain Guy        if (animated && mCurrentGesture != null) {
4199edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            mFadingAlpha = 1.0f;
4209edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            mIsFadingOut = true;
4219edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            mFadingHasStarted = false;
4229edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            mFadingStart = AnimationUtils.currentAnimationTimeMillis() + mFadeOffset;
4239edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy
424d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy            postDelayed(mFadingOut, mFadeOffset);
42535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        } else {
4269edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            mFadingAlpha = 1.0f;
4279edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            mIsFadingOut = false;
4289edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            mFadingHasStarted = false;
4299edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy
43003f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy            if (immediate) {
4319edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy                mCurrentGesture = null;
4329edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy                mPath.rewind();
4339edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy                invalidate();
43403f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy            } else if (fireActionPerformed) {
43503f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy                postDelayed(mFadingOut, mFadeOffset);
43673d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy            } else if (mGestureStrokeType == GESTURE_STROKE_TYPE_MULTIPLE) {
43773d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy                mFadingOut.resetMultipleStrokes = true;
43873d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy                postDelayed(mFadingOut, mFadeOffset);
43903f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy            } else {
44003f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy                mCurrentGesture = null;
44103f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy                mPath.rewind();
44203f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy                invalidate();
4439edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            }
44435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
44535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
44635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
44782f3495b146b267f3786997752cef25310176349Romain Guy    public void cancelClearAnimation() {
44882f3495b146b267f3786997752cef25310176349Romain Guy        setPaintAlpha(255);
44935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        mIsFadingOut = false;
45082f3495b146b267f3786997752cef25310176349Romain Guy        mFadingHasStarted = false;
451d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        removeCallbacks(mFadingOut);
45282f3495b146b267f3786997752cef25310176349Romain Guy        mPath.rewind();
45382f3495b146b267f3786997752cef25310176349Romain Guy        mCurrentGesture = null;
45482f3495b146b267f3786997752cef25310176349Romain Guy    }
45582f3495b146b267f3786997752cef25310176349Romain Guy
45682f3495b146b267f3786997752cef25310176349Romain Guy    public void cancelGesture() {
45782f3495b146b267f3786997752cef25310176349Romain Guy        mIsListeningForGestures = false;
45882f3495b146b267f3786997752cef25310176349Romain Guy
45982f3495b146b267f3786997752cef25310176349Romain Guy        // add the stroke to the current gesture
46082f3495b146b267f3786997752cef25310176349Romain Guy        mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer));
46182f3495b146b267f3786997752cef25310176349Romain Guy
46282f3495b146b267f3786997752cef25310176349Romain Guy        // pass the event to handlers
46382f3495b146b267f3786997752cef25310176349Romain Guy        final long now = SystemClock.uptimeMillis();
46482f3495b146b267f3786997752cef25310176349Romain Guy        final MotionEvent event = MotionEvent.obtain(now, now,
46582f3495b146b267f3786997752cef25310176349Romain Guy                MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
46682f3495b146b267f3786997752cef25310176349Romain Guy
46782f3495b146b267f3786997752cef25310176349Romain Guy        final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
4689af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        int count = listeners.size();
46982f3495b146b267f3786997752cef25310176349Romain Guy        for (int i = 0; i < count; i++) {
47082f3495b146b267f3786997752cef25310176349Romain Guy            listeners.get(i).onGestureCancelled(this, event);
47182f3495b146b267f3786997752cef25310176349Romain Guy        }
47282f3495b146b267f3786997752cef25310176349Romain Guy
47382f3495b146b267f3786997752cef25310176349Romain Guy        event.recycle();
47482f3495b146b267f3786997752cef25310176349Romain Guy
47582f3495b146b267f3786997752cef25310176349Romain Guy        clear(false);
47682f3495b146b267f3786997752cef25310176349Romain Guy        mIsGesturing = false;
477834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy        mPreviousWasGesturing = false;
47882f3495b146b267f3786997752cef25310176349Romain Guy        mStrokeBuffer.clear();
4799af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
4809af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        final ArrayList<OnGesturingListener> otherListeners = mOnGesturingListeners;
4819af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        count = otherListeners.size();
4829af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        for (int i = 0; i < count; i++) {
4839af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy            otherListeners.get(i).onGesturingEnded(this);
4849af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        }
48535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
48635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
48735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    @Override
48882f3495b146b267f3786997752cef25310176349Romain Guy    protected void onDetachedFromWindow() {
48982f3495b146b267f3786997752cef25310176349Romain Guy        cancelClearAnimation();
49082f3495b146b267f3786997752cef25310176349Romain Guy    }
49182f3495b146b267f3786997752cef25310176349Romain Guy
49282f3495b146b267f3786997752cef25310176349Romain Guy    @Override
49382f3495b146b267f3786997752cef25310176349Romain Guy    public boolean dispatchTouchEvent(MotionEvent event) {
49482f3495b146b267f3786997752cef25310176349Romain Guy        if (isEnabled()) {
495834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy            final boolean cancelDispatch = (mIsGesturing || (mCurrentGesture != null &&
496834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy                    mCurrentGesture.getStrokesCount() > 0 && mPreviousWasGesturing)) &&
497834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy                    mInterceptEvents;
498834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy
49982f3495b146b267f3786997752cef25310176349Romain Guy            processEvent(event);
50082f3495b146b267f3786997752cef25310176349Romain Guy
50182f3495b146b267f3786997752cef25310176349Romain Guy            if (cancelDispatch) {
50282f3495b146b267f3786997752cef25310176349Romain Guy                event.setAction(MotionEvent.ACTION_CANCEL);
50382f3495b146b267f3786997752cef25310176349Romain Guy            }
50482f3495b146b267f3786997752cef25310176349Romain Guy
50582f3495b146b267f3786997752cef25310176349Romain Guy            super.dispatchTouchEvent(event);
506834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy
50735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            return true;
50835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
50935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
51082f3495b146b267f3786997752cef25310176349Romain Guy        return super.dispatchTouchEvent(event);
511d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    }
512d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
51382f3495b146b267f3786997752cef25310176349Romain Guy    private boolean processEvent(MotionEvent event) {
51435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        switch (event.getAction()) {
51535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            case MotionEvent.ACTION_DOWN:
516e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy                touchDown(event);
51782f3495b146b267f3786997752cef25310176349Romain Guy                invalidate();
51882f3495b146b267f3786997752cef25310176349Romain Guy                return true;
51935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            case MotionEvent.ACTION_MOVE:
52082f3495b146b267f3786997752cef25310176349Romain Guy                if (mIsListeningForGestures) {
52182f3495b146b267f3786997752cef25310176349Romain Guy                    Rect rect = touchMove(event);
52282f3495b146b267f3786997752cef25310176349Romain Guy                    if (rect != null) {
52382f3495b146b267f3786997752cef25310176349Romain Guy                        invalidate(rect);
52482f3495b146b267f3786997752cef25310176349Romain Guy                    }
52582f3495b146b267f3786997752cef25310176349Romain Guy                    return true;
52635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li                }
52735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li                break;
52835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            case MotionEvent.ACTION_UP:
52982f3495b146b267f3786997752cef25310176349Romain Guy                if (mIsListeningForGestures) {
53082f3495b146b267f3786997752cef25310176349Romain Guy                    touchUp(event, false);
53182f3495b146b267f3786997752cef25310176349Romain Guy                    invalidate();
53282f3495b146b267f3786997752cef25310176349Romain Guy                    return true;
53382f3495b146b267f3786997752cef25310176349Romain Guy                }
534d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy                break;
535d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy            case MotionEvent.ACTION_CANCEL:
53682f3495b146b267f3786997752cef25310176349Romain Guy                if (mIsListeningForGestures) {
53782f3495b146b267f3786997752cef25310176349Romain Guy                    touchUp(event, true);
53882f3495b146b267f3786997752cef25310176349Romain Guy                    invalidate();
53982f3495b146b267f3786997752cef25310176349Romain Guy                    return true;
54082f3495b146b267f3786997752cef25310176349Romain Guy                }
54135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
54282f3495b146b267f3786997752cef25310176349Romain Guy
54382f3495b146b267f3786997752cef25310176349Romain Guy        return false;
54435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
54535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
546e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    private void touchDown(MotionEvent event) {
54782f3495b146b267f3786997752cef25310176349Romain Guy        mIsListeningForGestures = true;
54882f3495b146b267f3786997752cef25310176349Romain Guy
54935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        float x = event.getX();
55035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        float y = event.getY();
55135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
55235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        mX = x;
55335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        mY = y;
55435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
55582f3495b146b267f3786997752cef25310176349Romain Guy        mTotalLength = 0;
55682f3495b146b267f3786997752cef25310176349Romain Guy        mIsGesturing = false;
55782f3495b146b267f3786997752cef25310176349Romain Guy
55873d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy        if (mGestureStrokeType == GESTURE_STROKE_TYPE_SINGLE || mResetGesture) {
55982f3495b146b267f3786997752cef25310176349Romain Guy            if (mHandleGestureActions) setCurrentColor(mUncertainGestureColor);
56073d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy            mResetGesture = false;
56182f3495b146b267f3786997752cef25310176349Romain Guy            mCurrentGesture = null;
56282f3495b146b267f3786997752cef25310176349Romain Guy            mPath.rewind();
56382f3495b146b267f3786997752cef25310176349Romain Guy        } else if (mCurrentGesture == null || mCurrentGesture.getStrokesCount() == 0) {
56482f3495b146b267f3786997752cef25310176349Romain Guy            if (mHandleGestureActions) setCurrentColor(mUncertainGestureColor);
56582f3495b146b267f3786997752cef25310176349Romain Guy        }
56682f3495b146b267f3786997752cef25310176349Romain Guy
56782f3495b146b267f3786997752cef25310176349Romain Guy        // if there is fading out going on, stop it.
56882f3495b146b267f3786997752cef25310176349Romain Guy        if (mFadingHasStarted) {
56982f3495b146b267f3786997752cef25310176349Romain Guy            cancelClearAnimation();
57082f3495b146b267f3786997752cef25310176349Romain Guy        } else if (mIsFadingOut) {
57182f3495b146b267f3786997752cef25310176349Romain Guy            setPaintAlpha(255);
57282f3495b146b267f3786997752cef25310176349Romain Guy            mIsFadingOut = false;
57382f3495b146b267f3786997752cef25310176349Romain Guy            mFadingHasStarted = false;
57482f3495b146b267f3786997752cef25310176349Romain Guy            removeCallbacks(mFadingOut);
57582f3495b146b267f3786997752cef25310176349Romain Guy        }
57682f3495b146b267f3786997752cef25310176349Romain Guy
57735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        if (mCurrentGesture == null) {
57835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            mCurrentGesture = new Gesture();
57935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
58035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
58182f3495b146b267f3786997752cef25310176349Romain Guy        mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
58235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        mPath.moveTo(x, y);
58335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
58482f3495b146b267f3786997752cef25310176349Romain Guy        final int border = mInvalidateExtraBorder;
58582f3495b146b267f3786997752cef25310176349Romain Guy        mInvalidRect.set((int) x - border, (int) y - border, (int) x + border, (int) y + border);
58682f3495b146b267f3786997752cef25310176349Romain Guy
58735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        mCurveEndX = x;
58835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        mCurveEndY = y;
589ff686ce11e952a9001b1384d8e7d82127b0529a2Romain Guy
590ff686ce11e952a9001b1384d8e7d82127b0529a2Romain Guy        // pass the event to handlers
591ff686ce11e952a9001b1384d8e7d82127b0529a2Romain Guy        final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
592ff686ce11e952a9001b1384d8e7d82127b0529a2Romain Guy        final int count = listeners.size();
593ff686ce11e952a9001b1384d8e7d82127b0529a2Romain Guy        for (int i = 0; i < count; i++) {
594ff686ce11e952a9001b1384d8e7d82127b0529a2Romain Guy            listeners.get(i).onGestureStarted(this, event);
5958d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy        }
59635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
59735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
59835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private Rect touchMove(MotionEvent event) {
59935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        Rect areaToRefresh = null;
6008d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy
601d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        final float x = event.getX();
602d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        final float y = event.getY();
603d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
604d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        final float previousX = mX;
605d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        final float previousY = mY;
60635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
607d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        final float dx = Math.abs(x - previousX);
608d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        final float dy = Math.abs(y - previousY);
6098d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy
610d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        if (dx >= GestureStroke.TOUCH_TOLERANCE || dy >= GestureStroke.TOUCH_TOLERANCE) {
611d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy            areaToRefresh = mInvalidRect;
612d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
61335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            // start with the curve end
61482f3495b146b267f3786997752cef25310176349Romain Guy            final int border = mInvalidateExtraBorder;
61582f3495b146b267f3786997752cef25310176349Romain Guy            areaToRefresh.set((int) mCurveEndX - border, (int) mCurveEndY - border,
61682f3495b146b267f3786997752cef25310176349Romain Guy                    (int) mCurveEndX + border, (int) mCurveEndY + border);
6178d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy
61882f3495b146b267f3786997752cef25310176349Romain Guy            float cX = mCurveEndX = (x + previousX) / 2;
61982f3495b146b267f3786997752cef25310176349Romain Guy            float cY = mCurveEndY = (y + previousY) / 2;
620d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
62182f3495b146b267f3786997752cef25310176349Romain Guy            mPath.quadTo(previousX, previousY, cX, cY);
6228d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy
62335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            // union with the control point of the new curve
62482f3495b146b267f3786997752cef25310176349Romain Guy            areaToRefresh.union((int) previousX - border, (int) previousY - border,
62582f3495b146b267f3786997752cef25310176349Romain Guy                    (int) previousX + border, (int) previousY + border);
6268d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy
62735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            // union with the end point of the new curve
62882f3495b146b267f3786997752cef25310176349Romain Guy            areaToRefresh.union((int) cX - border, (int) cY - border,
62982f3495b146b267f3786997752cef25310176349Romain Guy                    (int) cX + border, (int) cY + border);
63035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
63135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            mX = x;
63235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            mY = y;
63335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
634d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
63535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
636d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            if (mHandleGestureActions && !mIsGesturing) {
637d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                mTotalLength += (float) Math.sqrt(dx * dx + dy * dy);
63882f3495b146b267f3786997752cef25310176349Romain Guy
639d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                if (mTotalLength > mGestureStrokeLengthThreshold) {
640d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                    final OrientedBoundingBox box =
64146c53129c6f27c9193ab195a69cb50591b8c1fa2Romain Guy                            GestureUtils.computeOrientedBoundingBox(mStrokeBuffer);
64282f3495b146b267f3786997752cef25310176349Romain Guy
643d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                    float angle = Math.abs(box.orientation);
644d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                    if (angle > 90) {
645d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                        angle = 180 - angle;
646d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                    }
64782f3495b146b267f3786997752cef25310176349Romain Guy
648d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                    if (box.squareness > mGestureStrokeSquarenessTreshold ||
649d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                            (mOrientation == ORIENTATION_VERTICAL ?
650d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                                    angle < mGestureStrokeAngleThreshold :
651d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                                    angle > mGestureStrokeAngleThreshold)) {
65282f3495b146b267f3786997752cef25310176349Romain Guy
653d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                        mIsGesturing = true;
654d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                        setCurrentColor(mCertainGestureColor);
6559af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
6569af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy                        final ArrayList<OnGesturingListener> listeners = mOnGesturingListeners;
6579af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy                        int count = listeners.size();
6589af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy                        for (int i = 0; i < count; i++) {
6599af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy                            listeners.get(i).onGesturingStarted(this);
6609af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy                        }
661d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                    }
66282f3495b146b267f3786997752cef25310176349Romain Guy                }
66382f3495b146b267f3786997752cef25310176349Romain Guy            }
66482f3495b146b267f3786997752cef25310176349Romain Guy
665d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            // pass the event to handlers
666d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
667d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            final int count = listeners.size();
668d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            for (int i = 0; i < count; i++) {
669d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                listeners.get(i).onGesture(this, event);
670d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            }
671d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        }
672ff686ce11e952a9001b1384d8e7d82127b0529a2Romain Guy
67335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        return areaToRefresh;
67435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
67535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
676d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private void touchUp(MotionEvent event, boolean cancel) {
67782f3495b146b267f3786997752cef25310176349Romain Guy        mIsListeningForGestures = false;
67835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
679d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        // A gesture wasn't started or was cancelled
680d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        if (mCurrentGesture != null) {
681d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            // add the stroke to the current gesture
682d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer));
683d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy
684d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            if (!cancel) {
685d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                // pass the event to handlers
686d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
687d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                int count = listeners.size();
688d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                for (int i = 0; i < count; i++) {
689d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                    listeners.get(i).onGestureEnded(this, event);
690d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                }
691d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
69203f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy                clear(mHandleGestureActions && mFadeEnabled, mHandleGestureActions && mIsGesturing,
69303f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy                        false);
694d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            } else {
695d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                cancelGesture(event);
69682f3495b146b267f3786997752cef25310176349Romain Guy
69782f3495b146b267f3786997752cef25310176349Romain Guy            }
698d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        } else {
699d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            cancelGesture(event);
70035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
701d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
7028d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy        mStrokeBuffer.clear();
703834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy        mPreviousWasGesturing = mIsGesturing;
70482f3495b146b267f3786997752cef25310176349Romain Guy        mIsGesturing = false;
7059af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
7069af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        final ArrayList<OnGesturingListener> listeners = mOnGesturingListeners;
7079af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        int count = listeners.size();
7089af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        for (int i = 0; i < count; i++) {
7099af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy            listeners.get(i).onGesturingEnded(this);
7109af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        }
71182f3495b146b267f3786997752cef25310176349Romain Guy    }
71282f3495b146b267f3786997752cef25310176349Romain Guy
713d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy    private void cancelGesture(MotionEvent event) {
714d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        // pass the event to handlers
715d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
716d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        final int count = listeners.size();
717d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        for (int i = 0; i < count; i++) {
718d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            listeners.get(i).onGestureCancelled(this, event);
719d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        }
720d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy
721d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        clear(false);
722d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy    }
723d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy
724ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy    private void fireOnGesturePerformed() {
7259af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        final ArrayList<OnGesturePerformedListener> actionListeners = mOnGesturePerformedListeners;
726ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy        final int count = actionListeners.size();
727ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy        for (int i = 0; i < count; i++) {
7289af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy            actionListeners.get(i).onGesturePerformed(GestureOverlayView.this, mCurrentGesture);
729ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy        }
730e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    }
731ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy
73282f3495b146b267f3786997752cef25310176349Romain Guy    private class FadeOutRunnable implements Runnable {
73382f3495b146b267f3786997752cef25310176349Romain Guy        boolean fireActionPerformed;
73473d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy        boolean resetMultipleStrokes;
73582f3495b146b267f3786997752cef25310176349Romain Guy
73682f3495b146b267f3786997752cef25310176349Romain Guy        public void run() {
73782f3495b146b267f3786997752cef25310176349Romain Guy            if (mIsFadingOut) {
73882f3495b146b267f3786997752cef25310176349Romain Guy                final long now = AnimationUtils.currentAnimationTimeMillis();
73982f3495b146b267f3786997752cef25310176349Romain Guy                final long duration = now - mFadingStart;
74082f3495b146b267f3786997752cef25310176349Romain Guy
74182f3495b146b267f3786997752cef25310176349Romain Guy                if (duration > mFadeDuration) {
74282f3495b146b267f3786997752cef25310176349Romain Guy                    if (fireActionPerformed) {
743ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy                        fireOnGesturePerformed();
74482f3495b146b267f3786997752cef25310176349Romain Guy                    }
74582f3495b146b267f3786997752cef25310176349Romain Guy
746834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy                    mPreviousWasGesturing = false;
74782f3495b146b267f3786997752cef25310176349Romain Guy                    mIsFadingOut = false;
74882f3495b146b267f3786997752cef25310176349Romain Guy                    mFadingHasStarted = false;
74982f3495b146b267f3786997752cef25310176349Romain Guy                    mPath.rewind();
75082f3495b146b267f3786997752cef25310176349Romain Guy                    mCurrentGesture = null;
75182f3495b146b267f3786997752cef25310176349Romain Guy                    setPaintAlpha(255);
75282f3495b146b267f3786997752cef25310176349Romain Guy                } else {
75382f3495b146b267f3786997752cef25310176349Romain Guy                    mFadingHasStarted = true;
75482f3495b146b267f3786997752cef25310176349Romain Guy                    float interpolatedTime = Math.max(0.0f,
75582f3495b146b267f3786997752cef25310176349Romain Guy                            Math.min(1.0f, duration / (float) mFadeDuration));
75682f3495b146b267f3786997752cef25310176349Romain Guy                    mFadingAlpha = 1.0f - mInterpolator.getInterpolation(interpolatedTime);
75782f3495b146b267f3786997752cef25310176349Romain Guy                    setPaintAlpha((int) (255 * mFadingAlpha));
75882f3495b146b267f3786997752cef25310176349Romain Guy                    postDelayed(this, FADE_ANIMATION_RATE);
75982f3495b146b267f3786997752cef25310176349Romain Guy                }
76073d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy            } else if (resetMultipleStrokes) {
76173d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy                mResetGesture = true;
7629edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            } else {
763ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy                fireOnGesturePerformed();
764ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy
765ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy                mFadingHasStarted = false;
766ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy                mPath.rewind();
767ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy                mCurrentGesture = null;
768834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy                mPreviousWasGesturing = false;
769ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy                setPaintAlpha(255);
77082f3495b146b267f3786997752cef25310176349Romain Guy            }
7719edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy
7728d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy            invalidate();
77382f3495b146b267f3786997752cef25310176349Romain Guy        }
77435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
77535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
7769af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    public static interface OnGesturingListener {
7779af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        void onGesturingStarted(GestureOverlayView overlay);
7789af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
7799af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        void onGesturingEnded(GestureOverlayView overlay);
7809af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    }
7819af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
782db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public static interface OnGestureListener {
783d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        void onGestureStarted(GestureOverlayView overlay, MotionEvent event);
784d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
785d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        void onGesture(GestureOverlayView overlay, MotionEvent event);
786db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy
787d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        void onGestureEnded(GestureOverlayView overlay, MotionEvent event);
788db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy
789d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        void onGestureCancelled(GestureOverlayView overlay, MotionEvent event);
790db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    }
79182f3495b146b267f3786997752cef25310176349Romain Guy
79282f3495b146b267f3786997752cef25310176349Romain Guy    public static interface OnGesturePerformedListener {
79382f3495b146b267f3786997752cef25310176349Romain Guy        void onGesturePerformed(GestureOverlayView overlay, Gesture gesture);
79482f3495b146b267f3786997752cef25310176349Romain Guy    }
79535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li}
796