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

Commit dc90d0a1 authored by Tadashi G. Takaoka's avatar Tadashi G. Takaoka
Browse files

Refactoring keyboard drawing code and KeyDetector

Change-Id: I55009bf38b1422301223bd90463f837562559dc5
parent ffca7630
Loading
Loading
Loading
Loading
+112 −26
Original line number Diff line number Diff line
@@ -16,35 +16,35 @@

package com.android.inputmethod.keyboard;

import android.util.Log;

import java.util.Arrays;
import java.util.List;

public abstract class KeyDetector {
    public static final int NOT_A_KEY = -1;
    public static final int NOT_A_CODE = -1;

    protected Keyboard mKeyboard;

    private Key[] mKeys;
public class KeyDetector {
    private static final String TAG = KeyDetector.class.getSimpleName();
    private static final boolean DEBUG = false;

    protected int mCorrectionX;

    protected int mCorrectionY;
    public static final int NOT_A_CODE = -1;
    public static final int NOT_A_KEY = -1;

    protected boolean mProximityCorrectOn;
    private Keyboard mKeyboard;
    private int mCorrectionX;
    private int mCorrectionY;
    private boolean mProximityCorrectOn;
    private int mProximityThresholdSquare;

    protected int mProximityThresholdSquare;
    // working area
    private static final int MAX_NEARBY_KEYS = 12;
    private final int[] mDistances = new int[MAX_NEARBY_KEYS];
    private final int[] mIndices = new int[MAX_NEARBY_KEYS];

    public Key[] setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
    public void setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
        if (keyboard == null)
            throw new NullPointerException();
        mCorrectionX = (int)correctionX;
        mCorrectionY = (int)correctionY;
        mKeyboard = keyboard;
        List<Key> keys = mKeyboard.getKeys();
        Key[] array = keys.toArray(new Key[keys.size()]);
        mKeys = array;
        return array;
    }

