Android Open Source - translationKeyboard Keyboard






From Project

Back to project page translationKeyboard.

License

The source code is released under:

GNU General Public License

If you think the Android project translationKeyboard listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2008-2009 Google Inc.// w ww .ja v a  2 s  . c  o m
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.distantshoresmedia.translationkeyboard;

import org.distantshoresmedia.translationkeyboard.R;
import org.xmlpull.v1.XmlPullParserException;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.util.Xml;
import android.util.DisplayMetrics;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.StringTokenizer;


/**
 * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
 * consists of rows of keys.
 * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
 * <pre>
 * &lt;Keyboard
 *         android:keyWidth="%10p"
 *         android:keyHeight="50px"
 *         android:horizontalGap="2px"
 *         android:verticalGap="2px" &gt;
 *     &lt;Row android:keyWidth="32px" &gt;
 *         &lt;Key android:keyLabel="A" /&gt;
 *         ...
 *     &lt;/Row&gt;
 *     ...
 * &lt;/Keyboard&gt;
 * </pre>
 * @attr ref android.R.styleable#Keyboard_keyWidth
 * @attr ref android.R.styleable#Keyboard_keyHeight
 * @attr ref android.R.styleable#Keyboard_horizontalGap
 * @attr ref android.R.styleable#Keyboard_verticalGap
 */
public class Keyboard {

    static final String TAG = "Keyboard";

    public final static char DEAD_KEY_PLACEHOLDER = 0x25cc; // dotted small circle
    public final static String DEAD_KEY_PLACEHOLDER_STRING = Character.toString(DEAD_KEY_PLACEHOLDER);

    // Keyboard XML Tags
    private static final String TAG_KEYBOARD = "Keyboard";
    private static final String TAG_ROW = "Row";
    private static final String TAG_KEY = "Key";

    public static final int EDGE_LEFT = 0x01;
    public static final int EDGE_RIGHT = 0x02;
    public static final int EDGE_TOP = 0x04;
    public static final int EDGE_BOTTOM = 0x08;

    public static final int KEYCODE_SHIFT = -1;
    public static final int KEYCODE_MODE_CHANGE = -2;
    public static final int KEYCODE_CANCEL = -3;
    public static final int KEYCODE_DONE = -4;
    public static final int KEYCODE_DELETE = -5;
    public static final int KEYCODE_ALT_SYM = -6;

    // Backwards compatible setting to avoid having to change all the kbd_qwerty files
    public static final int DEFAULT_LAYOUT_ROWS = 4;
    public static final int DEFAULT_LAYOUT_COLUMNS = 10;

    // Flag values for popup key contents. Keep in sync with strings.xml values.
    public static final int POPUP_ADD_SHIFT = 1; 
    public static final int POPUP_ADD_CASE = 2; 
    public static final int POPUP_ADD_SELF = 4; 
    public static final int POPUP_DISABLE = 256; 
    public static final int POPUP_AUTOREPEAT = 512; 

    /** Horizontal gap default for all rows */
    private float mDefaultHorizontalGap;

    private float mHorizontalPad;
    private float mVerticalPad;

    /** Default key width */
    private float mDefaultWidth;

    /** Default key height */
    private int mDefaultHeight;

    /** Default gap between rows */
    private int mDefaultVerticalGap;

    public static final int SHIFT_OFF = 0;
    public static final int SHIFT_ON = 1;
    public static final int SHIFT_LOCKED = 2;
    public static final int SHIFT_CAPS = 3;
    public static final int SHIFT_CAPS_LOCKED = 4;
    
    /** Is the keyboard in the shifted state */
    private int mShiftState = SHIFT_OFF;

    /** Key instance for the shift key, if present */
    private Key mShiftKey;
    private Key mAltKey;
    private Key mCtrlKey;
    private Key mMetaKey;

    /** Key index for the shift key, if present */
    private int mShiftKeyIndex = -1;

    /** Total height of the keyboard, including the padding and keys */
    private int mTotalHeight;

    /**
     * Total width of the keyboard, including left side gaps and keys, but not any gaps on the
     * right side.
     */
    private int mTotalWidth;

    /** List of keys in this keyboard */
    private List<Key> mKeys;

    /** List of modifier keys such as Shift & Alt, if any */
    private List<Key> mModifierKeys;

    /** Width of the screen available to fit the keyboard */
    private int mDisplayWidth;

    /** Height of the screen and keyboard */
    private int mDisplayHeight;
    private int mKeyboardHeight;

    /** Keyboard mode, or zero, if none.  */
    private int mKeyboardMode;
    
    private boolean mUseExtension;

    public int mLayoutRows;
    public int mLayoutColumns;
    public int mRowCount = 1;
    public int mExtensionRowCount = 0;

    // Variables for pre-computing nearest keys.
    private int mCellWidth;
    private int mCellHeight;
    private int[][] mGridNeighbors;
    private int mProximityThreshold;
    /** Number of key widths from current touch point to search for nearest keys. */
    private static float SEARCH_DISTANCE = 1.8f;

    /**
     * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
     * Some of the key size defaults can be overridden per row from what the {@link Keyboard}
     * defines.
     * @attr ref android.R.styleable#Keyboard_keyWidth
     * @attr ref android.R.styleable#Keyboard_keyHeight
     * @attr ref android.R.styleable#Keyboard_horizontalGap
     * @attr ref android.R.styleable#Keyboard_verticalGap
     * @attr ref android.R.styleable#Keyboard_Row_keyboardMode
     */
    public static class Row {
        /** Default width of a key in this row. */
        public float defaultWidth;
        /** Default height of a key in this row. */
        public int defaultHeight;
        /** Default horizontal gap between keys in this row. */
        public float defaultHorizontalGap;
        /** Vertical gap following this row. */
        public int verticalGap;

        /** The keyboard mode for this row */
        public int mode;
        
        public boolean extension;

        private Keyboard parent;

        public Row(Keyboard parent) {
            this.parent = parent;
        }

