1333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka/*
2333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka * Copyright (C) 2012 The Android Open Source Project
3333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka *
4333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka * in compliance with the License. You may obtain a copy of the License at
6333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka *
7333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0
8333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka *
9333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software distributed under the License
10333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka * or implied. See the License for the specific language governing permissions and limitations under
12333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka * the License.
13333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka */
14333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
15333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaokapackage com.android.inputmethod.keyboard.internal;
16333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
17333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaokaimport android.content.res.TypedArray;
18333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaokaimport android.graphics.Canvas;
19333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaokaimport android.graphics.Paint;
20c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaokaimport android.graphics.Path;
219ad4b2a8942e1acad11d017ad1a97ad34f9b199aTadashi G. Takaokaimport android.graphics.Rect;
22c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaokaimport android.graphics.RectF;
23333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaokaimport android.os.SystemClock;
24333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
25333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaokaimport com.android.inputmethod.latin.Constants;
26333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaokaimport com.android.inputmethod.latin.R;
27333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaokaimport com.android.inputmethod.latin.ResizableIntArray;
28333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
29c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaokafinal class GesturePreviewTrail {
30c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private static final int DEFAULT_CAPACITY = GestureStrokeWithPreviewPoints.PREVIEW_CAPACITY;
31333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
32333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka    private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
33333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka    private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
34333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka    private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY);
355e3b93542d2096b0537390996c04a23013e325a5Tadashi G. Takaoka    private int mCurrentStrokeId = -1;
36f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka    // The wall time of the zero value in {@link #mEventTimes}
37f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka    private long mCurrentTimeBase;
381c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka    private int mTrailStartIndex;
39333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
40c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka    static final class Params {
41c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka        public final int mTrailColor;
42c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka        public final float mTrailStartWidth;
43c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka        public final float mTrailEndWidth;
44333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        public final int mFadeoutStartDelay;
45333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        public final int mFadeoutDuration;
46333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        public final int mUpdateInterval;
47333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
48c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka        public final int mTrailLingerDuration;
49c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka
50c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka        public Params(final TypedArray keyboardViewAttr) {
51c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka            mTrailColor = keyboardViewAttr.getColor(
52c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka                    R.styleable.KeyboardView_gesturePreviewTrailColor, 0);
53c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka            mTrailStartWidth = keyboardViewAttr.getDimension(
54c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka                    R.styleable.KeyboardView_gesturePreviewTrailStartWidth, 0.0f);
55c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka            mTrailEndWidth = keyboardViewAttr.getDimension(
56c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka                    R.styleable.KeyboardView_gesturePreviewTrailEndWidth, 0.0f);
57333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka            mFadeoutStartDelay = keyboardViewAttr.getInt(
58333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka                    R.styleable.KeyboardView_gesturePreviewTrailFadeoutStartDelay, 0);
59333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka            mFadeoutDuration = keyboardViewAttr.getInt(
60333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka                    R.styleable.KeyboardView_gesturePreviewTrailFadeoutDuration, 0);
61c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka            mTrailLingerDuration = mFadeoutStartDelay + mFadeoutDuration;
62333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka            mUpdateInterval = keyboardViewAttr.getInt(
63333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka                    R.styleable.KeyboardView_gesturePreviewTrailUpdateInterval, 0);
64333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        }
65333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka    }
66333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
67f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka    // Use this value as imaginary zero because x-coordinates may be zero.
68f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka    private static final int DOWN_EVENT_MARKER = -128;
69f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka
70333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka    private static int markAsDownEvent(final int xCoord) {
71333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        return DOWN_EVENT_MARKER - xCoord;
72333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka    }
73333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
74333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka    private static boolean isDownEventXCoord(final int xCoordOrMark) {
75333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        return xCoordOrMark <= DOWN_EVENT_MARKER;
76333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka    }
77333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
78333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka    private static int getXCoordValue(final int xCoordOrMark) {
79333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        return isDownEventXCoord(xCoordOrMark)
80333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka                ? DOWN_EVENT_MARKER - xCoordOrMark : xCoordOrMark;
81333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka    }
82333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
83c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    public void addStroke(final GestureStrokeWithPreviewPoints stroke, final long downTime) {
84333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        final int trailSize = mEventTimes.getLength();
85333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        stroke.appendPreviewStroke(mEventTimes, mXCoordinates, mYCoordinates);
86f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka        if (mEventTimes.getLength() == trailSize) {
87333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka            return;
88333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        }
89f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka        final int[] eventTimes = mEventTimes.getPrimitiveArray();
90f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka        final int strokeId = stroke.getGestureStrokeId();
91f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka        if (strokeId != mCurrentStrokeId) {
92f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka            final int elapsedTime = (int)(downTime - mCurrentTimeBase);
931c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka            for (int i = mTrailStartIndex; i < trailSize; i++) {
94f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka                // Decay the previous strokes' event times.
95333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka                eventTimes[i] -= elapsedTime;
96333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka            }
97f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka            final int[] xCoords = mXCoordinates.getPrimitiveArray();
98f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka            final int downIndex = trailSize;
99f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka            xCoords[downIndex] = markAsDownEvent(xCoords[downIndex]);
100f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka            mCurrentTimeBase = downTime - eventTimes[downIndex];
101333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka            mCurrentStrokeId = strokeId;
102333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        }
103333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka    }
104333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
105c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka    private static int getAlpha(final int elapsedTime, final Params params) {
106c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka        if (elapsedTime < params.mFadeoutStartDelay) {
107333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka            return Constants.Color.ALPHA_OPAQUE;
108333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        }
109333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        final int decreasingAlpha = Constants.Color.ALPHA_OPAQUE
110c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka                * (elapsedTime - params.mFadeoutStartDelay)
111c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka                / params.mFadeoutDuration;
112333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        return Constants.Color.ALPHA_OPAQUE - decreasingAlpha;
113333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka    }
114333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
115c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka    private static float getWidth(final int elapsedTime, final Params params) {
116c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka        return Math.max((params.mTrailLingerDuration - elapsedTime)
117c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka                * (params.mTrailStartWidth - params.mTrailEndWidth)
118c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka                / params.mTrailLingerDuration, 0.0f);
119c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka    }
120c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka
121c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    static final class WorkingSet {
122c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Input
123c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Previous point (P1) coordinates and trail radius.
124c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        public float p1x, p1y;
125c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        public float r1;
126c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Current point (P2) coordinates and trail radius.
127c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        public float p2x, p2y;
128c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        public float r2;
129c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka
130c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Output
131c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Closing point of arc at P1.
132c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        public float p1ax, p1ay;
133c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Opening point of arc at P1.
134c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        public float p1bx, p1by;
135c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Opening point of arc at P2.
136c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        public float p2ax, p2ay;
137c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Closing point of arc at P2.
138c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        public float p2bx, p2by;
139c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Start angle of the trail arcs.
140c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        public float aa;
141c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Sweep angle of the trail arc at P1.
142c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        public float a1;
143c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        public RectF arc1 = new RectF();
144c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Sweep angle of the trail arc at P2.
145c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        public float a2;
146c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        public RectF arc2 = new RectF();
147c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    }
148c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka
149c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private static final float RIGHT_ANGLE = (float)(Math.PI / 2.0d);
150c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private static final float RADIAN_TO_DEGREE = (float)(180.0d / Math.PI);
151c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka
152c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private static boolean calculatePathPoints(final WorkingSet w) {
153c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        final float dx = w.p2x - w.p1x;
154c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        final float dy = w.p2y - w.p1y;
155c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Distance of the points.
156c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        final double l = Math.hypot(dx, dy);
157c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        if (Double.compare(0.0d, l) == 0) {
158c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            return false;
159c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        }
160c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Angle of the line p1-p2
161c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        final float a = (float)Math.atan2(dy, dx);
162c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Difference of trail cap radius.
163c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        final float dr = w.r2 - w.r1;
164c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Variation of angle at trail cap.
165c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        final float ar = (float)Math.asin(dr / l);
166c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // The start angle of trail cap arc at P1.
167c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        final float aa = a - (RIGHT_ANGLE + ar);
168c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // The end angle of trail cap arc at P2.
169c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        final float ab = a + (RIGHT_ANGLE + ar);
170c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        final float cosa = (float)Math.cos(aa);
171c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        final float sina = (float)Math.sin(aa);
172c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        final float cosb = (float)Math.cos(ab);
173c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        final float sinb = (float)Math.sin(ab);
174c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.p1ax = w.p1x + w.r1 * cosa;
175c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.p1ay = w.p1y + w.r1 * sina;
176c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.p1bx = w.p1x + w.r1 * cosb;
177c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.p1by = w.p1y + w.r1 * sinb;
178c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.p2ax = w.p2x + w.r2 * cosa;
179c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.p2ay = w.p2y + w.r2 * sina;
180c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.p2bx = w.p2x + w.r2 * cosb;
181c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.p2by = w.p2y + w.r2 * sinb;
182c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.aa = aa * RADIAN_TO_DEGREE;
183c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        final float ar2degree = ar * 2.0f * RADIAN_TO_DEGREE;
184c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.a1 = -180.0f + ar2degree;
185c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.a2 = 180.0f + ar2degree;
186c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.arc1.set(w.p1x, w.p1y, w.p1x, w.p1y);
187c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.arc1.inset(-w.r1, -w.r1);
188c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.arc2.set(w.p2x, w.p2y, w.p2x, w.p2y);
189c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        w.arc2.inset(-w.r2, -w.r2);
190c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        return true;
191c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    }
192c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka
193c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private static void createPath(final Path path, final WorkingSet w) {
194c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        path.rewind();
195c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Trail cap at P1.
196c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        path.moveTo(w.p1x, w.p1y);
197c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        path.arcTo(w.arc1, w.aa, w.a1);
198c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Trail cap at P2.
199c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        path.moveTo(w.p2x, w.p2y);
200c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        path.arcTo(w.arc2, w.aa, w.a2);
201c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        // Two trapezoids connecting P1 and P2.
202c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        path.moveTo(w.p1ax, w.p1ay);
203c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        path.lineTo(w.p1x, w.p1y);
204c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        path.lineTo(w.p1bx, w.p1by);
205c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        path.lineTo(w.p2bx, w.p2by);
206c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        path.lineTo(w.p2x, w.p2y);
207c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        path.lineTo(w.p2ax, w.p2ay);
208c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka        path.close();
209c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    }
210c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka
211c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private final WorkingSet mWorkingSet = new WorkingSet();
212c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka    private final Path mPath = new Path();
213c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka
214333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka    /**
215333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka     * Draw gesture preview trail
216333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka     * @param canvas The canvas to draw the gesture preview trail
217333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka     * @param paint The paint object to be used to draw the gesture preview trail
2189ad4b2a8942e1acad11d017ad1a97ad34f9b199aTadashi G. Takaoka     * @param outBoundsRect the bounding box of this gesture trail drawing
219c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka     * @param params The drawing parameters of gesture preview trail
220333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka     * @return true if some gesture preview trails remain to be drawn
221333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka     */
2229ad4b2a8942e1acad11d017ad1a97ad34f9b199aTadashi G. Takaoka    public boolean drawGestureTrail(final Canvas canvas, final Paint paint,
2239ad4b2a8942e1acad11d017ad1a97ad34f9b199aTadashi G. Takaoka            final Rect outBoundsRect, final Params params) {
224333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        final int trailSize = mEventTimes.getLength();
225333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        if (trailSize == 0) {
226333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka            return false;
227333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        }
228333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
229333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        final int[] eventTimes = mEventTimes.getPrimitiveArray();
230333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        final int[] xCoords = mXCoordinates.getPrimitiveArray();
231333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        final int[] yCoords = mYCoordinates.getPrimitiveArray();
232f117f77766689219c3a32dafb8b7446bdf4e4ebcTadashi G. Takaoka        final int sinceDown = (int)(SystemClock.uptimeMillis() - mCurrentTimeBase);
233333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        int startIndex;
2341c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka        for (startIndex = mTrailStartIndex; startIndex < trailSize; startIndex++) {
235333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka            final int elapsedTime = sinceDown - eventTimes[startIndex];
236333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka            // Skip too old trail points.
237c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka            if (elapsedTime < params.mTrailLingerDuration) {
238333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka                break;
239333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka            }
240333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        }
2411c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka        mTrailStartIndex = startIndex;
242333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
243333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        if (startIndex < trailSize) {
244c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka            paint.setColor(params.mTrailColor);
245c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            paint.setStyle(Paint.Style.FILL);
246c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            final Path path = mPath;
247c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            final WorkingSet w = mWorkingSet;
248c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            w.p1x = getXCoordValue(xCoords[startIndex]);
249c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            w.p1y = yCoords[startIndex];
250c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            int lastTime = sinceDown - eventTimes[startIndex];
251c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            float maxWidth = getWidth(lastTime, params);
252c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            w.r1 = maxWidth / 2.0f;
2539ad4b2a8942e1acad11d017ad1a97ad34f9b199aTadashi G. Takaoka            // Initialize bounds rectangle.
254c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka            outBoundsRect.set((int)w.p1x, (int)w.p1y, (int)w.p1x, (int)w.p1y);
255333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka            for (int i = startIndex + 1; i < trailSize - 1; i++) {
256333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka                final int elapsedTime = sinceDown - eventTimes[i];
257c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka                w.p2x = getXCoordValue(xCoords[i]);
258c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka                w.p2y = yCoords[i];
259333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka                // Draw trail line only when the current point isn't a down point.
260c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka                if (!isDownEventXCoord(xCoords[i])) {
261c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka                    final int alpha = getAlpha(elapsedTime, params);
262c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka                    paint.setAlpha(alpha);
263c1780c16a7444148dedd5471ffadbdff592df2f4Tadashi G. Takaoka                    final float width = getWidth(elapsedTime, params);
264c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka                    w.r2 = width / 2.0f;
265c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka                    if (calculatePathPoints(w)) {
266c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka                        createPath(path, w);
267c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka                        canvas.drawPath(path, paint);
268c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka                        outBoundsRect.union((int)w.p2x, (int)w.p2y);
269c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka                    }
2709ad4b2a8942e1acad11d017ad1a97ad34f9b199aTadashi G. Takaoka                    // Take union for the bounds.
2719ad4b2a8942e1acad11d017ad1a97ad34f9b199aTadashi G. Takaoka                    maxWidth = Math.max(maxWidth, width);
272333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka                }
273c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka                w.p1x = w.p2x;
274c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka                w.p1y = w.p2y;
275c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka                w.r1 = w.r2;
276c7dc673cf0fb56015826079423ced659b9180febTadashi G. Takaoka                lastTime = elapsedTime;
277333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka            }
2789ad4b2a8942e1acad11d017ad1a97ad34f9b199aTadashi G. Takaoka            // Take care of trail line width.
2799ad4b2a8942e1acad11d017ad1a97ad34f9b199aTadashi G. Takaoka            final int inset = -((int)maxWidth + 1);
2809ad4b2a8942e1acad11d017ad1a97ad34f9b199aTadashi G. Takaoka            outBoundsRect.inset(inset, inset);
281333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        }
282333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka
283333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        final int newSize = trailSize - startIndex;
2841c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka        if (newSize < startIndex) {
2851c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka            mTrailStartIndex = 0;
2861c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka            if (newSize > 0) {
2871c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka                System.arraycopy(eventTimes, startIndex, eventTimes, 0, newSize);
2881c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka                System.arraycopy(xCoords, startIndex, xCoords, 0, newSize);
2891c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka                System.arraycopy(yCoords, startIndex, yCoords, 0, newSize);
2901c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka            }
2911c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka            mEventTimes.setLength(newSize);
2921c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka            mXCoordinates.setLength(newSize);
2931c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka            mYCoordinates.setLength(newSize);
2941c2f33223995d8a6c069a85655f790388cd4e581Tadashi G. Takaoka        }
295333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka        return newSize > 0;
296333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka    }
297333a300586c3bedb3d51524642b542cefaa1a22dTadashi G. Takaoka}
298