Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4daf32b6 authored by Tom Ouyang's avatar Tom Ouyang
Browse files

Add gesture trail feedback.

Change-Id: I32709fac0dec3165678a052aa286e2fb3d90721b
parent 48ded4e3
Loading
Loading
Loading
Loading
+62 −7
Original line number Original line Diff line number Diff line
@@ -38,6 +38,7 @@ import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.TextView;


import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
@@ -94,7 +95,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
    // The maximum key label width in the proportion to the key width.
    // The maximum key label width in the proportion to the key width.
    private static final float MAX_LABEL_RATIO = 0.90f;
    private static final float MAX_LABEL_RATIO = 0.90f;


    public final static int ALPHA_OPAQUE = 255;
    private final static int GESTURE_DRAWING_WIDTH = 5;
    private final static int GESTURE_DRAWING_COLOR = 0xff33b5e5;


    // Main keyboard
    // Main keyboard
    private Keyboard mKeyboard;
    private Keyboard mKeyboard;
@@ -118,11 +120,14 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
    private final HashSet<Key> mInvalidatedKeys = new HashSet<Key>();
    private final HashSet<Key> mInvalidatedKeys = new HashSet<Key>();
    /** The region of invalidated keys */
    /** The region of invalidated keys */
    private final Rect mInvalidatedKeysRect = new Rect();
    private final Rect mInvalidatedKeysRect = new Rect();
    /** The region of invalidated gestures */
    private final Rect mInvalidatedGesturesRect = new Rect();
    /** The keyboard bitmap buffer for faster updates */
    /** The keyboard bitmap buffer for faster updates */
    private Bitmap mBuffer;
    private Bitmap mBuffer;
    /** The canvas for the above mutable keyboard bitmap */
    /** The canvas for the above mutable keyboard bitmap */
    private Canvas mCanvas;
    private Canvas mCanvas;
    private final Paint mPaint = new Paint();
    private final Paint mPaint = new Paint();
    private final Paint mGesturePaint = new Paint();
    private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics();
    private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics();
    // This sparse array caches key label text height in pixel indexed by key label text size.
    // This sparse array caches key label text height in pixel indexed by key label text size.
    private static final SparseArray<Float> sTextHeightCache = new SparseArray<Float>();
    private static final SparseArray<Float> sTextHeightCache = new SparseArray<Float>();
@@ -264,7 +269,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {


        public void blendAlpha(Paint paint) {
        public void blendAlpha(Paint paint) {
            final int color = paint.getColor();
            final int color = paint.getColor();
            paint.setARGB((paint.getAlpha() * mAnimAlpha) / ALPHA_OPAQUE,
            paint.setARGB((paint.getAlpha() * mAnimAlpha) / Constants.Color.ALPHA_OPAQUE,
                    Color.red(color), Color.green(color), Color.blue(color));
                    Color.red(color), Color.green(color), Color.blue(color));
        }
        }
    }
    }
@@ -372,6 +377,13 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
        mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout;
        mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout;


        mPaint.setAntiAlias(true);
        mPaint.setAntiAlias(true);

        // TODO: These paint parameters should be specified via attribute of the view and styleable.
        mGesturePaint.setAntiAlias(true);
        mGesturePaint.setStyle(Paint.Style.STROKE);
        mGesturePaint.setStrokeJoin(Paint.Join.ROUND);
        mGesturePaint.setColor(GESTURE_DRAWING_COLOR);
        mGesturePaint.setStrokeWidth(GESTURE_DRAWING_WIDTH);
    }
    }


    // Read fraction value in TypedArray as float.
    // Read fraction value in TypedArray as float.
@@ -517,7 +529,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
        final int keyDrawY = key.mY + getPaddingTop();
        final int keyDrawY = key.mY + getPaddingTop();
        canvas.translate(keyDrawX, keyDrawY);
        canvas.translate(keyDrawX, keyDrawY);


        params.mAnimAlpha = ALPHA_OPAQUE;
        params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE;
        if (!key.isSpacer()) {
        if (!key.isSpacer()) {
            onDrawKeyBackground(key, canvas, params);
            onDrawKeyBackground(key, canvas, params);
        }
        }