        public Row(Resources res, Keyboard parent, XmlResourceParser parser) {
            this.parent = parent;
            TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
                    R.styleable.Keyboard);
            defaultWidth = getDimensionOrFraction(a,
                    R.styleable.Keyboard_keyWidth,
                    parent.mDisplayWidth, parent.mDefaultWidth);
            defaultHeight = Math.round(getDimensionOrFraction(a,
                    R.styleable.Keyboard_keyHeight,
                    parent.mDisplayHeight, parent.mDefaultHeight));
            defaultHorizontalGap = getDimensionOrFraction(a,
                    R.styleable.Keyboard_horizontalGap,
                    parent.mDisplayWidth, parent.mDefaultHorizontalGap);
            verticalGap = Math.round(getDimensionOrFraction(a,
                    R.styleable.Keyboard_verticalGap,
                    parent.mDisplayHeight, parent.mDefaultVerticalGap));
            a.recycle();
            a = res.obtainAttributes(Xml.asAttributeSet(parser),
                    R.styleable.Keyboard_Row);
            mode = a.getResourceId(R.styleable.Keyboard_Row_keyboardMode,
                    0);
            extension = a.getBoolean(R.styleable.Keyboard_Row_extension, false);

            if (parent.mLayoutRows >= 5) {
                boolean isTop = (extension || parent.mRowCount - parent.mExtensionRowCount <= 0);
                float topScale = LatinIME.sKeyboardSettings.topRowScale;
                float scale = isTop ? topScale : 1.0f + (1.0f - topScale) / (parent.mLayoutRows - 1);
                defaultHeight = Math.round(defaultHeight * scale);
            }
            a.recycle();
        }
    }

    /**
     * Class for describing the position and characteristics of a single key in the keyboard.
     *
     * @attr ref android.R.styleable#Keyboard_keyWidth
     * @attr ref android.R.styleable#Keyboard_keyHeight
     * @attr ref android.R.styleable#Keyboard_horizontalGap
     * @attr ref android.R.styleable#Keyboard_Key_codes
     * @attr ref android.R.styleable#Keyboard_Key_keyIcon
     * @attr ref android.R.styleable#Keyboard_Key_keyLabel
     * @attr ref android.R.styleable#Keyboard_Key_iconPreview
     * @attr ref android.R.styleable#Keyboard_Key_isSticky
     * @attr ref android.R.styleable#Keyboard_Key_isRepeatable
     * @attr ref android.R.styleable#Keyboard_Key_isModifier
     * @attr ref android.R.styleable#Keyboard_Key_popupKeyboard
     * @attr ref android.R.styleable#Keyboard_Key_popupCharacters
     * @attr ref android.R.styleable#Keyboard_Key_keyOutputText
     */
    public static class Key {
        /**
         * All the key codes (unicode or custom code) that this key could generate, zero'th
         * being the most important.
         */
        public int[] codes;

        /** Label to display */
        public CharSequence label;
        public CharSequence shiftLabel;
        public CharSequence capsLabel;

        /** Icon to display instead of a label. Icon takes precedence over a label */
        public Drawable icon;
        /** Preview version of the icon, for the preview popup */
        public Drawable iconPreview;
        /** Width of the key, not including the gap */
        public int width;
        /** Height of the key, not including the gap */
        private float realWidth;
        public int height;
        /** The horizontal gap before this key */
        public int gap;
        private float realGap;
        /** Whether this key is sticky, i.e., a toggle key */
        public boolean sticky;
        /** X coordinate of the key in the keyboard layout */
        public int x;
        private float realX;
        /** Y coordinate of the key in the keyboard layout */
        public int y;
        /** The current pressed state of this key */
        public boolean pressed;
        /** If this is a sticky key, is it on or locked? */
        public boolean on;
        public boolean locked;
        /** Text to output when pressed. This can be multiple characters, like ".com" */
        public CharSequence text;
        /** Popup characters */
        public CharSequence popupCharacters;
        public boolean popupReversed;
        public boolean isCursor;
        public String hint; // Set by LatinKeyboardBaseView
        public String altHint; // Set by LatinKeyboardBaseView

        /**
         * Flags that specify the anchoring to edges of the keyboard for detecting touch events
         * that are just out of the boundary of the key. This is a bit mask of
         * {@link Keyboard#EDGE_LEFT}, {@link Keyboard#EDGE_RIGHT}, {@link Keyboard#EDGE_TOP} and
         * {@link Keyboard#EDGE_BOTTOM}.
         */
        public int edgeFlags;
        /** Whether this is a modifier key, such as Shift or Alt */
        public boolean modifier;
        /** The keyboard that this key belongs to */
        private Keyboard keyboard;
        /**
         * If this key pops up a mini keyboard, this is the resource id for the XML layout for that
         * keyboard.
         */
        public int popupResId;
        /** Whether this key repeats itself when held down */
        public boolean repeatable;
        /** Is the shifted character the uppercase equivalent of the unshifted one? */
        private boolean isSimpleUppercase;
        /** Is the shifted character a distinct uppercase char that's different from the shifted char? */
        private boolean isDistinctUppercase;

        private final static int[] KEY_STATE_NORMAL_ON = {
            android.R.attr.state_checkable,
            android.R.attr.state_checked
        };

        private final static int[] KEY_STATE_PRESSED_ON = {
            android.R.attr.state_pressed,
            android.R.attr.state_checkable,
            android.R.attr.state_checked
        };

        private final static int[] KEY_STATE_NORMAL_LOCK = {
            android.R.attr.state_active,
            android.R.attr.state_checkable,
            android.R.attr.state_checked
        };

        private final static int[] KEY_STATE_PRESSED_LOCK = {
            android.R.attr.state_active,
            android.R.attr.state_pressed,
            android.R.attr.state_checkable,
            android.R.attr.state_checked
        };

        private final static int[] KEY_STATE_NORMAL_OFF = {
            android.R.attr.state_checkable
        };

        private final static int[] KEY_STATE_PRESSED_OFF = {
            android.R.attr.state_pressed,
            android.R.attr.state_checkable
        };

        private final static int[] KEY_STATE_NORMAL = {
        };

        private final static int[] KEY_STATE_PRESSED = {
            android.R.attr.state_pressed
        };

        /** Create an empty key with no attributes. */
        public Key(Row parent) {
            keyboard = parent.parent;
            height = parent.defaultHeight;
            width = Math.round(parent.defaultWidth);
            realWidth = parent.defaultWidth;
            gap = Math.round(parent.defaultHorizontalGap);
            realGap = parent.defaultHorizontalGap;
        }

        /** Create a key with the given top-left coordinate and extract its attributes from
         * the XML parser.
         * @param res resources associated with the caller's context
         * @param parent the row that this key belongs to. The row must already be attached to
         * a {@link Keyboard}.
         * @param x the x coordinate of the top-left
         * @param y the y coordinate of the top-left
         * @param parser the XML parser containing the attributes for this key
         */
        public Key(Resources res, Row parent, int x, int y, XmlResourceParser parser) {
            this(parent);

            this.x = x;
            this.y = y;

            TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
                    R.styleable.Keyboard);

            realWidth = getDimensionOrFraction(a,
                    R.styleable.Keyboard_keyWidth,
                    keyboard.mDisplayWidth, parent.defaultWidth);
            float realHeight = getDimensionOrFraction(a,
                    R.styleable.Keyboard_keyHeight,
                    keyboard.mDisplayHeight, parent.defaultHeight);
            realHeight -= parent.parent.mVerticalPad;
            height = Math.round(realHeight);
            this.y += parent.parent.mVerticalPad / 2;
            realGap = getDimensionOrFraction(a,
                    R.styleable.Keyboard_horizontalGap,
                    keyboard.mDisplayWidth, parent.defaultHorizontalGap);
            realGap += parent.parent.mHorizontalPad;
            realWidth -= parent.parent.mHorizontalPad;
            width = Math.round(realWidth);
            gap = Math.round(realGap);
            a.recycle();
            a = res.obtainAttributes(Xml.asAttributeSet(parser),
                    R.styleable.Keyboard_Key);
            this.realX = this.x + realGap - parent.parent.mHorizontalPad / 2;
            this.x = Math.round(this.realX);
            TypedValue codesValue = new TypedValue();
            a.getValue(R.styleable.Keyboard_Key_codes,
                    codesValue);
            if (codesValue.type == TypedValue.TYPE_INT_DEC
                    || codesValue.type == TypedValue.TYPE_INT_HEX) {
                codes = new int[] { codesValue.data };
            } else if (codesValue.type == TypedValue.TYPE_STRING) {
                codes = parseCSV(codesValue.string.toString());
            }

            iconPreview = a.getDrawable(R.styleable.Keyboard_Key_iconPreview);
            if (iconPreview != null) {
                iconPreview.setBounds(0, 0, iconPreview.getIntrinsicWidth(),
                        iconPreview.getIntrinsicHeight());
            }
            popupCharacters = a.getText(
                    R.styleable.Keyboard_Key_popupCharacters);
            popupResId = a.getResourceId(
                    R.styleable.Keyboard_Key_popupKeyboard, 0);
            repeatable = a.getBoolean(
                    R.styleable.Keyboard_Key_isRepeatable, false);
            modifier = a.getBoolean(
                    R.styleable.Keyboard_Key_isModifier, false);
            sticky = a.getBoolean(
                    R.styleable.Keyboard_Key_isSticky, false);
            isCursor = a.getBoolean(
                    R.styleable.Keyboard_Key_isCursor, false);

            icon = a.getDrawable(
                    R.styleable.Keyboard_Key_keyIcon);
            if (icon != null) {
                icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
            }
            label = a.getText(R.styleable.Keyboard_Key_keyLabel);
            shiftLabel = a.getText(R.styleable.Keyboard_Key_shiftLabel);
            if (shiftLabel != null && shiftLabel.length() == 0) shiftLabel = null;
            capsLabel = a.getText(R.styleable.Keyboard_Key_capsLabel);
            if (capsLabel != null && capsLabel.length() == 0) capsLabel = null;
            text = a.getText(R.styleable.Keyboard_Key_keyOutputText);

            if (codes == null && !TextUtils.isEmpty(label)) {
                codes = getFromString(label);
                if (codes != null && codes.length == 1) {
                    final Locale locale = LatinIME.sKeyboardSettings.inputLocale;
                    String upperLabel = label.toString().toUpperCase(locale);
                    if (shiftLabel == null) {
                        // No shiftLabel supplied, auto-set to uppercase if possible.
                        if (!upperLabel.equals(label.toString()) && upperLabel.length() == 1) {
                            shiftLabel = upperLabel;
                            isSimpleUppercase = true;
                        }
                    } else {
                        // Both label and shiftLabel supplied. Check if
                        // the shiftLabel is the uppercased normal label.
                        // If not, treat it as a distinct uppercase variant.
                        if (capsLabel != null) {
                            isDistinctUppercase = true;
                        } else if (upperLabel.equals(shiftLabel.toString())) {
                            isSimpleUppercase = true;
                        } else if (upperLabel.length() == 1) {
                            capsLabel = upperLabel;
                            isDistinctUppercase = true;
                        }
                    }
                }
                if ((LatinIME.sKeyboardSettings.popupKeyboardFlags & POPUP_DISABLE) != 0) {
                    popupCharacters = null;
                    popupResId = 0;
                }
                if ((LatinIME.sKeyboardSettings.popupKeyboardFlags & POPUP_AUTOREPEAT) != 0) {
                    // Assume POPUP_DISABLED is set too, otherwise things may get weird.
                    repeatable = true;
                }
            }
            //Log.i(TAG, "added key definition: " + this);
            a.recycle();
        }

        public boolean isDistinctCaps() {
            return isDistinctUppercase && keyboard.isShiftCaps();
        }

        public boolean isShifted() {
            boolean shifted = keyboard.isShifted(isSimpleUppercase);
            //Log.i(TAG, "FIXME isShifted=" + shifted + " for " + this);
            return shifted;
        }

        public int getPrimaryCode(boolean isShiftCaps, boolean isShifted) {
            if (isDistinctUppercase && isShiftCaps) {
                return capsLabel.charAt(0);
            }
            //Log.i(TAG, "getPrimaryCode(), shifted=" + shifted);
            if (isShifted && shiftLabel != null) {
                if (shiftLabel.charAt(0) == DEAD_KEY_PLACEHOLDER && shiftLabel.length() >= 2) {
                    return shiftLabel.charAt(1);
                } else {
                    return shiftLabel.charAt(0);
                }
            } else {
                return codes[0];
            }
        }

        public int getPrimaryCode() {
            return getPrimaryCode(keyboard.isShiftCaps(), keyboard.isShifted(isSimpleUppercase));
        }

        public boolean isDeadKey() {
            if (codes == null || codes.length < 1) return false;
            return Character.getType(codes[0]) == Character.NON_SPACING_MARK;
        }

        public int[] getFromString(CharSequence str) {
            if (str.length() > 1) {
                if (str.charAt(0) == DEAD_KEY_PLACEHOLDER && str.length() >= 2) {
                    return new int[] { str.charAt(1) }; // FIXME: >1 length?
                } else {
                    text = str; // TODO: add space?
                    return new int[] { 0 };
                }
            } else {
                char c = str.charAt(0);
                return new int[] { c };
            }
        }

        public String getCaseLabel() {
            if (isDistinctUppercase && keyboard.isShiftCaps()) {
                return capsLabel.toString();
            }
            boolean isShifted = keyboard.isShifted(isSimpleUppercase);
            if (isShifted && shiftLabel != null) {
                return shiftLabel.toString();
            } else {
                return label != null ? label.toString() : null;
            }
        }

        private String getPopupKeyboardContent(boolean isShiftCaps, boolean isShifted, boolean addExtra) {
            int mainChar = getPrimaryCode(false, false);
            int shiftChar = getPrimaryCode(false, true);
            int capsChar = getPrimaryCode(true, true);

            // Remove duplicates
            if (shiftChar == mainChar) shiftChar = 0;
            if (capsChar == shiftChar || capsChar == mainChar) capsChar = 0;

            int popupLen = (popupCharacters == null) ? 0 : popupCharacters.length();
            StringBuilder popup = new StringBuilder(popupLen);
            for (int i = 0; i < popupLen; ++i) {
                char c = popupCharacters.charAt(i);
                if (isShifted || isShiftCaps) {
                    String upper = Character.toString(c).toUpperCase(LatinIME.sKeyboardSettings.inputLocale);
                    if (upper.length() == 1) c = upper.charAt(0);
                }

                if (c == mainChar || c == shiftChar || c == capsChar) continue;
                popup.append(c);
            }

            if (addExtra) {
                StringBuilder extra = new StringBuilder(3 + popup.length());
                int flags = LatinIME.sKeyboardSettings.popupKeyboardFlags;
                if ((flags & POPUP_ADD_SELF) != 0) {
                    // if shifted, add unshifted key to extra, and vice versa
                    if (isDistinctUppercase && isShiftCaps) {
                        if (capsChar > 0) { extra.append((char) capsChar); capsChar = 0; }
                    } else if (isShifted) {
                        if (shiftChar > 0) { extra.append((char) shiftChar); shiftChar = 0; }
                    } else {
                        if (mainChar > 0) { extra.append((char) mainChar); mainChar = 0; }
                    }
                }

                if ((flags & POPUP_ADD_CASE) != 0) {
                    // if shifted, add unshifted key to popup, and vice versa
                    if (isDistinctUppercase && isShiftCaps) {
                        if (mainChar > 0) { extra.append((char) mainChar); mainChar = 0; }
                        if (shiftChar > 0) { extra.append((char) shiftChar); shiftChar = 0; }
                    } else if (isShifted) {
                        if (mainChar > 0) { extra.append((char) mainChar); mainChar = 0; }
                        if (capsChar > 0) { extra.append((char) capsChar); capsChar = 0; }
                    } else {
                        if (shiftChar > 0) { extra.append((char) shiftChar); shiftChar = 0; }
                        if (capsChar > 0) { extra.append((char) capsChar); capsChar = 0; }
                    }
                }

                if (!isSimpleUppercase && (flags & POPUP_ADD_SHIFT) != 0) {
                    // if shifted, add unshifted key to popup, and vice versa
                    if (isShifted) {
                        if (mainChar > 0) { extra.append((char) mainChar); mainChar = 0; }
                    } else {
                        if (shiftChar > 0) { extra.append((char) shiftChar); shiftChar = 0; }
                    }
                }

                extra.append(popup);
                return extra.toString();
            }

            return popup.toString();
        }

        public Keyboard getPopupKeyboard(Context context, int padding) {
            if (popupCharacters == null) {
                if (popupResId != 0) {
                    return new Keyboard(context, keyboard.mDefaultHeight, popupResId);
                } else {
                    if (modifier) return null; // Space, Return etc.
                }
            }

            if ((LatinIME.sKeyboardSettings.popupKeyboardFlags & POPUP_DISABLE) != 0) return null;

            String popup = getPopupKeyboardContent(keyboard.isShiftCaps(), keyboard.isShifted(isSimpleUppercase), true);
            //Log.i(TAG, "getPopupKeyboard: popup='" + popup + "' for " + this);
            if (popup.length() > 0) {
                int resId = popupResId;
                if (resId == 0) resId = R.xml.kbd_popup_template;
                return new Keyboard(context, keyboard.mDefaultHeight, resId, popup, popupReversed, -1, padding);
            } else {
                return null;
            }
        }

        public String getHintLabel(boolean wantAscii, boolean wantAll) {
            if (hint == null) {
                hint = "";
                if (shiftLabel != null && !isSimpleUppercase) {
                    char c = shiftLabel.charAt(0);
                    if (wantAll || wantAscii && is7BitAscii(c)) {
                        hint = Character.toString(c);
                    }
                }
            }
            return hint;
        }

        public String getAltHintLabel(boolean wantAscii, boolean wantAll) {
            if (altHint == null) {
                altHint = "";
                String popup = getPopupKeyboardContent(false, false, false);
                if (popup.length() > 0) {
                    char c = popup.charAt(0);
                    if (wantAll || wantAscii && is7BitAscii(c)) {
                        altHint = Character.toString(c);
                    }
                }
            }
            return altHint;
        }

        private static boolean is7BitAscii(char c) {
            if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) return false;
            return c >= 32 && c < 127;
        }
        
        /**
         * Informs the key that it has been pressed, in case it needs to change its appearance or
         * state.
         * @see #onReleased(boolean)
         */
        public void onPressed() {
            pressed = !pressed;
        }

        /**
         * Changes the pressed state of the key. Sticky key indicators are handled explicitly elsewhere.
         * @param inside whether the finger was released inside the key
         * @see #onPressed()
         */
        public void onReleased(boolean inside) {
            pressed = !pressed;
        }

        int[] parseCSV(String value) {
            int count = 0;
            int lastIndex = 0;
            if (value.length() > 0) {
                count++;
                while ((lastIndex = value.indexOf(",", lastIndex + 1)) > 0) {
                    count++;
                }
            }
            int[] values = new int[count];
            count = 0;
            StringTokenizer st = new StringTokenizer(value, ",");
            while (st.hasMoreTokens()) {
                try {
                    values[count++] = Integer.parseInt(st.nextToken());
                } catch (NumberFormatException nfe) {
                    Log.e(TAG, "Error parsing keycodes " + value);
                }
            }
            return values;
        }

        /**
         * Detects if a point falls inside this key.
         * @param x the x-coordinate of the point
         * @param y the y-coordinate of the point
         * @return whether or not the point falls inside the key. If the key is attached to an edge,
         * it will assume that all points between the key and the edge are considered to be inside
         * the key.
         */
        public boolean isInside(int x, int y) {
            boolean leftEdge = (edgeFlags & EDGE_LEFT) > 0;
            boolean rightEdge = (edgeFlags & EDGE_RIGHT) > 0;
            boolean topEdge = (edgeFlags & EDGE_TOP) > 0;
            boolean bottomEdge = (edgeFlags & EDGE_BOTTOM) > 0;
            if ((x >= this.x || (leftEdge && x <= this.x + this.width))
                    && (x < this.x + this.width || (rightEdge && x >= this.x))
                    && (y >= this.y || (topEdge && y <= this.y + this.height))
                    && (y < this.y + this.height || (bottomEdge && y >= this.y))) {
                return true;
            } else {
                return false;
            }
        }

        /**
         * Returns the square of the distance between the center of the key and the given point.
         * @param x the x-coordinate of the point
         * @param y the y-coordinate of the point
         * @return the square of the distance of the point from the center of the key
         */
        public int squaredDistanceFrom(int x, int y) {
            int xDist = this.x + width / 2 - x;
            int yDist = this.y + height / 2 - y;
            return xDist * xDist + yDist * yDist;
        }

        /**
         * Returns the drawable state for the key, based on the current state and type of the key.
         * @return the drawable state of the key.
         * @see android.graphics.drawable.StateListDrawable#setState(int[])
         */
        public int[] getCurrentDrawableState() {
            int[] states = KEY_STATE_NORMAL;

            if (locked) {
                if (pressed) {
                    states = KEY_STATE_PRESSED_LOCK;
                } else {
                    states = KEY_STATE_NORMAL_LOCK;
                }
            } else if (on) {
                if (pressed) {
                    states = KEY_STATE_PRESSED_ON;
                } else {
                    states = KEY_STATE_NORMAL_ON;
                }
            } else {
                if (sticky) {
                    if (pressed) {
                        states = KEY_STATE_PRESSED_OFF;
                    } else {
                        states = KEY_STATE_NORMAL_OFF;
                    }
                } else {
                    if (pressed) {
                        states = KEY_STATE_PRESSED;
                    }
                }
            }
            return states;
        }

        public String toString() {
            int code = (codes != null && codes.length > 0) ? codes[0] : 0;
            String edges = (
                    ((edgeFlags & Keyboard.EDGE_LEFT) != 0 ? "L" : "-") +
                    ((edgeFlags & Keyboard.EDGE_RIGHT) != 0 ? "R" : "-") +
                    ((edgeFlags & Keyboard.EDGE_TOP) != 0 ? "T" : "-") +
                    ((edgeFlags & Keyboard.EDGE_BOTTOM) != 0 ? "B" : "-"));
            return "KeyDebugFIXME(label=" + label +
                (shiftLabel != null ? " shift=" + shiftLabel : "") +
                (capsLabel != null ? " caps=" + capsLabel : "") +
                (text != null ? " text=" + text : "" ) +
                " code=" + code +
                (code <= 0 || Character.isWhitespace(code) ? "" : ":'" + (char)code + "'" ) +
                " x=" + x + ".." + (x+width) + " y=" + y + ".." + (y+height) +
                " edgeFlags=" + edges +
                (popupCharacters != null ? " pop=" + popupCharacters : "" ) +
                " res=" + popupResId +
                ")";
        }
    }

    /**
     * Creates a keyboard from the given xml key layout file.
     * @param context the application or service context
     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
     */
    public Keyboard(Context context, int defaultHeight, int xmlLayoutResId) {
        this(context, defaultHeight, xmlLayoutResId, 0);
    }

    public Keyboard(Context context, int defaultHeight, int xmlLayoutResId, int modeId) {
        this(context, defaultHeight, xmlLayoutResId, modeId, 0);
    }

    /**
     * Creates a keyboard from the given xml key layout file. Weeds out rows
     * that have a keyboard mode defined but don't match the specified mode.
     * @param context the application or service context
     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
     * @param modeId keyboard mode identifier
     * @param rowHeightPercent height of each row as percentage of screen height
     */
    public Keyboard(Context context, int defaultHeight, int xmlLayoutResId, int modeId, float kbHeightPercent) {
        DisplayMetrics dm = context.getResources().getDisplayMetrics();
        mDisplayWidth = dm.widthPixels;
        mDisplayHeight = dm.heightPixels;
        Log.v(TAG, "keyboard's display metrics:" + dm + ", mDisplayWidth=" + mDisplayWidth);

        mDefaultHorizontalGap = 0;
        mDefaultWidth = mDisplayWidth / 10;
        mDefaultVerticalGap = 0;
        mDefaultHeight = defaultHeight; // may be zero, to be adjusted below
        mKeyboardHeight = Math.round(mDisplayHeight * kbHeightPercent / 100); 
        //Log.i("PCKeyboard", "mDefaultHeight=" + mDefaultHeight + "(arg=" + defaultHeight + ")" + " kbHeight=" + mKeyboardHeight + " displayHeight="+mDisplayHeight+")");
        mKeys = new ArrayList<Key>();
        mModifierKeys = new ArrayList<Key>();
        mKeyboardMode = modeId;
        mUseExtension = LatinIME.sKeyboardSettings.useExtension;
        loadKeyboard(context, context.getResources().getXml(xmlLayoutResId));
        setEdgeFlags();
        fixAltChars(LatinIME.sKeyboardSettings.inputLocale);
    }

    /**
     * <p>Creates a blank keyboard from the given resource file and populates it with the specified
     * characters in left-to-right, top-to-bottom fashion, using the specified number of columns.
     * </p>
     * <p>If the specified number of columns is -1, then the keyboard will fit as many keys as
     * possible in each row.</p>
     * @param context the application or service context
     * @param layoutTemplateResId the layout template file, containing no keys.
     * @param characters the list of characters to display on the keyboard. One key will be created
     * for each character.
     * @param columns the number of columns of keys to display. If this number is greater than the
     * number of keys that can fit in a row, it will be ignored. If this number is -1, the
     * keyboard will fit as many keys as possible in each row.
     */
    private Keyboard(Context context, int defaultHeight, int layoutTemplateResId,
            CharSequence characters, boolean reversed, int columns, int horizontalPadding) {
        this(context, defaultHeight, layoutTemplateResId);
        int x = 0;
        int y = 0;
        int column = 0;
        mTotalWidth = 0;

        Row row = new Row(this);
        row.defaultHeight = mDefaultHeight;
        row.defaultWidth = mDefaultWidth;
        row.defaultHorizontalGap = mDefaultHorizontalGap;
        row.verticalGap = mDefaultVerticalGap;
        final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;
        mLayoutRows = 1;
        int start = reversed ? characters.length()-1 : 0;
        int end = reversed ? -1 : characters.length();
        int step = reversed ? -1 : 1;
        for (int i = start; i != end; i+=step) {
            char c = characters.charAt(i);
            if (column >= maxColumns
                    || x + mDefaultWidth + horizontalPadding > mDisplayWidth) {
                x = 0;
                y += mDefaultVerticalGap + mDefaultHeight;
                column = 0;
                ++mLayoutRows;
            }
            final Key key = new Key(row);
            key.x = x;
            key.realX = x;
            key.y = y;
            key.label = String.valueOf(c);
            key.codes = key.getFromString(key.label);
            column++;
            x += key.width + key.gap;
            mKeys.add(key);
            if (x > mTotalWidth) {
                mTotalWidth = x;
            }
        }
        mTotalHeight = y + mDefaultHeight;
        mLayoutColumns = columns == -1 ? column : maxColumns;
        setEdgeFlags();
    }

    private void setEdgeFlags() {
        if (mRowCount == 0) mRowCount = 1; // Assume one row if not set
        int row = 0;
        Key prevKey = null;
        int rowFlags = 0;
        for (Key key : mKeys) {
            int keyFlags = 0;
            if (prevKey == null || key.x <= prevKey.x) {
                // Start new row.
                if (prevKey != null) {
                    // Add "right edge" to rightmost key of previous row.
                    // Need to do the last key separately below.
                    prevKey.edgeFlags |= Keyboard.EDGE_RIGHT;
                }

                // Set the row flags for the current row.
                rowFlags = 0;
                if (row == 0) rowFlags |= Keyboard.EDGE_TOP;
                if (row == mRowCount - 1) rowFlags |= Keyboard.EDGE_BOTTOM;
                ++row;

                // Mark current key as "left edge"
                keyFlags |= Keyboard.EDGE_LEFT;
            }
            key.edgeFlags = rowFlags | keyFlags;
            prevKey = key;
        }
        // Fix up the last key
        if (prevKey != null) prevKey.edgeFlags |= Keyboard.EDGE_RIGHT;

//        Log.i(TAG, "setEdgeFlags() done:");
//        for (Key key : mKeys) {
//            Log.i(TAG, "key=" + key);
//        }
    }

    private void fixAltChars(Locale locale) {
        if (locale == null) locale = Locale.getDefault();
        Set<Character> mainKeys = new HashSet<Character>();
        for (Key key : mKeys) {
            // Remember characters on the main keyboard so that they can be removed from popups.
            // This makes it easy to share popup char maps between the normal and shifted
            // keyboards.
            if (key.label != null && !key.modifier && key.label.length() == 1) {
                char c = key.label.charAt(0);
                mainKeys.add(c);
            }
        }

        for (Key key : mKeys) {
            if (key.popupCharacters == null) continue;
            int popupLen = key.popupCharacters.length();
            if (popupLen == 0) {
                continue;
            }
            if (key.x >= mTotalWidth / 2) {
                key.popupReversed = true;
            }

            // Uppercase the alt chars if the main key is uppercase
            boolean needUpcase = key.label != null && key.label.length() == 1 && Character.isUpperCase(key.label.charAt(0));
            if (needUpcase) {
                key.popupCharacters = key.popupCharacters.toString().toUpperCase();
                popupLen = key.popupCharacters.length();
            }

            StringBuilder newPopup = new StringBuilder(popupLen);
            for (int i = 0; i < popupLen; ++i) {
                char c = key.popupCharacters.charAt(i);

                if (Character.isDigit(c) && mainKeys.contains(c)) continue;  // already present elsewhere

                // Skip extra digit alt keys on 5-row keyboards
                if ((key.edgeFlags & EDGE_TOP) == 0 && Character.isDigit(c)) continue;

                newPopup.append(c);
            }
            //Log.i("PCKeyboard", "popup for " + key.label + " '" + key.popupCharacters + "' => '"+ newPopup + "' length " + newPopup.length());

            key.popupCharacters = newPopup.toString();
        }
    }

    public List<Key> getKeys() {
        return mKeys;
    }

    public List<Key> getModifierKeys() {
        return mModifierKeys;
    }

    protected int getHorizontalGap() {
        return Math.round(mDefaultHorizontalGap);
    }

    protected void setHorizontalGap(int gap) {
        mDefaultHorizontalGap = gap;
    }

    protected int getVerticalGap() {
        return mDefaultVerticalGap;
    }

    protected void setVerticalGap(int gap) {
        mDefaultVerticalGap = gap;
    }

    protected int getKeyHeight() {
        return mDefaultHeight;
    }

    protected void setKeyHeight(int height) {
        mDefaultHeight = height;
    }

    protected int getKeyWidth() {
        return Math.round(mDefaultWidth);
    }

    protected void setKeyWidth(int width) {
        mDefaultWidth = width;
    }

    /**
     * Returns the total height of the keyboard
     * @return the total height of the keyboard
     */
    public int getHeight() {
        return mTotalHeight;
    }

    public int getScreenHeight() {
        return mDisplayHeight;
    }

    public int getMinWidth() {
        return mTotalWidth;
    }

    public boolean setShiftState(int shiftState, boolean updateKey) {
        //Log.i(TAG, "setShiftState " + mShiftState + " -> " + shiftState);
        if (updateKey && mShiftKey != null) {
            mShiftKey.on = (shiftState != SHIFT_OFF);
        }
        if (mShiftState != shiftState) {
            mShiftState = shiftState;
            return true;
        }
        return false;
    }

    public boolean setShiftState(int shiftState) {
        return setShiftState(shiftState, true);
    }
    
    public Key setCtrlIndicator(boolean active) {
        //Log.i(TAG, "setCtrlIndicator " + active + " ctrlKey=" + mCtrlKey);
        if (mCtrlKey != null) mCtrlKey.on = active;
        return mCtrlKey;
    }

    public Key setAltIndicator(boolean active) {
        if (mAltKey != null) mAltKey.on = active;
        return mAltKey;
    }

    public Key setMetaIndicator(boolean active) {
        if (mMetaKey != null) mMetaKey.on = active;
        return mMetaKey;
    }

    public boolean isShiftCaps() {
        return mShiftState == SHIFT_CAPS || mShiftState == SHIFT_CAPS_LOCKED;
    }

    public boolean isShifted(boolean applyCaps) {
        if (applyCaps) {
            return mShiftState != SHIFT_OFF;
        } else {
            return mShiftState == SHIFT_ON || mShiftState == SHIFT_LOCKED;
        }
    }

    public int getShiftState() {
        return mShiftState;
    }

    public int getShiftKeyIndex() {
        return mShiftKeyIndex;
    }

    private void computeNearestNeighbors() {
        // Round-up so we don't have any pixels outside the grid
        mCellWidth = (getMinWidth() + mLayoutColumns - 1) / mLayoutColumns;
        mCellHeight = (getHeight() + mLayoutRows - 1) / mLayoutRows;
        mGridNeighbors = new int[mLayoutColumns * mLayoutRows][];
        int[] indices = new int[mKeys.size()];
        final int gridWidth = mLayoutColumns * mCellWidth;
        final int gridHeight = mLayoutRows * mCellHeight;
        for (int x = 0; x < gridWidth; x += mCellWidth) {
            for (int y = 0; y < gridHeight; y += mCellHeight) {
                int count = 0;
                for (int i = 0; i < mKeys.size(); i++) {
                    final Key key = mKeys.get(i);
                    boolean isSpace = key.codes != null && key.codes.length > 0 &&
                        key.codes[0] == LatinIME.ASCII_SPACE;
                    if (key.squaredDistanceFrom(x, y) < mProximityThreshold ||
                            key.squaredDistanceFrom(x + mCellWidth - 1, y) < mProximityThreshold ||
                            key.squaredDistanceFrom(x + mCellWidth - 1, y + mCellHeight - 1)
                                < mProximityThreshold ||
                            key.squaredDistanceFrom(x, y + mCellHeight - 1) < mProximityThreshold ||
                            isSpace && !(
                                x + mCellWidth - 1 < key.x ||
                                x > key.x + key.width ||
                                y + mCellHeight - 1 < key.y ||
                                y > key.y + key.height)) {
                      //if (isSpace) Log.i(TAG, "space at grid" + x + "," + y);
                        indices[count++] = i;
                    }
                }
                int [] cell = new int[count];
                System.arraycopy(indices, 0, cell, 0, count);
                mGridNeighbors[(y / mCellHeight) * mLayoutColumns + (x / mCellWidth)] = cell;
            }
        }
    }

    /**
     * Returns the indices of the keys that are closest to the given point.
     * @param x the x-coordinate of the point
     * @param y the y-coordinate of the point
     * @return the array of integer indices for the nearest keys to the given point. If the given
     * point is out of range, then an array of size zero is returned.
     */
    public int[] getNearestKeys(int x, int y) {
        if (mGridNeighbors == null) computeNearestNeighbors();
        if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) {
            int index = (y / mCellHeight) * mLayoutColumns + (x / mCellWidth);
            if (index < mLayoutRows * mLayoutColumns) {
                return mGridNeighbors[index];
            }
        }
        return new int[0];
    }

    protected Row createRowFromXml(Resources res, XmlResourceParser parser) {
        return new Row(res, this, parser);
    }

    protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
            XmlResourceParser parser) {
        return new Key(res, parent, x, y, parser);
    }

    private void loadKeyboard(Context context, XmlResourceParser parser) {
        boolean inKey = false;
        boolean inRow = false;
        float x = 0;
        int y = 0;
        Key key = null;
        Row currentRow = null;
        Resources res = context.getResources();
        boolean skipRow = false;
        mRowCount = 0;

        try {
            int event;
            Key prevKey = null;
            while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
                if (event == XmlResourceParser.START_TAG) {
                    String tag = parser.getName();
                    if (TAG_ROW.equals(tag)) {
                        inRow = true;
                        x = 0;
                        currentRow = createRowFromXml(res, parser);
                        skipRow = currentRow.mode != 0 && currentRow.mode != mKeyboardMode;
                        if (currentRow.extension) {
                            if (mUseExtension) {
                                ++mExtensionRowCount;
                            } else {
                                skipRow = true;
                            }
                        }
                        if (skipRow) {
                            skipToEndOfRow(parser);
                            inRow = false;
                        }
                   } else if (TAG_KEY.equals(tag)) {
                        inKey = true;
                        key = createKeyFromXml(res, currentRow, Math.round(x), y, parser);
                        key.realX = x;
                        if (key.codes == null) {
                          // skip this key, adding its width to the previous one
                          if (prevKey != null) {
                              prevKey.width += key.width;
                          }
                        } else {
                          mKeys.add(key);
                          prevKey = key;
                          if (key.codes[0] == KEYCODE_SHIFT) {
                              if (mShiftKeyIndex == -1) {
                                  mShiftKey = key;
                                  mShiftKeyIndex = mKeys.size()-1;
                              }
                              mModifierKeys.add(key);
                          } else if (key.codes[0] == KEYCODE_ALT_SYM) {
                              mModifierKeys.add(key);
                          } else if (key.codes[0] == LatinKeyboardView.KEYCODE_CTRL_LEFT) {
                              mCtrlKey = key;
                          } else if (key.codes[0] == LatinKeyboardView.KEYCODE_ALT_LEFT) {
                              mAltKey = key;
                          } else if (key.codes[0] == LatinKeyboardView.KEYCODE_META_LEFT) {
                              mMetaKey = key;
                          }
                        }
                    } else if (TAG_KEYBOARD.equals(tag)) {
                        parseKeyboardAttributes(res, parser);
                    }
                } else if (event == XmlResourceParser.END_TAG) {
                    if (inKey) {
                        inKey = false;
                        x += key.realGap + key.realWidth;
                        if (x > mTotalWidth) {
                            mTotalWidth = Math.round(x);
                        }
                    } else if (inRow) {
                        inRow = false;
                        y += currentRow.verticalGap;
                        y += currentRow.defaultHeight;
                        mRowCount++;
                    } else {
                        // TODO: error or extend?
                    }
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Parse error:" + e);
            e.printStackTrace();
        }
        mTotalHeight = y - mDefaultVerticalGap;
    }

    public void setKeyboardWidth(int newWidth) {
        Log.i(TAG, "setKeyboardWidth newWidth=" + newWidth + ", mTotalWidth=" + mTotalWidth);
        if (newWidth <= 0) return;  // view not initialized?
        if (mTotalWidth <= newWidth) return;  // it already fits
        float scale = (float) newWidth / mDisplayWidth;
        Log.i("PCKeyboard", "Rescaling keyboard: " + mTotalWidth + " => " + newWidth);
        for (Key key : mKeys) {
            key.x = Math.round(key.realX * scale);
        }
        mTotalWidth = newWidth;
    }

    private void skipToEndOfRow(XmlResourceParser parser)
            throws XmlPullParserException, IOException {
        int event;
        while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) {
            if (event == XmlResourceParser.END_TAG
                    && parser.getName().equals(TAG_ROW)) {
                break;
            }
        }
    }

    private void parseKeyboardAttributes(Resources res, XmlResourceParser parser) {
        TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
                R.styleable.Keyboard);

        mDefaultWidth = getDimensionOrFraction(a,
                R.styleable.Keyboard_keyWidth,
                mDisplayWidth, mDisplayWidth / 10);
        mDefaultHeight = Math.round(getDimensionOrFraction(a,
                R.styleable.Keyboard_keyHeight,
                mDisplayHeight, mDefaultHeight));
        mDefaultHorizontalGap = getDimensionOrFraction(a,
                R.styleable.Keyboard_horizontalGap,
                mDisplayWidth, 0);
        mDefaultVerticalGap = Math.round(getDimensionOrFraction(a,
                R.styleable.Keyboard_verticalGap,
                mDisplayHeight, 0));
        mHorizontalPad = getDimensionOrFraction(a,
                R.styleable.Keyboard_horizontalPad,
                mDisplayWidth, res.getDimension(R.dimen.key_horizontal_pad));
        mVerticalPad = getDimensionOrFraction(a,
                R.styleable.Keyboard_verticalPad,
                mDisplayHeight, res.getDimension(R.dimen.key_vertical_pad));
        mLayoutRows = a.getInteger(R.styleable.Keyboard_layoutRows, DEFAULT_LAYOUT_ROWS);
        mLayoutColumns = a.getInteger(R.styleable.Keyboard_layoutColumns, DEFAULT_LAYOUT_COLUMNS);
        if (mDefaultHeight == 0 && mKeyboardHeight > 0 && mLayoutRows > 0) {
            mDefaultHeight = mKeyboardHeight / mLayoutRows;
            //Log.i(TAG, "got mLayoutRows=" + mLayoutRows + ", mDefaultHeight=" + mDefaultHeight);
        }
        mProximityThreshold = (int) (mDefaultWidth * SEARCH_DISTANCE);
        mProximityThreshold = mProximityThreshold * mProximityThreshold; // Square it for comparison
        a.recycle();
    }

    static float getDimensionOrFraction(TypedArray a, int index, int base, float defValue) {
        TypedValue value = a.peekValue(index);
        if (value == null) return defValue;
        if (value.type == TypedValue.TYPE_DIMENSION) {
            return a.getDimensionPixelOffset(index, Math.round(defValue));
        } else if (value.type == TypedValue.TYPE_FRACTION) {
            // Round it to avoid values like 47.9999 from getting truncated
            //return Math.round(a.getFraction(index, base, base, defValue));
            return a.getFraction(index, base, base, defValue);
        }
        return defValue;
    }

    @Override
    public String toString() {
        return "Keyboard(" + mLayoutColumns + "x" + mLayoutRows +
            " keys=" + mKeys.size() +
            " rowCount=" + mRowCount +
            " mode=" + mKeyboardMode +
            " size=" + mTotalWidth + "x" + mTotalHeight +
            ")";

    }
}




