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
1980756e38882720860db52f1fcc21fa1505a02abfTor Norbyeimport android.annotation.ColorInt;
2035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport android.content.Context;
21d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guyimport android.content.res.TypedArray;
2235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport android.graphics.Canvas;
2335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport android.graphics.Paint;
2435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport android.graphics.Path;
2535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport android.graphics.Rect;
2682f3495b146b267f3786997752cef25310176349Romain Guyimport android.graphics.RectF;
2735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport android.util.AttributeSet;
2835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport android.view.MotionEvent;
29d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guyimport android.view.animation.AnimationUtils;
30d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guyimport android.view.animation.AccelerateDecelerateInterpolator;
3182f3495b146b267f3786997752cef25310176349Romain Guyimport android.widget.FrameLayout;
3282f3495b146b267f3786997752cef25310176349Romain Guyimport android.os.SystemClock;
3351099c5aa63c8349bb7de6d1720480f2c84f404eRomain Guyimport android.annotation.Widget;
34d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guyimport com.android.internal.R;
3535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
3635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Liimport java.util.ArrayList;
3735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
3835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li/**
3982f3495b146b267f3786997752cef25310176349Romain Guy * A transparent overlay for gesture input that can be placed on top of other
4082f3495b146b267f3786997752cef25310176349Romain Guy * widgets or contain other widgets.
41d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy *
4282f3495b146b267f3786997752cef25310176349Romain Guy * @attr ref android.R.styleable#GestureOverlayView_eventsInterceptionEnabled
4382f3495b146b267f3786997752cef25310176349Romain Guy * @attr ref android.R.styleable#GestureOverlayView_fadeDuration
4482f3495b146b267f3786997752cef25310176349Romain Guy * @attr ref android.R.styleable#GestureOverlayView_fadeOffset
45ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy * @attr ref android.R.styleable#GestureOverlayView_fadeEnabled
46d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeWidth
4782f3495b146b267f3786997752cef25310176349Romain Guy * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeAngleThreshold
4882f3495b146b267f3786997752cef25310176349Romain Guy * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeLengthThreshold
4982f3495b146b267f3786997752cef25310176349Romain Guy * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeSquarenessThreshold
508d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy * @attr ref android.R.styleable#GestureOverlayView_gestureStrokeType
51d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy * @attr ref android.R.styleable#GestureOverlayView_gestureColor
528d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy * @attr ref android.R.styleable#GestureOverlayView_orientation
53d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy * @attr ref android.R.styleable#GestureOverlayView_uncertainGestureColor
5435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li */
5551099c5aa63c8349bb7de6d1720480f2c84f404eRomain Guy@Widget
5682f3495b146b267f3786997752cef25310176349Romain Guypublic class GestureOverlayView extends FrameLayout {
5782f3495b146b267f3786997752cef25310176349Romain Guy    public static final int GESTURE_STROKE_TYPE_SINGLE = 0;
5882f3495b146b267f3786997752cef25310176349Romain Guy    public static final int GESTURE_STROKE_TYPE_MULTIPLE = 1;
5982f3495b146b267f3786997752cef25310176349Romain Guy
60e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    public static final int ORIENTATION_HORIZONTAL = 0;
61e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    public static final int ORIENTATION_VERTICAL = 1;
62e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy
6382f3495b146b267f3786997752cef25310176349Romain Guy    private static final int FADE_ANIMATION_RATE = 16;
6435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private static final boolean GESTURE_RENDERING_ANTIALIAS = true;
6535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private static final boolean DITHER_FLAG = true;
6635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
6782f3495b146b267f3786997752cef25310176349Romain Guy    private final Paint mGesturePaint = new Paint();
6835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
6982f3495b146b267f3786997752cef25310176349Romain Guy    private long mFadeDuration = 150;
7082f3495b146b267f3786997752cef25310176349Romain Guy    private long mFadeOffset = 420;
71d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private long mFadingStart;
7282f3495b146b267f3786997752cef25310176349Romain Guy    private boolean mFadingHasStarted;
73ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy    private boolean mFadeEnabled = true;
74db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy
7582f3495b146b267f3786997752cef25310176349Romain Guy    private int mCurrentColor;
76d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private int mCertainGestureColor = 0xFFFFFF00;
7782f3495b146b267f3786997752cef25310176349Romain Guy    private int mUncertainGestureColor = 0x48FFFF00;
7882f3495b146b267f3786997752cef25310176349Romain Guy    private float mGestureStrokeWidth = 12.0f;
79d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private int mInvalidateExtraBorder = 10;
8035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
8182f3495b146b267f3786997752cef25310176349Romain Guy    private int mGestureStrokeType = GESTURE_STROKE_TYPE_SINGLE;
82d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy    private float mGestureStrokeLengthThreshold = 50.0f;
8382f3495b146b267f3786997752cef25310176349Romain Guy    private float mGestureStrokeSquarenessTreshold = 0.275f;
8482f3495b146b267f3786997752cef25310176349Romain Guy    private float mGestureStrokeAngleThreshold = 40.0f;
8582f3495b146b267f3786997752cef25310176349Romain Guy
86e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    private int mOrientation = ORIENTATION_VERTICAL;
87e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy
88d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private final Rect mInvalidRect = new Rect();
89d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private final Path mPath = new Path();
90e705f2c52e683bdad2e845b50df6b6cbed660614Romain Guy    private boolean mGestureVisible = true;
9135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
9235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private float mX;
9335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private float mY;
948d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy
9535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private float mCurveEndX;
9635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private float mCurveEndY;
9735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
9882f3495b146b267f3786997752cef25310176349Romain Guy    private float mTotalLength;
9982f3495b146b267f3786997752cef25310176349Romain Guy    private boolean mIsGesturing = false;
100834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy    private boolean mPreviousWasGesturing = false;
10182f3495b146b267f3786997752cef25310176349Romain Guy    private boolean mInterceptEvents = true;
10282f3495b146b267f3786997752cef25310176349Romain Guy    private boolean mIsListeningForGestures;
10373d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy    private boolean mResetGesture;
10482f3495b146b267f3786997752cef25310176349Romain Guy
10535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    // current gesture
10682f3495b146b267f3786997752cef25310176349Romain Guy    private Gesture mCurrentGesture;
10782f3495b146b267f3786997752cef25310176349Romain Guy    private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
10835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
109c534727972c3835ed997e84a349f259915ef2cddRomain Guy    // TODO: Make this a list of WeakReferences
110d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private final ArrayList<OnGestureListener> mOnGestureListeners =
111d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy            new ArrayList<OnGestureListener>();
11282f3495b146b267f3786997752cef25310176349Romain Guy    // TODO: Make this a list of WeakReferences
11382f3495b146b267f3786997752cef25310176349Romain Guy    private final ArrayList<OnGesturePerformedListener> mOnGesturePerformedListeners =
11482f3495b146b267f3786997752cef25310176349Romain Guy            new ArrayList<OnGesturePerformedListener>();
1159af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    // TODO: Make this a list of WeakReferences
1169af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    private final ArrayList<OnGesturingListener> mOnGesturingListeners =
1179af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy            new ArrayList<OnGesturingListener>();
11882f3495b146b267f3786997752cef25310176349Romain Guy
11982f3495b146b267f3786997752cef25310176349Romain Guy    private boolean mHandleGestureActions;
12035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
12135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    // fading out effect
12235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private boolean mIsFadingOut = false;
12382f3495b146b267f3786997752cef25310176349Romain Guy    private float mFadingAlpha = 1.0f;
124d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private final AccelerateDecelerateInterpolator mInterpolator =
125d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy            new AccelerateDecelerateInterpolator();
12635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
12782f3495b146b267f3786997752cef25310176349Romain Guy    private final FadeOutRunnable mFadingOut = new FadeOutRunnable();
12835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
129db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public GestureOverlayView(Context context) {
13035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        super(context);
13135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        init();
13235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
13335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
134db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public GestureOverlayView(Context context, AttributeSet attrs) {
135d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        this(context, attrs, com.android.internal.R.attr.gestureOverlayViewStyle);
136d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    }
137d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
138617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    public GestureOverlayView(Context context, AttributeSet attrs, int defStyleAttr) {
139617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        this(context, attrs, defStyleAttr, 0);
140617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    }
141617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette
142617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    public GestureOverlayView(
143617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
144617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        super(context, attrs, defStyleAttr, defStyleRes);
145d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
146617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        final TypedArray a = context.obtainStyledAttributes(
147617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette                attrs, R.styleable.GestureOverlayView, defStyleAttr, defStyleRes);
148d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
14982f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeWidth = a.getFloat(R.styleable.GestureOverlayView_gestureStrokeWidth,
15082f3495b146b267f3786997752cef25310176349Romain Guy                mGestureStrokeWidth);
15182f3495b146b267f3786997752cef25310176349Romain Guy        mInvalidateExtraBorder = Math.max(1, ((int) mGestureStrokeWidth) - 1);
152d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        mCertainGestureColor = a.getColor(R.styleable.GestureOverlayView_gestureColor,
153d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy                mCertainGestureColor);
154d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        mUncertainGestureColor = a.getColor(R.styleable.GestureOverlayView_uncertainGestureColor,
155d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy                mUncertainGestureColor);
156d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        mFadeDuration = a.getInt(R.styleable.GestureOverlayView_fadeDuration, (int) mFadeDuration);
157d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        mFadeOffset = a.getInt(R.styleable.GestureOverlayView_fadeOffset, (int) mFadeOffset);
15882f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeType = a.getInt(R.styleable.GestureOverlayView_gestureStrokeType,
15982f3495b146b267f3786997752cef25310176349Romain Guy                mGestureStrokeType);
16082f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeLengthThreshold = a.getFloat(
16182f3495b146b267f3786997752cef25310176349Romain Guy                R.styleable.GestureOverlayView_gestureStrokeLengthThreshold,
16282f3495b146b267f3786997752cef25310176349Romain Guy                mGestureStrokeLengthThreshold);
16382f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeAngleThreshold = a.getFloat(
16482f3495b146b267f3786997752cef25310176349Romain Guy                R.styleable.GestureOverlayView_gestureStrokeAngleThreshold,
16582f3495b146b267f3786997752cef25310176349Romain Guy                mGestureStrokeAngleThreshold);
16682f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeSquarenessTreshold = a.getFloat(
16782f3495b146b267f3786997752cef25310176349Romain Guy                R.styleable.GestureOverlayView_gestureStrokeSquarenessThreshold,
16882f3495b146b267f3786997752cef25310176349Romain Guy                mGestureStrokeSquarenessTreshold);
16982f3495b146b267f3786997752cef25310176349Romain Guy        mInterceptEvents = a.getBoolean(R.styleable.GestureOverlayView_eventsInterceptionEnabled,
17082f3495b146b267f3786997752cef25310176349Romain Guy                mInterceptEvents);
171ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy        mFadeEnabled = a.getBoolean(R.styleable.GestureOverlayView_fadeEnabled,
172ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy                mFadeEnabled);
173e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy        mOrientation = a.getInt(R.styleable.GestureOverlayView_orientation, mOrientation);
174d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
175d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        a.recycle();
176d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
17735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        init();
17835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
17935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
18082f3495b146b267f3786997752cef25310176349Romain Guy    private void init() {
18182f3495b146b267f3786997752cef25310176349Romain Guy        setWillNotDraw(false);
18235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
18382f3495b146b267f3786997752cef25310176349Romain Guy        final Paint gesturePaint = mGesturePaint;
18482f3495b146b267f3786997752cef25310176349Romain Guy        gesturePaint.setAntiAlias(GESTURE_RENDERING_ANTIALIAS);
18582f3495b146b267f3786997752cef25310176349Romain Guy        gesturePaint.setColor(mCertainGestureColor);
18682f3495b146b267f3786997752cef25310176349Romain Guy        gesturePaint.setStyle(Paint.Style.STROKE);
18782f3495b146b267f3786997752cef25310176349Romain Guy        gesturePaint.setStrokeJoin(Paint.Join.ROUND);
18882f3495b146b267f3786997752cef25310176349Romain Guy        gesturePaint.setStrokeCap(Paint.Cap.ROUND);
18982f3495b146b267f3786997752cef25310176349Romain Guy        gesturePaint.setStrokeWidth(mGestureStrokeWidth);
19082f3495b146b267f3786997752cef25310176349Romain Guy        gesturePaint.setDither(DITHER_FLAG);
19182f3495b146b267f3786997752cef25310176349Romain Guy
19282f3495b146b267f3786997752cef25310176349Romain Guy        mCurrentColor = mCertainGestureColor;
19382f3495b146b267f3786997752cef25310176349Romain Guy        setPaintAlpha(255);
19435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
19535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
19682f3495b146b267f3786997752cef25310176349Romain Guy    public ArrayList<GesturePoint> getCurrentStroke() {
19782f3495b146b267f3786997752cef25310176349Romain Guy        return mStrokeBuffer;
19835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
19935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
200e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    public int getOrientation() {
201e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy        return mOrientation;
202e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    }
203e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy
204e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    public void setOrientation(int orientation) {
205e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy        mOrientation = orientation;
206e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    }
207e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy
20880756e38882720860db52f1fcc21fa1505a02abfTor Norbye    public void setGestureColor(@ColorInt int color) {
209db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy        mCertainGestureColor = color;
210db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    }
211db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy
21280756e38882720860db52f1fcc21fa1505a02abfTor Norbye    public void setUncertainGestureColor(@ColorInt int color) {
213db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy        mUncertainGestureColor = color;
214db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    }
215db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy
21680756e38882720860db52f1fcc21fa1505a02abfTor Norbye    @ColorInt
217db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public int getUncertainGestureColor() {
218db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy        return mUncertainGestureColor;
219db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    }
220db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy
22180756e38882720860db52f1fcc21fa1505a02abfTor Norbye    @ColorInt
222db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public int getGestureColor() {
223db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy        return mCertainGestureColor;
224db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    }
225db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy
22682f3495b146b267f3786997752cef25310176349Romain Guy    public float getGestureStrokeWidth() {
22782f3495b146b267f3786997752cef25310176349Romain Guy        return mGestureStrokeWidth;
228d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    }
229d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
23082f3495b146b267f3786997752cef25310176349Romain Guy    public void setGestureStrokeWidth(float gestureStrokeWidth) {
23182f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeWidth = gestureStrokeWidth;
23282f3495b146b267f3786997752cef25310176349Romain Guy        mInvalidateExtraBorder = Math.max(1, ((int) gestureStrokeWidth) - 1);
23382f3495b146b267f3786997752cef25310176349Romain Guy        mGesturePaint.setStrokeWidth(gestureStrokeWidth);
234d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    }
235d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
23682f3495b146b267f3786997752cef25310176349Romain Guy    public int getGestureStrokeType() {
23782f3495b146b267f3786997752cef25310176349Romain Guy        return mGestureStrokeType;
23882f3495b146b267f3786997752cef25310176349Romain Guy    }
23935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
24082f3495b146b267f3786997752cef25310176349Romain Guy    public void setGestureStrokeType(int gestureStrokeType) {
24182f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeType = gestureStrokeType;
24282f3495b146b267f3786997752cef25310176349Romain Guy    }
24335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
24482f3495b146b267f3786997752cef25310176349Romain Guy    public float getGestureStrokeLengthThreshold() {
24582f3495b146b267f3786997752cef25310176349Romain Guy        return mGestureStrokeLengthThreshold;
24635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
24735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
24882f3495b146b267f3786997752cef25310176349Romain Guy    public void setGestureStrokeLengthThreshold(float gestureStrokeLengthThreshold) {
24982f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeLengthThreshold = gestureStrokeLengthThreshold;
25082f3495b146b267f3786997752cef25310176349Romain Guy    }
251c534727972c3835ed997e84a349f259915ef2cddRomain Guy
25282f3495b146b267f3786997752cef25310176349Romain Guy    public float getGestureStrokeSquarenessTreshold() {
25382f3495b146b267f3786997752cef25310176349Romain Guy        return mGestureStrokeSquarenessTreshold;
25435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
25535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
25682f3495b146b267f3786997752cef25310176349Romain Guy    public void setGestureStrokeSquarenessTreshold(float gestureStrokeSquarenessTreshold) {
25782f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeSquarenessTreshold = gestureStrokeSquarenessTreshold;
25882f3495b146b267f3786997752cef25310176349Romain Guy    }
259c534727972c3835ed997e84a349f259915ef2cddRomain Guy
26082f3495b146b267f3786997752cef25310176349Romain Guy    public float getGestureStrokeAngleThreshold() {
26182f3495b146b267f3786997752cef25310176349Romain Guy        return mGestureStrokeAngleThreshold;
26282f3495b146b267f3786997752cef25310176349Romain Guy    }
263c534727972c3835ed997e84a349f259915ef2cddRomain Guy
26482f3495b146b267f3786997752cef25310176349Romain Guy    public void setGestureStrokeAngleThreshold(float gestureStrokeAngleThreshold) {
26582f3495b146b267f3786997752cef25310176349Romain Guy        mGestureStrokeAngleThreshold = gestureStrokeAngleThreshold;
26682f3495b146b267f3786997752cef25310176349Romain Guy    }
267c534727972c3835ed997e84a349f259915ef2cddRomain Guy
26882f3495b146b267f3786997752cef25310176349Romain Guy    public boolean isEventsInterceptionEnabled() {
26982f3495b146b267f3786997752cef25310176349Romain Guy        return mInterceptEvents;
27082f3495b146b267f3786997752cef25310176349Romain Guy    }
271c534727972c3835ed997e84a349f259915ef2cddRomain Guy
27282f3495b146b267f3786997752cef25310176349Romain Guy    public void setEventsInterceptionEnabled(boolean enabled) {
27382f3495b146b267f3786997752cef25310176349Romain Guy        mInterceptEvents = enabled;
27482f3495b146b267f3786997752cef25310176349Romain Guy    }
27582f3495b146b267f3786997752cef25310176349Romain Guy
276ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy    public boolean isFadeEnabled() {
277ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy        return mFadeEnabled;
278ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy    }
279ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy
280ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy    public void setFadeEnabled(boolean fadeEnabled) {
281ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy        mFadeEnabled = fadeEnabled;
282ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy    }
283ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy
28482f3495b146b267f3786997752cef25310176349Romain Guy    public Gesture getGesture() {
28582f3495b146b267f3786997752cef25310176349Romain Guy        return mCurrentGesture;
28682f3495b146b267f3786997752cef25310176349Romain Guy    }
287c534727972c3835ed997e84a349f259915ef2cddRomain Guy
28882f3495b146b267f3786997752cef25310176349Romain Guy    public void setGesture(Gesture gesture) {
28935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        if (mCurrentGesture != null) {
29082f3495b146b267f3786997752cef25310176349Romain Guy            clear(false);
29135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
29282f3495b146b267f3786997752cef25310176349Romain Guy
29382f3495b146b267f3786997752cef25310176349Romain Guy        setCurrentColor(mCertainGestureColor);
29482f3495b146b267f3786997752cef25310176349Romain Guy        mCurrentGesture = gesture;
29582f3495b146b267f3786997752cef25310176349Romain Guy
29682f3495b146b267f3786997752cef25310176349Romain Guy        final Path path = mCurrentGesture.toPath();
29782f3495b146b267f3786997752cef25310176349Romain Guy        final RectF bounds = new RectF();
29882f3495b146b267f3786997752cef25310176349Romain Guy        path.computeBounds(bounds, true);
29982f3495b146b267f3786997752cef25310176349Romain Guy
300cfbe8cf878f60ab2e3e25c09a91227dd85731718Romain Guy        // TODO: The path should also be scaled to fit inside this view
30182f3495b146b267f3786997752cef25310176349Romain Guy        mPath.rewind();
30203f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy        mPath.addPath(path, -bounds.left + (getWidth() - bounds.width()) / 2.0f,
30303f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy                -bounds.top + (getHeight() - bounds.height()) / 2.0f);
30403f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy
30503f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy        mResetGesture = true;
30682f3495b146b267f3786997752cef25310176349Romain Guy
30782f3495b146b267f3786997752cef25310176349Romain Guy        invalidate();
30835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
30935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
310b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    public Path getGesturePath() {
311b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        return mPath;
312b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    }
313b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy
314b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    public Path getGesturePath(Path path) {
315b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        path.set(mPath);
316b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        return path;
317b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    }
318b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy
319b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    public boolean isGestureVisible() {
320b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        return mGestureVisible;
321b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    }
322b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy
323b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    public void setGestureVisible(boolean visible) {
324b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        mGestureVisible = visible;
325b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    }
326b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy
327b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    public long getFadeOffset() {
328b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        return mFadeOffset;
329b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    }
330b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy
331b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    public void setFadeOffset(long fadeOffset) {
332b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        mFadeOffset = fadeOffset;
333b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy    }
334b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy
335db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public void addOnGestureListener(OnGestureListener listener) {
336db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy        mOnGestureListeners.add(listener);
33735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
33835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
339db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public void removeOnGestureListener(OnGestureListener listener) {
340db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy        mOnGestureListeners.remove(listener);
34135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
34235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
343d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    public void removeAllOnGestureListeners() {
344d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        mOnGestureListeners.clear();
345d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    }
346d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
34782f3495b146b267f3786997752cef25310176349Romain Guy    public void addOnGesturePerformedListener(OnGesturePerformedListener listener) {
34882f3495b146b267f3786997752cef25310176349Romain Guy        mOnGesturePerformedListeners.add(listener);
34982f3495b146b267f3786997752cef25310176349Romain Guy        if (mOnGesturePerformedListeners.size() > 0) {
35082f3495b146b267f3786997752cef25310176349Romain Guy            mHandleGestureActions = true;
35182f3495b146b267f3786997752cef25310176349Romain Guy        }
35282f3495b146b267f3786997752cef25310176349Romain Guy    }
35382f3495b146b267f3786997752cef25310176349Romain Guy
35482f3495b146b267f3786997752cef25310176349Romain Guy    public void removeOnGesturePerformedListener(OnGesturePerformedListener listener) {
35582f3495b146b267f3786997752cef25310176349Romain Guy        mOnGesturePerformedListeners.remove(listener);
35682f3495b146b267f3786997752cef25310176349Romain Guy        if (mOnGesturePerformedListeners.size() <= 0) {
35782f3495b146b267f3786997752cef25310176349Romain Guy            mHandleGestureActions = false;
35882f3495b146b267f3786997752cef25310176349Romain Guy        }
35982f3495b146b267f3786997752cef25310176349Romain Guy    }
36082f3495b146b267f3786997752cef25310176349Romain Guy
36182f3495b146b267f3786997752cef25310176349Romain Guy    public void removeAllOnGesturePerformedListeners() {
36282f3495b146b267f3786997752cef25310176349Romain Guy        mOnGesturePerformedListeners.clear();
36382f3495b146b267f3786997752cef25310176349Romain Guy        mHandleGestureActions = false;
36482f3495b146b267f3786997752cef25310176349Romain Guy    }
36535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
3669af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    public void addOnGesturingListener(OnGesturingListener listener) {
3679af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        mOnGesturingListeners.add(listener);
3689af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    }
3699af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
3709af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    public void removeOnGesturingListener(OnGesturingListener listener) {
3719af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        mOnGesturingListeners.remove(listener);
3729af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    }
3739af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
3749af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    public void removeAllOnGesturingListeners() {
3759af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        mOnGesturingListeners.clear();
3769af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    }
3779af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
37882f3495b146b267f3786997752cef25310176349Romain Guy    public boolean isGesturing() {
37982f3495b146b267f3786997752cef25310176349Romain Guy        return mIsGesturing;
38082f3495b146b267f3786997752cef25310176349Romain Guy    }
38182f3495b146b267f3786997752cef25310176349Romain Guy
38282f3495b146b267f3786997752cef25310176349Romain Guy    private void setCurrentColor(int color) {
38382f3495b146b267f3786997752cef25310176349Romain Guy        mCurrentColor = color;
38482f3495b146b267f3786997752cef25310176349Romain Guy        if (mFadingHasStarted) {
38582f3495b146b267f3786997752cef25310176349Romain Guy            setPaintAlpha((int) (255 * mFadingAlpha));
38635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        } else {
38782f3495b146b267f3786997752cef25310176349Romain Guy            setPaintAlpha(255);
38882f3495b146b267f3786997752cef25310176349Romain Guy        }
38982f3495b146b267f3786997752cef25310176349Romain Guy        invalidate();
39082f3495b146b267f3786997752cef25310176349Romain Guy    }
39182f3495b146b267f3786997752cef25310176349Romain Guy
39227a2b50dedd4315b921d502a30ac0fda51fb6a7cRomain Guy    /**
39327a2b50dedd4315b921d502a30ac0fda51fb6a7cRomain Guy     * @hide
39427a2b50dedd4315b921d502a30ac0fda51fb6a7cRomain Guy     */
39527a2b50dedd4315b921d502a30ac0fda51fb6a7cRomain Guy    public Paint getGesturePaint() {
39627a2b50dedd4315b921d502a30ac0fda51fb6a7cRomain Guy        return mGesturePaint;
39727a2b50dedd4315b921d502a30ac0fda51fb6a7cRomain Guy    }
39827a2b50dedd4315b921d502a30ac0fda51fb6a7cRomain Guy
39982f3495b146b267f3786997752cef25310176349Romain Guy    @Override
40082f3495b146b267f3786997752cef25310176349Romain Guy    public void draw(Canvas canvas) {
40182f3495b146b267f3786997752cef25310176349Romain Guy        super.draw(canvas);
40282f3495b146b267f3786997752cef25310176349Romain Guy
403b973eef289ee0315dbae147eccb1915d3ed08f43Romain Guy        if (mCurrentGesture != null && mGestureVisible) {
40482f3495b146b267f3786997752cef25310176349Romain Guy            canvas.drawPath(mPath, mGesturePaint);
40535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
40682f3495b146b267f3786997752cef25310176349Romain Guy    }
40782f3495b146b267f3786997752cef25310176349Romain Guy
40882f3495b146b267f3786997752cef25310176349Romain Guy    private void setPaintAlpha(int alpha) {
40982f3495b146b267f3786997752cef25310176349Romain Guy        alpha += alpha >> 7;
41082f3495b146b267f3786997752cef25310176349Romain Guy        final int baseAlpha = mCurrentColor >>> 24;
41182f3495b146b267f3786997752cef25310176349Romain Guy        final int useAlpha = baseAlpha * alpha >> 8;
41282f3495b146b267f3786997752cef25310176349Romain Guy        mGesturePaint.setColor((mCurrentColor << 8 >>> 8) | (useAlpha << 24));
41382f3495b146b267f3786997752cef25310176349Romain Guy    }
41435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
41582f3495b146b267f3786997752cef25310176349Romain Guy    public void clear(boolean animated) {
41603f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy        clear(animated, false, true);
41735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
41835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
41903f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy    private void clear(boolean animated, boolean fireActionPerformed, boolean immediate) {
42082f3495b146b267f3786997752cef25310176349Romain Guy        setPaintAlpha(255);
4219edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy        removeCallbacks(mFadingOut);
42273d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy        mResetGesture = false;
4239edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy        mFadingOut.fireActionPerformed = fireActionPerformed;
42473d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy        mFadingOut.resetMultipleStrokes = false;
4259edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy
42682f3495b146b267f3786997752cef25310176349Romain Guy        if (animated && mCurrentGesture != null) {
4279edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            mFadingAlpha = 1.0f;
4289edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            mIsFadingOut = true;
4299edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            mFadingHasStarted = false;
4309edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            mFadingStart = AnimationUtils.currentAnimationTimeMillis() + mFadeOffset;
4319edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy
432d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy            postDelayed(mFadingOut, mFadeOffset);
43335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        } else {
4349edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            mFadingAlpha = 1.0f;
4359edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            mIsFadingOut = false;
4369edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            mFadingHasStarted = false;
4379edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy
43803f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy            if (immediate) {
4399edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy                mCurrentGesture = null;
4409edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy                mPath.rewind();
4419edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy                invalidate();
44203f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy            } else if (fireActionPerformed) {
44303f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy                postDelayed(mFadingOut, mFadeOffset);
44473d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy            } else if (mGestureStrokeType == GESTURE_STROKE_TYPE_MULTIPLE) {
44573d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy                mFadingOut.resetMultipleStrokes = true;
44673d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy                postDelayed(mFadingOut, mFadeOffset);
44703f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy            } else {
44803f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy                mCurrentGesture = null;
44903f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy                mPath.rewind();
45003f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy                invalidate();
4519edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            }
45235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
45335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
45435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
45582f3495b146b267f3786997752cef25310176349Romain Guy    public void cancelClearAnimation() {
45682f3495b146b267f3786997752cef25310176349Romain Guy        setPaintAlpha(255);
45735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        mIsFadingOut = false;
45882f3495b146b267f3786997752cef25310176349Romain Guy        mFadingHasStarted = false;
459d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        removeCallbacks(mFadingOut);
46082f3495b146b267f3786997752cef25310176349Romain Guy        mPath.rewind();
46182f3495b146b267f3786997752cef25310176349Romain Guy        mCurrentGesture = null;
46282f3495b146b267f3786997752cef25310176349Romain Guy    }
46382f3495b146b267f3786997752cef25310176349Romain Guy
46482f3495b146b267f3786997752cef25310176349Romain Guy    public void cancelGesture() {
46582f3495b146b267f3786997752cef25310176349Romain Guy        mIsListeningForGestures = false;
46682f3495b146b267f3786997752cef25310176349Romain Guy
46782f3495b146b267f3786997752cef25310176349Romain Guy        // add the stroke to the current gesture
46882f3495b146b267f3786997752cef25310176349Romain Guy        mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer));
46982f3495b146b267f3786997752cef25310176349Romain Guy
47082f3495b146b267f3786997752cef25310176349Romain Guy        // pass the event to handlers
47182f3495b146b267f3786997752cef25310176349Romain Guy        final long now = SystemClock.uptimeMillis();
47282f3495b146b267f3786997752cef25310176349Romain Guy        final MotionEvent event = MotionEvent.obtain(now, now,
47382f3495b146b267f3786997752cef25310176349Romain Guy                MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
47482f3495b146b267f3786997752cef25310176349Romain Guy
47582f3495b146b267f3786997752cef25310176349Romain Guy        final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
4769af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        int count = listeners.size();
47782f3495b146b267f3786997752cef25310176349Romain Guy        for (int i = 0; i < count; i++) {
47882f3495b146b267f3786997752cef25310176349Romain Guy            listeners.get(i).onGestureCancelled(this, event);
47982f3495b146b267f3786997752cef25310176349Romain Guy        }
48082f3495b146b267f3786997752cef25310176349Romain Guy
48182f3495b146b267f3786997752cef25310176349Romain Guy        event.recycle();
48282f3495b146b267f3786997752cef25310176349Romain Guy
48382f3495b146b267f3786997752cef25310176349Romain Guy        clear(false);
48482f3495b146b267f3786997752cef25310176349Romain Guy        mIsGesturing = false;
485834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy        mPreviousWasGesturing = false;
48682f3495b146b267f3786997752cef25310176349Romain Guy        mStrokeBuffer.clear();
4879af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
4889af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        final ArrayList<OnGesturingListener> otherListeners = mOnGesturingListeners;
4899af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        count = otherListeners.size();
4909af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        for (int i = 0; i < count; i++) {
4919af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy            otherListeners.get(i).onGesturingEnded(this);
4929af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        }
49335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
49435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
49535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    @Override
49682f3495b146b267f3786997752cef25310176349Romain Guy    protected void onDetachedFromWindow() {
49746bfc4811094e5b1e3196246e457d4c6b58332ecRomain Guy        super.onDetachedFromWindow();
49882f3495b146b267f3786997752cef25310176349Romain Guy        cancelClearAnimation();
49982f3495b146b267f3786997752cef25310176349Romain Guy    }
50082f3495b146b267f3786997752cef25310176349Romain Guy
50182f3495b146b267f3786997752cef25310176349Romain Guy    @Override
50282f3495b146b267f3786997752cef25310176349Romain Guy    public boolean dispatchTouchEvent(MotionEvent event) {
50382f3495b146b267f3786997752cef25310176349Romain Guy        if (isEnabled()) {
504834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy            final boolean cancelDispatch = (mIsGesturing || (mCurrentGesture != null &&
505834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy                    mCurrentGesture.getStrokesCount() > 0 && mPreviousWasGesturing)) &&
506834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy                    mInterceptEvents;
507834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy
50882f3495b146b267f3786997752cef25310176349Romain Guy            processEvent(event);
50982f3495b146b267f3786997752cef25310176349Romain Guy
51082f3495b146b267f3786997752cef25310176349Romain Guy            if (cancelDispatch) {
51182f3495b146b267f3786997752cef25310176349Romain Guy                event.setAction(MotionEvent.ACTION_CANCEL);
51282f3495b146b267f3786997752cef25310176349Romain Guy            }
51382f3495b146b267f3786997752cef25310176349Romain Guy
51482f3495b146b267f3786997752cef25310176349Romain Guy            super.dispatchTouchEvent(event);
515834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy
51635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            return true;
51735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
51835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
51982f3495b146b267f3786997752cef25310176349Romain Guy        return super.dispatchTouchEvent(event);
520d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    }
521d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
52282f3495b146b267f3786997752cef25310176349Romain Guy    private boolean processEvent(MotionEvent event) {
52335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        switch (event.getAction()) {
52435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            case MotionEvent.ACTION_DOWN:
525e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy                touchDown(event);
52682f3495b146b267f3786997752cef25310176349Romain Guy                invalidate();
52782f3495b146b267f3786997752cef25310176349Romain Guy                return true;
52835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            case MotionEvent.ACTION_MOVE:
52982f3495b146b267f3786997752cef25310176349Romain Guy                if (mIsListeningForGestures) {
53082f3495b146b267f3786997752cef25310176349Romain Guy                    Rect rect = touchMove(event);
53182f3495b146b267f3786997752cef25310176349Romain Guy                    if (rect != null) {
53282f3495b146b267f3786997752cef25310176349Romain Guy                        invalidate(rect);
53382f3495b146b267f3786997752cef25310176349Romain Guy                    }
53482f3495b146b267f3786997752cef25310176349Romain Guy                    return true;
53535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li                }
53635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li                break;
53735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            case MotionEvent.ACTION_UP:
53882f3495b146b267f3786997752cef25310176349Romain Guy                if (mIsListeningForGestures) {
53982f3495b146b267f3786997752cef25310176349Romain Guy                    touchUp(event, false);
54082f3495b146b267f3786997752cef25310176349Romain Guy                    invalidate();
54182f3495b146b267f3786997752cef25310176349Romain Guy                    return true;
54282f3495b146b267f3786997752cef25310176349Romain Guy                }
543d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy                break;
544d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy            case MotionEvent.ACTION_CANCEL:
54582f3495b146b267f3786997752cef25310176349Romain Guy                if (mIsListeningForGestures) {
54682f3495b146b267f3786997752cef25310176349Romain Guy                    touchUp(event, true);
54782f3495b146b267f3786997752cef25310176349Romain Guy                    invalidate();
54882f3495b146b267f3786997752cef25310176349Romain Guy                    return true;
54982f3495b146b267f3786997752cef25310176349Romain Guy                }
55035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
55182f3495b146b267f3786997752cef25310176349Romain Guy
55282f3495b146b267f3786997752cef25310176349Romain Guy        return false;
55335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
55435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
555e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    private void touchDown(MotionEvent event) {
55682f3495b146b267f3786997752cef25310176349Romain Guy        mIsListeningForGestures = true;
55782f3495b146b267f3786997752cef25310176349Romain Guy
55835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        float x = event.getX();
55935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        float y = event.getY();
56035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
56135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        mX = x;
56235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        mY = y;
56335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
56482f3495b146b267f3786997752cef25310176349Romain Guy        mTotalLength = 0;
56582f3495b146b267f3786997752cef25310176349Romain Guy        mIsGesturing = false;
56682f3495b146b267f3786997752cef25310176349Romain Guy
56773d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy        if (mGestureStrokeType == GESTURE_STROKE_TYPE_SINGLE || mResetGesture) {
56882f3495b146b267f3786997752cef25310176349Romain Guy            if (mHandleGestureActions) setCurrentColor(mUncertainGestureColor);
56973d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy            mResetGesture = false;
57082f3495b146b267f3786997752cef25310176349Romain Guy            mCurrentGesture = null;
57182f3495b146b267f3786997752cef25310176349Romain Guy            mPath.rewind();
57282f3495b146b267f3786997752cef25310176349Romain Guy        } else if (mCurrentGesture == null || mCurrentGesture.getStrokesCount() == 0) {
57382f3495b146b267f3786997752cef25310176349Romain Guy            if (mHandleGestureActions) setCurrentColor(mUncertainGestureColor);
57482f3495b146b267f3786997752cef25310176349Romain Guy        }
57582f3495b146b267f3786997752cef25310176349Romain Guy
57682f3495b146b267f3786997752cef25310176349Romain Guy        // if there is fading out going on, stop it.
57782f3495b146b267f3786997752cef25310176349Romain Guy        if (mFadingHasStarted) {
57882f3495b146b267f3786997752cef25310176349Romain Guy            cancelClearAnimation();
57982f3495b146b267f3786997752cef25310176349Romain Guy        } else if (mIsFadingOut) {
58082f3495b146b267f3786997752cef25310176349Romain Guy            setPaintAlpha(255);
58182f3495b146b267f3786997752cef25310176349Romain Guy            mIsFadingOut = false;
58282f3495b146b267f3786997752cef25310176349Romain Guy            mFadingHasStarted = false;
58382f3495b146b267f3786997752cef25310176349Romain Guy            removeCallbacks(mFadingOut);
58482f3495b146b267f3786997752cef25310176349Romain Guy        }
58582f3495b146b267f3786997752cef25310176349Romain Guy
58635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        if (mCurrentGesture == null) {
58735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            mCurrentGesture = new Gesture();
58835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
58935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
59082f3495b146b267f3786997752cef25310176349Romain Guy        mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
59135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        mPath.moveTo(x, y);
59235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
59382f3495b146b267f3786997752cef25310176349Romain Guy        final int border = mInvalidateExtraBorder;
59482f3495b146b267f3786997752cef25310176349Romain Guy        mInvalidRect.set((int) x - border, (int) y - border, (int) x + border, (int) y + border);
59582f3495b146b267f3786997752cef25310176349Romain Guy
59635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        mCurveEndX = x;
59735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        mCurveEndY = y;
598ff686ce11e952a9001b1384d8e7d82127b0529a2Romain Guy
599ff686ce11e952a9001b1384d8e7d82127b0529a2Romain Guy        // pass the event to handlers
600ff686ce11e952a9001b1384d8e7d82127b0529a2Romain Guy        final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
601ff686ce11e952a9001b1384d8e7d82127b0529a2Romain Guy        final int count = listeners.size();
602ff686ce11e952a9001b1384d8e7d82127b0529a2Romain Guy        for (int i = 0; i < count; i++) {
603ff686ce11e952a9001b1384d8e7d82127b0529a2Romain Guy            listeners.get(i).onGestureStarted(this, event);
6048d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy        }
60535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
60635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
60735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    private Rect touchMove(MotionEvent event) {
60835aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        Rect areaToRefresh = null;
6098d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy
610d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        final float x = event.getX();
611d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        final float y = event.getY();
612d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
613d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        final float previousX = mX;
614d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        final float previousY = mY;
61535aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
616d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        final float dx = Math.abs(x - previousX);
617d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        final float dy = Math.abs(y - previousY);
6188d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy
619d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        if (dx >= GestureStroke.TOUCH_TOLERANCE || dy >= GestureStroke.TOUCH_TOLERANCE) {
620d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy            areaToRefresh = mInvalidRect;
621d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
62235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            // start with the curve end
62382f3495b146b267f3786997752cef25310176349Romain Guy            final int border = mInvalidateExtraBorder;
62482f3495b146b267f3786997752cef25310176349Romain Guy            areaToRefresh.set((int) mCurveEndX - border, (int) mCurveEndY - border,
62582f3495b146b267f3786997752cef25310176349Romain Guy                    (int) mCurveEndX + border, (int) mCurveEndY + border);
6268d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy
62782f3495b146b267f3786997752cef25310176349Romain Guy            float cX = mCurveEndX = (x + previousX) / 2;
62882f3495b146b267f3786997752cef25310176349Romain Guy            float cY = mCurveEndY = (y + previousY) / 2;
629d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
63082f3495b146b267f3786997752cef25310176349Romain Guy            mPath.quadTo(previousX, previousY, cX, cY);
6318d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy
63235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            // union with the control point of the new curve
63382f3495b146b267f3786997752cef25310176349Romain Guy            areaToRefresh.union((int) previousX - border, (int) previousY - border,
63482f3495b146b267f3786997752cef25310176349Romain Guy                    (int) previousX + border, (int) previousY + border);
6358d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy
63635aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            // union with the end point of the new curve
63782f3495b146b267f3786997752cef25310176349Romain Guy            areaToRefresh.union((int) cX - border, (int) cY - border,
63882f3495b146b267f3786997752cef25310176349Romain Guy                    (int) cX + border, (int) cY + border);
63935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
64035aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            mX = x;
64135aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li            mY = y;
64235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
643d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
64435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
645d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            if (mHandleGestureActions && !mIsGesturing) {
64633253a4baa6279f81a73425b49dfb6abe5f5416eNeil Fuller                mTotalLength += (float) Math.hypot(dx, dy);
64782f3495b146b267f3786997752cef25310176349Romain Guy
648d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                if (mTotalLength > mGestureStrokeLengthThreshold) {
649d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                    final OrientedBoundingBox box =
65046c53129c6f27c9193ab195a69cb50591b8c1fa2Romain Guy                            GestureUtils.computeOrientedBoundingBox(mStrokeBuffer);
65182f3495b146b267f3786997752cef25310176349Romain Guy
652d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                    float angle = Math.abs(box.orientation);
653d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                    if (angle > 90) {
654d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                        angle = 180 - angle;
655d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                    }
65682f3495b146b267f3786997752cef25310176349Romain Guy
657d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                    if (box.squareness > mGestureStrokeSquarenessTreshold ||
658d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                            (mOrientation == ORIENTATION_VERTICAL ?
659d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                                    angle < mGestureStrokeAngleThreshold :
660d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                                    angle > mGestureStrokeAngleThreshold)) {
66182f3495b146b267f3786997752cef25310176349Romain Guy
662d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                        mIsGesturing = true;
663d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                        setCurrentColor(mCertainGestureColor);
6649af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
6659af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy                        final ArrayList<OnGesturingListener> listeners = mOnGesturingListeners;
6669af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy                        int count = listeners.size();
6679af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy                        for (int i = 0; i < count; i++) {
6689af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy                            listeners.get(i).onGesturingStarted(this);
6699af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy                        }
670d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                    }
67182f3495b146b267f3786997752cef25310176349Romain Guy                }
67282f3495b146b267f3786997752cef25310176349Romain Guy            }
67382f3495b146b267f3786997752cef25310176349Romain Guy
674d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            // pass the event to handlers
675d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
676d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            final int count = listeners.size();
677d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            for (int i = 0; i < count; i++) {
678d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                listeners.get(i).onGesture(this, event);
679d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            }
680d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        }
681ff686ce11e952a9001b1384d8e7d82127b0529a2Romain Guy
68235aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        return areaToRefresh;
68335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
68435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
685d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy    private void touchUp(MotionEvent event, boolean cancel) {
68682f3495b146b267f3786997752cef25310176349Romain Guy        mIsListeningForGestures = false;
68735aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
688d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        // A gesture wasn't started or was cancelled
689d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        if (mCurrentGesture != null) {
690d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            // add the stroke to the current gesture
691d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            mCurrentGesture.addStroke(new GestureStroke(mStrokeBuffer));
692d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy
693d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            if (!cancel) {
694d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                // pass the event to handlers
695d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
696d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                int count = listeners.size();
697d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                for (int i = 0; i < count; i++) {
698d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                    listeners.get(i).onGestureEnded(this, event);
699d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                }
700d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
70103f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy                clear(mHandleGestureActions && mFadeEnabled, mHandleGestureActions && mIsGesturing,
70203f0b21b5a317aa6c0f0cd4d7ac91cabdf379d3eRomain Guy                        false);
703d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            } else {
704d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy                cancelGesture(event);
70582f3495b146b267f3786997752cef25310176349Romain Guy
70682f3495b146b267f3786997752cef25310176349Romain Guy            }
707d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        } else {
708d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            cancelGesture(event);
70935aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li        }
710d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
7118d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy        mStrokeBuffer.clear();
712834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy        mPreviousWasGesturing = mIsGesturing;
71382f3495b146b267f3786997752cef25310176349Romain Guy        mIsGesturing = false;
7149af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
7159af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        final ArrayList<OnGesturingListener> listeners = mOnGesturingListeners;
7169af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        int count = listeners.size();
7179af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        for (int i = 0; i < count; i++) {
7189af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy            listeners.get(i).onGesturingEnded(this);
7199af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        }
72082f3495b146b267f3786997752cef25310176349Romain Guy    }
72182f3495b146b267f3786997752cef25310176349Romain Guy
722d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy    private void cancelGesture(MotionEvent event) {
723d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        // pass the event to handlers
724d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        final ArrayList<OnGestureListener> listeners = mOnGestureListeners;
725d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        final int count = listeners.size();
726d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        for (int i = 0; i < count; i++) {
727d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy            listeners.get(i).onGestureCancelled(this, event);
728d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        }
729d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy
730d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy        clear(false);
731d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy    }
732d1c67d42abdf41d10c9a7589da1e0088af1e123aRomain Guy
733ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy    private void fireOnGesturePerformed() {
7349af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        final ArrayList<OnGesturePerformedListener> actionListeners = mOnGesturePerformedListeners;
735ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy        final int count = actionListeners.size();
736ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy        for (int i = 0; i < count; i++) {
7379af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy            actionListeners.get(i).onGesturePerformed(GestureOverlayView.this, mCurrentGesture);
738ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy        }
739e7c36dda7ada30e02f3aa9d75f282a53402aeae7Romain Guy    }
740ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy
74182f3495b146b267f3786997752cef25310176349Romain Guy    private class FadeOutRunnable implements Runnable {
74282f3495b146b267f3786997752cef25310176349Romain Guy        boolean fireActionPerformed;
74373d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy        boolean resetMultipleStrokes;
74482f3495b146b267f3786997752cef25310176349Romain Guy
74582f3495b146b267f3786997752cef25310176349Romain Guy        public void run() {
74682f3495b146b267f3786997752cef25310176349Romain Guy            if (mIsFadingOut) {
74782f3495b146b267f3786997752cef25310176349Romain Guy                final long now = AnimationUtils.currentAnimationTimeMillis();
74882f3495b146b267f3786997752cef25310176349Romain Guy                final long duration = now - mFadingStart;
74982f3495b146b267f3786997752cef25310176349Romain Guy
75082f3495b146b267f3786997752cef25310176349Romain Guy                if (duration > mFadeDuration) {
75182f3495b146b267f3786997752cef25310176349Romain Guy                    if (fireActionPerformed) {
752ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy                        fireOnGesturePerformed();
75382f3495b146b267f3786997752cef25310176349Romain Guy                    }
75482f3495b146b267f3786997752cef25310176349Romain Guy
755834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy                    mPreviousWasGesturing = false;
75682f3495b146b267f3786997752cef25310176349Romain Guy                    mIsFadingOut = false;
75782f3495b146b267f3786997752cef25310176349Romain Guy                    mFadingHasStarted = false;
75882f3495b146b267f3786997752cef25310176349Romain Guy                    mPath.rewind();
75982f3495b146b267f3786997752cef25310176349Romain Guy                    mCurrentGesture = null;
76082f3495b146b267f3786997752cef25310176349Romain Guy                    setPaintAlpha(255);
76182f3495b146b267f3786997752cef25310176349Romain Guy                } else {
76282f3495b146b267f3786997752cef25310176349Romain Guy                    mFadingHasStarted = true;
76382f3495b146b267f3786997752cef25310176349Romain Guy                    float interpolatedTime = Math.max(0.0f,
76482f3495b146b267f3786997752cef25310176349Romain Guy                            Math.min(1.0f, duration / (float) mFadeDuration));
76582f3495b146b267f3786997752cef25310176349Romain Guy                    mFadingAlpha = 1.0f - mInterpolator.getInterpolation(interpolatedTime);
76682f3495b146b267f3786997752cef25310176349Romain Guy                    setPaintAlpha((int) (255 * mFadingAlpha));
76782f3495b146b267f3786997752cef25310176349Romain Guy                    postDelayed(this, FADE_ANIMATION_RATE);
76882f3495b146b267f3786997752cef25310176349Romain Guy                }
76973d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy            } else if (resetMultipleStrokes) {
77073d2589d6df34307c22f1adcdfa1debf4c780eccRomain Guy                mResetGesture = true;
7719edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy            } else {
772ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy                fireOnGesturePerformed();
773ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy
774ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy                mFadingHasStarted = false;
775ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy                mPath.rewind();
776ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy                mCurrentGesture = null;
777834f0391b50d33a832bd583fd7763a9a12208e8dRomain Guy                mPreviousWasGesturing = false;
778ec25df9fbc685be384f8dd764fa224a4d923e9d8Romain Guy                setPaintAlpha(255);
77982f3495b146b267f3786997752cef25310176349Romain Guy            }
7809edc4e8930273d9bb3b5e9cb73c219e012de8ffbRomain Guy
7818d78756c160bda736cccef9ca1a6e2d6a159ac42Romain Guy            invalidate();
78282f3495b146b267f3786997752cef25310176349Romain Guy        }
78335aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li    }
78435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li
7859af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    public static interface OnGesturingListener {
7869af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        void onGesturingStarted(GestureOverlayView overlay);
7879af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
7889af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy        void onGesturingEnded(GestureOverlayView overlay);
7899af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy    }
7909af0b4f7be14f2b3ed0ecc843c57ea47ec288e55Romain Guy
791db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    public static interface OnGestureListener {
792d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        void onGestureStarted(GestureOverlayView overlay, MotionEvent event);
793d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy
794d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        void onGesture(GestureOverlayView overlay, MotionEvent event);
795db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy
796d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        void onGestureEnded(GestureOverlayView overlay, MotionEvent event);
797db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy
798d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy        void onGestureCancelled(GestureOverlayView overlay, MotionEvent event);
799db567c390bd56c05614eaa83c02dbb99f97ad9ccRomain Guy    }
80082f3495b146b267f3786997752cef25310176349Romain Guy
80182f3495b146b267f3786997752cef25310176349Romain Guy    public static interface OnGesturePerformedListener {
80282f3495b146b267f3786997752cef25310176349Romain Guy        void onGesturePerformed(GestureOverlayView overlay, Gesture gesture);
80382f3495b146b267f3786997752cef25310176349Romain Guy    }
80435aa84b1f9f5e42dd00cb66df993ed1628c8963bYang Li}
805