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