Java Source Code List

org.distantshoresmedia.translationkeyboard.AutoDictionary.java
org.distantshoresmedia.translationkeyboard.AutoSummaryEditTextPreference.java
org.distantshoresmedia.translationkeyboard.AutoSummaryListPreference.java
org.distantshoresmedia.translationkeyboard.BinaryDictionary.java
org.distantshoresmedia.translationkeyboard.CandidateView.java
org.distantshoresmedia.translationkeyboard.ComposeBase.java
org.distantshoresmedia.translationkeyboard.ComposeSequence.java
org.distantshoresmedia.translationkeyboard.ContactsDictionary.java
org.distantshoresmedia.translationkeyboard.DeadAccentSequence.java
org.distantshoresmedia.translationkeyboard.Dictionary.java
org.distantshoresmedia.translationkeyboard.EditingUtil.java
org.distantshoresmedia.translationkeyboard.ExpandableDictionary.java
org.distantshoresmedia.translationkeyboard.GlobalKeyboardSettings.java
org.distantshoresmedia.translationkeyboard.InputLanguageSelection.java
org.distantshoresmedia.translationkeyboard.KeyDetector.java
org.distantshoresmedia.translationkeyboard.KeyboardSwitcher.java
org.distantshoresmedia.translationkeyboard.Keyboard.java
org.distantshoresmedia.translationkeyboard.LanguageSwitcher.java
org.distantshoresmedia.translationkeyboard.LatinIMEBackupAgent.java
org.distantshoresmedia.translationkeyboard.LatinIMEDebugSettings.java
org.distantshoresmedia.translationkeyboard.LatinIMESettings.java
org.distantshoresmedia.translationkeyboard.LatinIMEUtil.java
org.distantshoresmedia.translationkeyboard.LatinIME.java
org.distantshoresmedia.translationkeyboard.LatinImeLogger.java
org.distantshoresmedia.translationkeyboard.LatinKeyboardBaseView.java
org.distantshoresmedia.translationkeyboard.LatinKeyboardView.java
org.distantshoresmedia.translationkeyboard.LatinKeyboard.java
org.distantshoresmedia.translationkeyboard.Main.java
org.distantshoresmedia.translationkeyboard.MiniKeyboardKeyDetector.java
org.distantshoresmedia.translationkeyboard.ModifierKeyState.java
org.distantshoresmedia.translationkeyboard.NotificationReceiver.java
org.distantshoresmedia.translationkeyboard.PluginManager.java
org.distantshoresmedia.translationkeyboard.PointerTracker.java
org.distantshoresmedia.translationkeyboard.PrefScreenActions.java
org.distantshoresmedia.translationkeyboard.PrefScreenFeedback.java
org.distantshoresmedia.translationkeyboard.PrefScreenView.java
org.distantshoresmedia.translationkeyboard.ProximityKeyDetector.java
org.distantshoresmedia.translationkeyboard.SeekBarPreferenceString.java
org.distantshoresmedia.translationkeyboard.SeekBarPreference.java
org.distantshoresmedia.translationkeyboard.SharedPreferencesCompat.java
org.distantshoresmedia.translationkeyboard.Suggest.java
org.distantshoresmedia.translationkeyboard.SwipeTracker.java
org.distantshoresmedia.translationkeyboard.TextEntryState.java
org.distantshoresmedia.translationkeyboard.Tutorial.java
org.distantshoresmedia.translationkeyboard.UserBigramDictionary.java
org.distantshoresmedia.translationkeyboard.UserDictionary.java
org.distantshoresmedia.translationkeyboard.VibratePreference.java
org.distantshoresmedia.translationkeyboard.WordComposer.java