    protected int getTouchX(int x) {
@@ -55,11 +55,11 @@ public abstract class KeyDetector {
        return y + mCorrectionY;
    }

    protected Key[] getKeys() {
        if (mKeys == null)
    protected List<Key> getKeys() {
        if (mKeyboard == null)
            throw new IllegalStateException("keyboard isn't set");
        // mKeyboard is guaranteed not to be null at setKeybaord() method if mKeys is not null
        return mKeys;
        return mKeyboard.getKeys();
    }

    public void setProximityCorrectionEnabled(boolean enabled) {
@@ -74,6 +74,17 @@ public abstract class KeyDetector {
        mProximityThresholdSquare = threshold * threshold;
    }

    /**
     * Computes maximum size of the array that can contain all nearby key indices returned by
     * {@link #getKeyIndexAndNearbyCodes}.
     *
     * @return Returns maximum size of the array that can contain all nearby key indices returned
     *         by {@link #getKeyIndexAndNearbyCodes}.
     */
    protected int getMaxNearbyKeys() {
        return MAX_NEARBY_KEYS;
    }

    /**
     * Allocates array that can hold all key indices returned by {@link #getKeyIndexAndNearbyCodes}
     * method. The maximum size of the array should be computed by {@link #getMaxNearbyKeys}.
@@ -88,14 +99,60 @@ public abstract class KeyDetector {
        return codes;
    }

    private void initializeNearbyKeys() {
        Arrays.fill(mDistances, Integer.MAX_VALUE);
        Arrays.fill(mIndices, NOT_A_KEY);
    }

    /**
     * Computes maximum size of the array that can contain all nearby key indices returned by
     * {@link #getKeyIndexAndNearbyCodes}.
     * Insert the key into nearby keys buffer and sort nearby keys by ascending order of distance.
     *
     * @return Returns maximum size of the array that can contain all nearby key indices returned
     *         by {@link #getKeyIndexAndNearbyCodes}.
     * @param keyIndex index of the key.
     * @param distance distance between the key's edge and user touched point.
     * @return order of the key in the nearby buffer, 0 if it is the nearest key.
     */
    abstract protected int getMaxNearbyKeys();
    private int sortNearbyKeys(int keyIndex, int distance) {
        final int[] distances = mDistances;
        final int[] indices = mIndices;
        for (int insertPos = 0; insertPos < distances.length; insertPos++) {
            if (distance < distances[insertPos]) {
                final int nextPos = insertPos + 1;
                if (nextPos < distances.length) {
                    System.arraycopy(distances, insertPos, distances, nextPos,
                            distances.length - nextPos);
                    System.arraycopy(indices, insertPos, indices, nextPos,
                            indices.length - nextPos);
                }
                distances[insertPos] = distance;
                indices[insertPos] = keyIndex;
                return insertPos;
            }
        }
        return distances.length;
    }

    private void getNearbyKeyCodes(final int[] allCodes) {
        final List<Key> keys = getKeys();
        final int[] indices = mIndices;

        // allCodes[0] should always have the key code even if it is a non-letter key.
        if (indices[0] == NOT_A_KEY) {
            allCodes[0] = NOT_A_CODE;
            return;
        }

        int numCodes = 0;
        for (int j = 0; j < indices.length && numCodes < allCodes.length; j++) {
            final int index = indices[j];
            if (index == NOT_A_KEY)
                break;
            final int code = keys.get(index).mCode;
            // filter out a non-letter key from nearby keys
            if (code < Keyboard.CODE_SPACE)
                continue;
            allCodes[numCodes++] = code;
        }
    }

    /**
     * Finds all possible nearby key indices around a touch event point and returns the nearest key
@@ -108,5 +165,34 @@ public abstract class KeyDetector {
     * @param allCodes All nearby key code except functional key are returned in this array
     * @return The nearest key index
     */
    abstract public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes);
    public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
        final List<Key> keys = getKeys();
        final int touchX = getTouchX(x);
        final int touchY = getTouchY(y);

        initializeNearbyKeys();
        int primaryIndex = NOT_A_KEY;
        for (final int index : mKeyboard.getNearestKeys(touchX, touchY)) {
            final Key key = keys.get(index);
            final boolean isInside = key.isInside(touchX, touchY);
            final int distance = key.squaredDistanceToEdge(touchX, touchY);
            if (isInside || (mProximityCorrectOn && distance < mProximityThresholdSquare)) {
                final int insertedPosition = sortNearbyKeys(index, distance);
                if (insertedPosition == 0 && isInside)
                    primaryIndex = index;
            }
        }

        if (allCodes != null && allCodes.length > 0) {
            getNearbyKeyCodes(allCodes);
            if (DEBUG) {
                Log.d(TAG, "x=" + x + " y=" + y
                        + " primary="
                        + (primaryIndex == NOT_A_KEY ? "none" : keys.get(primaryIndex).mCode)
                        + " codes=" + Arrays.toString(allCodes));
            }
        }

        return primaryIndex;
    }
}
+149 −145
Original line number Diff line number Diff line
@@ -106,7 +106,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {

    // Main keyboard
    private Keyboard mKeyboard;
    private Key[] mKeys;

    // Key preview popup
    private boolean mInForeground;
@@ -147,7 +146,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
    // Accessibility
    private boolean mIsAccessibilityEnabled;

    protected KeyDetector mKeyDetector = new ProximityKeyDetector();
    protected KeyDetector mKeyDetector = new KeyDetector();

    // Swipe gesture detector
    private GestureDetector mGestureDetector;
@@ -493,10 +492,10 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
        mHandler.cancelPopupPreview();
        mKeyboard = keyboard;
        LatinImeLogger.onSetKeyboard(keyboard);
        mKeys = mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
        mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
                -getPaddingTop() + mVerticalCorrection);
        for (PointerTracker tracker : mPointerTrackers) {
            tracker.setKeyboard(keyboard, mKeys, mKeyHysteresisDistance);
            tracker.setKeyboard(keyboard, mKeyHysteresisDistance);
        }
        requestLayout();
        mKeyboardChanged = true;
@@ -638,44 +637,75 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
        }
        final Canvas canvas = mCanvas;
        canvas.clipRect(mDirtyRect, Op.REPLACE);
        canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);

        if (mKeyboard == null) return;

        if (mInvalidatedKey != null && mInvalidatedKeyRect.contains(mDirtyRect)) {
            // Draw a single key.
            onBufferDrawKey(canvas, mInvalidatedKey);
        } else {
            // Draw all keys.
            for (final Key key : mKeyboard.getKeys()) {
                onBufferDrawKey(canvas, key);
            }
        }

        // TODO: Move this function to ProximityInfo for getting rid of
        // public declarations for
        // GRID_WIDTH and GRID_HEIGHT
        if (DEBUG_KEYBOARD_GRID) {
            Paint p = new Paint();
            p.setStyle(Paint.Style.STROKE);
            p.setStrokeWidth(1.0f);
            p.setColor(0x800000c0);
            int cw = (mKeyboard.getMinWidth() + mKeyboard.GRID_WIDTH - 1)
                    / mKeyboard.GRID_WIDTH;
            int ch = (mKeyboard.getHeight() + mKeyboard.GRID_HEIGHT - 1)
                    / mKeyboard.GRID_HEIGHT;
            for (int i = 0; i <= mKeyboard.GRID_WIDTH; i++)
                canvas.drawLine(i * cw, 0, i * cw, ch * mKeyboard.GRID_HEIGHT, p);
            for (int i = 0; i <= mKeyboard.GRID_HEIGHT; i++)
                canvas.drawLine(0, i * ch, cw * mKeyboard.GRID_WIDTH, i * ch, p);
        }

        // Overlay a dark rectangle to dim the keyboard
        if (mMiniKeyboardView != null) {
            mPaint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
            canvas.drawRect(0, 0, width, height, mPaint);
        }

        mInvalidatedKey = null;
        mDrawPending = false;
        mDirtyRect.setEmpty();
    }

    private void onBufferDrawKey(final Canvas canvas, final Key key) {
        final Paint paint = mPaint;
        final Drawable keyBackground = mKeyBackground;
        final Rect padding = mPadding;
        final int kbdPaddingLeft = getPaddingLeft();
        final int kbdPaddingTop = getPaddingTop();
        final Key[] keys = mKeys;
        final int keyDrawX = key.mX + key.mVisualInsetsLeft;
        final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
        final int rowHeight = padding.top + key.mHeight;
        final boolean isManualTemporaryUpperCase = mKeyboard.isManualTemporaryUpperCase();
        final boolean drawSingleKey = (mInvalidatedKey != null
                && mInvalidatedKeyRect.contains(mDirtyRect));

        canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
        final int keyCount = keys.length;
        for (int i = 0; i < keyCount; i++) {
            final Key key = keys[i];
            if (drawSingleKey && key != mInvalidatedKey) {
                continue;
            }
            int[] drawableState = key.getCurrentDrawableState();
            keyBackground.setState(drawableState);

            // Switch the character to uppercase if shift is pressed
            String label = key.mLabel == null? null : adjustCase(key.mLabel).toString();
        canvas.translate(keyDrawX + kbdPaddingLeft, key.mY + kbdPaddingTop);

            final int keyDrawX = key.mX + key.mVisualInsetsLeft;
            final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
        // Draw key background.
        final int[] drawableState = key.getCurrentDrawableState();
        keyBackground.setState(drawableState);
        final Rect bounds = keyBackground.getBounds();
        if (keyDrawWidth != bounds.right || key.mHeight != bounds.bottom) {
            keyBackground.setBounds(0, 0, keyDrawWidth, key.mHeight);
        }
            canvas.translate(keyDrawX + kbdPaddingLeft, key.mY + kbdPaddingTop);
        keyBackground.draw(canvas);

            final int rowHeight = padding.top + key.mHeight;
            // Draw key label
            if (label != null) {
        // Draw key label.
        if (key.mLabel != null) {
            // Switch the character to uppercase if shift is pressed
            final String label = key.mLabel == null ? null : adjustCase(key.mLabel).toString();
            // For characters, use large font. For labels like "Done", use small font.
            final int labelSize = getLabelSizeAndSetPaint(label, key.mLabelOption, paint);
            final int labelCharHeight = getLabelCharHeight(labelSize, paint);
@@ -683,15 +713,13 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
            // Vertical label text alignment.
            final float baseline;
            if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_BOTTOM) != 0) {
                    baseline = key.mHeight -
                            + labelCharHeight * KEY_LABEL_VERTICAL_PADDING_FACTOR;
                baseline = key.mHeight - labelCharHeight * KEY_LABEL_VERTICAL_PADDING_FACTOR;
                if (DEBUG_SHOW_ALIGN)
                    drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
                            new Paint());
            } else { // Align center
                final float centerY = (key.mHeight + padding.top - padding.bottom) / 2;
                    baseline = centerY
                            + labelCharHeight * KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER;
                baseline = centerY + labelCharHeight * KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER;
                if (DEBUG_SHOW_ALIGN)
                    drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000,
                            new Paint());
@@ -732,14 +760,14 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
            // Turn off drop shadow
            paint.setShadowLayer(0, 0, 0, 0);
        }
            // Draw key icon

        // Draw key icon.
        final Drawable icon = key.getIcon();
        if (key.mLabel == null && icon != null) {
            final int drawableWidth = icon.getIntrinsicWidth();
            final int drawableHeight = icon.getIntrinsicHeight();
            final int drawableX;
                final int drawableY = (
                        key.mHeight + padding.top - padding.bottom - drawableHeight) / 2;
            final int drawableY = (key.mHeight + padding.top - padding.bottom - drawableHeight) / 2;
            if ((key.mLabelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) {
                drawableX = padding.left + mKeyLabelHorizontalPadding;
                if (DEBUG_SHOW_ALIGN)
@@ -761,6 +789,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
                drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
                        0x80c00000, new Paint());
        }

        // Draw hint icon.
        if (key.mHintIcon != null) {
            final int drawableWidth = keyDrawWidth;
            final int drawableHeight = key.mHeight;
@@ -774,33 +804,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
                drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight,
                        0x80c0c000, new Paint());
        }
            canvas.translate(-keyDrawX - kbdPaddingLeft, -key.mY - kbdPaddingTop);
        }

        // TODO: Move this function to ProximityInfo for getting rid of public declarations for
        // GRID_WIDTH and GRID_HEIGHT
        if (DEBUG_KEYBOARD_GRID) {
            Paint p = new Paint();
            p.setStyle(Paint.Style.STROKE);
            p.setStrokeWidth(1.0f);
            p.setColor(0x800000c0);
            int cw = (mKeyboard.getMinWidth() + mKeyboard.GRID_WIDTH - 1) / mKeyboard.GRID_WIDTH;
            int ch = (mKeyboard.getHeight() + mKeyboard.GRID_HEIGHT - 1) / mKeyboard.GRID_HEIGHT;
            for (int i = 0; i <= mKeyboard.GRID_WIDTH; i++)
                canvas.drawLine(i * cw, 0, i * cw, ch * mKeyboard.GRID_HEIGHT, p);
            for (int i = 0; i <= mKeyboard.GRID_HEIGHT; i++)
                canvas.drawLine(0, i * ch, cw * mKeyboard.GRID_WIDTH, i * ch, p);
        }

        // Overlay a dark rectangle to dim the keyboard
        if (mMiniKeyboardView != null) {
            paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
            canvas.drawRect(0, 0, width, height, paint);
        }

        mInvalidatedKey = null;
        mDrawPending = false;
        mDirtyRect.setEmpty();
        canvas.translate(-keyDrawX - kbdPaddingLeft, -key.mY - kbdPaddingTop);
    }

    public int getLabelSizeAndSetPaint(CharSequence label, int keyLabelOption, Paint paint) {
@@ -1179,15 +1184,14 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {

    private PointerTracker getPointerTracker(final int id) {
        final ArrayList<PointerTracker> pointers = mPointerTrackers;
        final Key[] keys = mKeys;
        final KeyboardActionListener listener = mKeyboardActionListener;

        // Create pointer trackers until we can get 'id+1'-th tracker, if needed.
        for (int i = pointers.size(); i <= id; i++) {
            final PointerTracker tracker =
                new PointerTracker(i, mHandler, mKeyDetector, this, getResources());
            if (keys != null)
                tracker.setKeyboard(mKeyboard, keys, mKeyHysteresisDistance);
            if (mKeyboard != null)
                tracker.setKeyboard(mKeyboard, mKeyHysteresisDistance);
            if (listener != null)
                tracker.setOnKeyboardActionListener(listener);
            pointers.add(tracker);
+8 −7
Original line number Diff line number Diff line
@@ -16,9 +16,9 @@

package com.android.inputmethod.keyboard;

public class MiniKeyboardKeyDetector extends KeyDetector {
    private static final int MAX_NEARBY_KEYS = 1;
import java.util.List;

public class MiniKeyboardKeyDetector extends KeyDetector {
    private final int mSlideAllowanceSquare;
    private final int mSlideAllowanceSquareTop;

@@ -31,20 +31,21 @@ public class MiniKeyboardKeyDetector extends KeyDetector {

    @Override
    protected int getMaxNearbyKeys() {
        return MAX_NEARBY_KEYS;
        // No nearby key will be returned.
        return 1;
    }

    @Override
    public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
        final Key[] keys = getKeys();
        final List<Key> keys = getKeys();
        final int touchX = getTouchX(x);
        final int touchY = getTouchY(y);

        int nearestIndex = NOT_A_KEY;
        int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
        final int keyCount = keys.length;
        final int keyCount = keys.size();
        for (int index = 0; index < keyCount; index++) {
            final int dist = keys[index].squaredDistanceToEdge(touchX, touchY);
            final int dist = keys.get(index).squaredDistanceToEdge(touchX, touchY);
            if (dist < nearestDist) {
                nearestIndex = index;
                nearestDist = dist;
@@ -52,7 +53,7 @@ public class MiniKeyboardKeyDetector extends KeyDetector {
        }

        if (allCodes != null && nearestIndex != NOT_A_KEY)
            allCodes[0] = keys[nearestIndex].mCode;
            allCodes[0] = keys.get(nearestIndex).mCode;
        return nearestIndex;
    }
}
+16 −19
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.util.Log;
import android.view.MotionEvent;

import java.util.Arrays;
import java.util.List;

public class PointerTracker {
    private static final String TAG = PointerTracker.class.getSimpleName();
@@ -63,7 +64,7 @@ public class PointerTracker {
    private final int mTouchNoiseThresholdDistanceSquared;

    private Keyboard mKeyboard;
    private Key[] mKeys;
    private List<Key> mKeys;
    private int mKeyHysteresisDistanceSquared = -1;
    private int mKeyQuarterWidthSquared;

@@ -109,8 +110,8 @@ public class PointerTracker {
        public void onSwipeDown() {}
    };

    public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, UIProxy proxy,
            Resources res) {
    public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector,
            UIProxy proxy, Resources res) {
        if (proxy == null || handler == null || keyDetector == null)
            throw new NullPointerException();
        mPointerId = id;
@@ -197,11 +198,11 @@ public class PointerTracker {
        mListener.onCancelInput();
    }

    public void setKeyboard(Keyboard keyboard, Key[] keys, float keyHysteresisDistance) {
        if (keyboard == null || keys == null || keyHysteresisDistance < 0)
    public void setKeyboard(Keyboard keyboard, float keyHysteresisDistance) {
        if (keyboard == null || keyHysteresisDistance < 0)
            throw new IllegalArgumentException();
        mKeyboard = keyboard;
        mKeys = keys;
        mKeys = keyboard.getKeys();
        mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance);
        final int keyQuarterWidth = keyboard.getKeyWidth() / 4;
        mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
@@ -214,11 +215,11 @@ public class PointerTracker {
    }

    private boolean isValidKeyIndex(int keyIndex) {
        return keyIndex >= 0 && keyIndex < mKeys.length;
        return keyIndex >= 0 && keyIndex < mKeys.size();
    }

    public Key getKey(int keyIndex) {
        return isValidKeyIndex(keyIndex) ? mKeys[keyIndex] : null;
        return isValidKeyIndex(keyIndex) ? mKeys.get(keyIndex) : null;
    }

    private static boolean isModifierCode(int primaryCode) {
@@ -258,12 +259,14 @@ public class PointerTracker {
        mPreviousKey = keyIndex;
        if (keyIndex != oldKeyIndex) {
            if (isValidKeyIndex(oldKeyIndex)) {
                mKeys[oldKeyIndex].onReleased();
                mProxy.invalidateKey(mKeys[oldKeyIndex]);
                final Key oldKey = mKeys.get(oldKeyIndex);
                oldKey.onReleased();
                mProxy.invalidateKey(oldKey);
            }
            if (isValidKeyIndex(keyIndex)) {
                mKeys[keyIndex].onPressed();
                mProxy.invalidateKey(mKeys[keyIndex]);
                final Key newKey = mKeys.get(keyIndex);
                newKey.onPressed();
                mProxy.invalidateKey(newKey);
            }
        }
    }
@@ -488,9 +491,6 @@ public class PointerTracker {
        if (!mIsRepeatableKey) {
            detectAndSendKey(keyIndex, x, y);
        }

        if (isValidKeyIndex(keyIndex))
            mProxy.invalidateKey(mKeys[keyIndex]);
    }

    public void onCancelEvent(int x, int y, long eventTime, PointerTrackerQueue queue) {
@@ -508,9 +508,6 @@ public class PointerTracker {
        mHandler.cancelPopupPreview();
        showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY);
        mIsInSlidingKeyInput = false;
        int keyIndex = mKeyState.getKeyIndex();
        if (isValidKeyIndex(keyIndex))
           mProxy.invalidateKey(mKeys[keyIndex]);
    }

    public void repeatKey(int keyIndex) {
@@ -539,7 +536,7 @@ public class PointerTracker {
        if (newKey == curKey) {
            return true;
        } else if (isValidKeyIndex(curKey)) {
            return mKeys[curKey].squaredDistanceToEdge(x, y) < mKeyHysteresisDistanceSquared;
            return mKeys.get(curKey).squaredDistanceToEdge(x, y) < mKeyHysteresisDistanceSquared;
        } else {
            return false;
        }
+0 −124

File deleted.

Preview size limit exceeded, changes collapsed.

Loading