@@ -860,15 +872,58 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
        mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker);
        mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker);
    }
    }


    private static class PreviewView extends RelativeLayout {
        KeyPreviewDrawParams mParams;
        Paint mGesturePaint;

        public PreviewView(Context context, KeyPreviewDrawParams params, Paint gesturePaint) {
            super(context);
            setWillNotDraw(false);
            mParams = params;
            mGesturePaint = gesturePaint;
        }

        @Override
        public void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.translate(mParams.mCoordinates[0], mParams.mCoordinates[1]);
            PointerTracker.drawGestureTrailForAllPointerTrackers(canvas, mGesturePaint);
        }
    }

    private void addKeyPreview(TextView keyPreview) {
    private void addKeyPreview(TextView keyPreview) {
        if (mPreviewPlacer == null) {
        if (mPreviewPlacer == null) {
            mPreviewPlacer = new RelativeLayout(getContext());
            createPreviewPlacer();
        }
        mPreviewPlacer.addView(
                keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacer, 0, 0));
    }

    private void createPreviewPlacer() {
        mPreviewPlacer = new PreviewView(getContext(), mKeyPreviewDrawParams, mGesturePaint);
        final ViewGroup windowContentView =
        final ViewGroup windowContentView =
                (ViewGroup)getRootView().findViewById(android.R.id.content);
                (ViewGroup)getRootView().findViewById(android.R.id.content);
        windowContentView.addView(mPreviewPlacer);
        windowContentView.addView(mPreviewPlacer);
    }
    }
        mPreviewPlacer.addView(

                keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacer, 0, 0));
    @Override
    public void showGestureTrail(PointerTracker tracker) {
        if (mPreviewPlacer == null) {
            createPreviewPlacer();
        }
        final Rect r = tracker.getDrawingRect();
        if (!r.isEmpty()) {
            // Invalidate the rectangular region encompassing the gesture. This is needed because
            // past points along the gesture will fade and gradually disappear.
            final KeyPreviewDrawParams params = mKeyPreviewDrawParams;
            mInvalidatedGesturesRect.set(r.left + params.mCoordinates[0] - GESTURE_DRAWING_WIDTH,
                    r.top + params.mCoordinates[1] - GESTURE_DRAWING_WIDTH,
                    r.right + params.mCoordinates[0] + GESTURE_DRAWING_WIDTH,
                    r.bottom + params.mCoordinates[1] + GESTURE_DRAWING_WIDTH);
            mPreviewPlacer.invalidate(mInvalidatedGesturesRect);
        } else {
            mPreviewPlacer.invalidate();
        }
    }
    }


    @SuppressWarnings("deprecation") // setBackgroundDrawable is replaced by setBackground in API16
    @SuppressWarnings("deprecation") // setBackgroundDrawable is replaced by setBackground in API16
+7 −5
Original line number Original line Diff line number Diff line
@@ -43,6 +43,7 @@ import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.R;
@@ -82,7 +83,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
    private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
    private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
    private boolean mNeedsToDisplayLanguage;
    private boolean mNeedsToDisplayLanguage;
    private boolean mHasMultipleEnabledIMEsOrSubtypes;
    private boolean mHasMultipleEnabledIMEsOrSubtypes;
    private int mLanguageOnSpacebarAnimAlpha = ALPHA_OPAQUE;
    private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
    private final float mSpacebarTextRatio;
    private final float mSpacebarTextRatio;
    private float mSpacebarTextSize;
    private float mSpacebarTextSize;
    private final int mSpacebarTextColor;
    private final int mSpacebarTextColor;
@@ -98,7 +99,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
    // Stuff to draw altCodeWhileTyping keys.
    // Stuff to draw altCodeWhileTyping keys.
    private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
    private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
    private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
    private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
    private int mAltCodeKeyWhileTypingAnimAlpha = ALPHA_OPAQUE;
    private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;


    // More keys keyboard
    // More keys keyboard
    private PopupWindow mMoreKeysWindow;
    private PopupWindow mMoreKeysWindow;
