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

Commit 52873834 authored by Tom Ouyang's avatar Tom Ouyang Committed by Android (Google) Code Review
Browse files

Merge "Add gesture trail feedback."

parents 2f6a90ac 4daf32b6
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