@@ -361,7 +362,8 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
        mSpacebarTextShadowColor = a.getColor(
        mSpacebarTextShadowColor = a.getColor(
                R.styleable.LatinKeyboardView_spacebarTextShadowColor, 0);
                R.styleable.LatinKeyboardView_spacebarTextShadowColor, 0);
        mLanguageOnSpacebarFinalAlpha = a.getInt(
        mLanguageOnSpacebarFinalAlpha = a.getInt(
                R.styleable.LatinKeyboardView_languageOnSpacebarFinalAlpha, ALPHA_OPAQUE);
                R.styleable.LatinKeyboardView_languageOnSpacebarFinalAlpha,
                Constants.Color.ALPHA_OPAQUE);
        final int languageOnSpacebarFadeoutAnimatorResId = a.getResourceId(
        final int languageOnSpacebarFadeoutAnimatorResId = a.getResourceId(
                R.styleable.LatinKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
                R.styleable.LatinKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
        final int altCodeKeyWhileTypingFadeoutAnimatorResId = a.getResourceId(
        final int altCodeKeyWhileTypingFadeoutAnimatorResId = a.getResourceId(
@@ -468,7 +470,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke


        mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE);
        mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE);
        mSpaceIcon = (mSpaceKey != null)
        mSpaceIcon = (mSpaceKey != null)
                ? mSpaceKey.getIcon(keyboard.mIconsSet, ALPHA_OPAQUE) : null;
                ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null;
        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
        mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
        mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
        if (ProductionFlag.IS_EXPERIMENTAL) {
        if (ProductionFlag.IS_EXPERIMENTAL) {
@@ -870,7 +872,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
            mNeedsToDisplayLanguage = false;
            mNeedsToDisplayLanguage = false;
        } else {
        } else {
            if (subtypeChanged && needsToDisplayLanguage) {
            if (subtypeChanged && needsToDisplayLanguage) {
                setLanguageOnSpacebarAnimAlpha(ALPHA_OPAQUE);
                setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE);
                if (animator.isStarted()) {
                if (animator.isStarted()) {
                    animator.cancel();
                    animator.cancel();
                }
                }
+18 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,9 @@


package com.android.inputmethod.keyboard;
package com.android.inputmethod.keyboard;


import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.SystemClock;
import android.os.SystemClock;
import android.util.Log;
import android.util.Log;
import android.view.MotionEvent;
import android.view.MotionEvent;
@@ -76,6 +79,7 @@ public class PointerTracker {
        public TextView inflateKeyPreviewText();
        public TextView inflateKeyPreviewText();
        public void showKeyPreview(PointerTracker tracker);
        public void showKeyPreview(PointerTracker tracker);
        public void dismissKeyPreview(PointerTracker tracker);
        public void dismissKeyPreview(PointerTracker tracker);
        public void showGestureTrail(PointerTracker tracker);
    }
    }


    public interface TimerProxy {
    public interface TimerProxy {
@@ -283,6 +287,15 @@ public class PointerTracker {
        sAggregratedPointers.reset();
        sAggregratedPointers.reset();
    }
    }


    // TODO: To handle multi-touch gestures we may want to move this method to
    // {@link PointerTrackerQueue}.
    public static void drawGestureTrailForAllPointerTrackers(Canvas canvas, Paint paint) {
        for (final PointerTracker tracker : sTrackers) {
            tracker.mGestureStroke.drawGestureTrail(canvas, paint, tracker.getLastX(),
                    tracker.getLastY());
        }
    }

    private PointerTracker(int id, KeyEventHandler handler) {
    private PointerTracker(int id, KeyEventHandler handler) {
        if (handler == null)
        if (handler == null)
            throw new NullPointerException();
            throw new NullPointerException();
@@ -511,6 +524,9 @@ public class PointerTracker {
    public long getDownTime() {
    public long getDownTime() {
        return mDownTime;
        return mDownTime;
    }
    }
    public Rect getDrawingRect() {
        return mGestureStroke.getDrawingRect();
    }


    private Key onDownKey(int x, int y, long eventTime) {
    private Key onDownKey(int x, int y, long eventTime) {
        mDownTime = eventTime;
        mDownTime = eventTime;
@@ -696,6 +712,7 @@ public class PointerTracker {


        if (key != null && mInGesture) {
        if (key != null && mInGesture) {
            final InputPointers batchPoints = getIncrementalBatchPoints();
            final InputPointers batchPoints = getIncrementalBatchPoints();
            mDrawingProxy.showGestureTrail(this);
            if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
            if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
                updateBatchInput(batchPoints);
                updateBatchInput(batchPoints);
            }
            }
@@ -868,6 +885,7 @@ public class PointerTracker {
                callListenerOnRelease(mCurrentKey, mCurrentKey.mCode, true);
                callListenerOnRelease(mCurrentKey, mCurrentKey.mCode, true);
                mCurrentKey = null;
                mCurrentKey = null;
            }
            }
            mDrawingProxy.showGestureTrail(this);
            return;
            return;
        }
        }
        // This event will be recognized as a regular code input. Clear unused batch points so they
        // This event will be recognized as a regular code input. Clear unused batch points so they
+38 −1
Original line number Original line Diff line number Diff line
@@ -14,8 +14,12 @@


package com.android.inputmethod.keyboard.internal;
package com.android.inputmethod.keyboard.internal;


import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.FloatMath;
import android.util.FloatMath;


import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.InputPointers;


public class GestureStroke {
public class GestureStroke {
@@ -35,6 +39,7 @@ public class GestureStroke {
    private int mMinGestureLength;
    private int mMinGestureLength;
    private int mMinGestureLengthWhileInGesture;
    private int mMinGestureLengthWhileInGesture;
    private int mMinGestureSampleLength;
    private int mMinGestureSampleLength;
    private final Rect mDrawingRect = new Rect();


    // TODO: Move some of these to resource.
    // TODO: Move some of these to resource.
    private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH = 1.0f;
    private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH = 1.0f;
@@ -47,6 +52,10 @@ public class GestureStroke {


    private static final float DOUBLE_PI = (float)(2 * Math.PI);
    private static final float DOUBLE_PI = (float)(2 * Math.PI);


    // Fade based on number of gesture samples, see MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT
    private static final int DRAWING_GESTURE_FADE_START = 10;
    private static final int DRAWING_GESTURE_FADE_RATE = 6;

    public GestureStroke(int pointerId) {
    public GestureStroke(int pointerId) {
        mPointerId = pointerId;
        mPointerId = pointerId;
        reset();
        reset();
@@ -77,6 +86,7 @@ public class GestureStroke {
        mLastIncrementalBatchSize = 0;
        mLastIncrementalBatchSize = 0;
        mLastPointTime = 0;
        mLastPointTime = 0;
        mInputPointers.reset();
        mInputPointers.reset();
        mDrawingRect.setEmpty();
    }
    }


    private void updateLastPoint(final int x, final int y, final int time) {
    private void updateLastPoint(final int x, final int y, final int time) {
@@ -94,7 +104,6 @@ public class GestureStroke {
            }
            }
            return;
            return;
        }
        }

        final int[] xCoords = mInputPointers.getXCoordinates();
        final int[] xCoords = mInputPointers.getXCoordinates();
        final int[] yCoords = mInputPointers.getYCoordinates();
        final int[] yCoords = mInputPointers.getYCoordinates();
        final int lastX = xCoords[size - 1];
        final int lastX = xCoords[size - 1];
@@ -102,6 +111,11 @@ public class GestureStroke {
        final float dist = getDistance(lastX, lastY, x, y);
        final float dist = getDistance(lastX, lastY, x, y);
        if (dist > mMinGestureSampleLength) {
        if (dist > mMinGestureSampleLength) {
            mInputPointers.addPointer(x, y, mPointerId, time);
            mInputPointers.addPointer(x, y, mPointerId, time);
            if (mDrawingRect.isEmpty()) {
                mDrawingRect.set(x - 1, y - 1, x + 1, y + 1);
            } else {
                mDrawingRect.union(x, y);
            }
            mLength += dist;
            mLength += dist;
            final float angle = getAngle(lastX, lastY, x, y);
            final float angle = getAngle(lastX, lastY, x, y);
            if (size > 1) {
            if (size > 1) {
@@ -161,4 +175,27 @@ public class GestureStroke {
        }
        }
        return diff;
        return diff;
    }
    }

    public void drawGestureTrail(Canvas canvas, Paint paint, int lastX, int lastY) {
        // TODO: These paint parameter interpolation should be tunable, possibly introduce an object
        // that implements an interface such as Paint getPaint(int step, int strokePoints)
        final int size = mInputPointers.getPointerSize();
        int[] xCoords = mInputPointers.getXCoordinates();
        int[] yCoords = mInputPointers.getYCoordinates();
        int alpha = Constants.Color.ALPHA_OPAQUE;
        for (int i = size - 1; i > 0 && alpha > 0; i--) {
            paint.setAlpha(alpha);
            if (size - i > DRAWING_GESTURE_FADE_START) {
                alpha -= DRAWING_GESTURE_FADE_RATE;
            }
            canvas.drawLine(xCoords[i - 1], yCoords[i - 1], xCoords[i], yCoords[i], paint);
            if (i == size - 1) {
                canvas.drawLine(lastX, lastY, xCoords[i], yCoords[i], paint);
            }
        }
    }

    public Rect getDrawingRect() {
        return mDrawingRect;
    }
}
}
+7 −0
Original line number Original line Diff line number Diff line
@@ -19,6 +19,13 @@ package com.android.inputmethod.latin;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.EditorInfo;


public final class Constants {
public final class Constants {
    public static final class Color {
        /**
         * The alpha value for fully opaque.
         */
        public final static int ALPHA_OPAQUE = 255;
    }

    public static final class ImeOption {
    public static final class ImeOption {
        /**
        /**
         * The private IME option used to indicate that no microphone should be shown for a given
         * The private IME option used to indicate that no microphone should be shown for a given