diff options
Diffstat (limited to 'java/src/com/android/inputmethod')
-rw-r--r-- | java/src/com/android/inputmethod/keyboard/Key.java | 19 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/keyboard/KeyDetector.java | 41 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/keyboard/Keyboard.java | 36 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/keyboard/KeyboardView.java | 3 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/keyboard/MiniKeyboard.java | 4 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/keyboard/ProximityInfo.java | 50 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java (renamed from java/src/com/android/inputmethod/keyboard/internal/MoreKeySpecParser.java) | 180 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java | 3 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/latin/SettingsValues.java | 12 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/latin/Utils.java | 120 | ||||
-rw-r--r-- | java/src/com/android/inputmethod/latin/WordComposer.java | 4 |
11 files changed, 294 insertions, 178 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 5e58821d0..8f2efab29 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -28,8 +28,9 @@ import android.util.Xml; import com.android.inputmethod.keyboard.internal.KeyStyles; import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; -import com.android.inputmethod.keyboard.internal.MoreKeySpecParser; +import com.android.inputmethod.keyboard.internal.KeySpecParser; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.XmlParseUtils; import org.xmlpull.v1.XmlPullParser; @@ -128,7 +129,7 @@ public class Key { private boolean mEnabled = true; private static Drawable getIcon(Keyboard.Params params, String moreKeySpec) { - final int iconAttrId = MoreKeySpecParser.getIconAttrId(moreKeySpec); + final int iconAttrId = KeySpecParser.getIconAttrId(moreKeySpec); if (iconAttrId == KeyboardIconsSet.ICON_UNDEFINED) { return null; } else { @@ -141,9 +142,9 @@ public class Key { */ public Key(Resources res, Keyboard.Params params, String moreKeySpec, int x, int y, int width, int height) { - this(params, MoreKeySpecParser.getLabel(moreKeySpec), null, getIcon(params, moreKeySpec), - MoreKeySpecParser.getCode(res, moreKeySpec), - MoreKeySpecParser.getOutputText(moreKeySpec), + this(params, KeySpecParser.getLabel(moreKeySpec), null, getIcon(params, moreKeySpec), + KeySpecParser.getCode(res, moreKeySpec), + KeySpecParser.getOutputText(moreKeySpec), x, y, width, height); } @@ -245,7 +246,7 @@ public class Key { int actionFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags, 0); final String[] additionalMoreKeys = style.getStringArray( keyAttr, R.styleable.Keyboard_Key_additionalMoreKeys); - final String[] moreKeys = MoreKeySpecParser.insertAddtionalMoreKeys(style.getStringArray( + final String[] moreKeys = KeySpecParser.insertAddtionalMoreKeys(style.getStringArray( keyAttr, R.styleable.Keyboard_Key_moreKeys), additionalMoreKeys); if (moreKeys != null) { actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS; @@ -270,7 +271,7 @@ public class Key { // Choose the first letter of the label as primary code if not specified. if (code == Keyboard.CODE_UNSPECIFIED && TextUtils.isEmpty(outputText) && !TextUtils.isEmpty(mLabel)) { - if (mLabel.codePointCount(0, mLabel.length()) == 1) { + if (Utils.codePointCount(mLabel) == 1) { // Use the first letter of the hint label if shiftedLetterActivated flag is // specified. if (hasShiftedLetterHint() && isShiftedLetterActivated() @@ -308,7 +309,7 @@ public class Key { if (!Keyboard.isLetterCode(code) || preserveCase) return code; final String text = new String(new int[] { code } , 0, 1); final String casedText = adjustCaseOfStringForKeyboardId(text, preserveCase, id); - return casedText.codePointCount(0, casedText.length()) == 1 + return Utils.codePointCount(casedText) == 1 ? casedText.codePointAt(0) : Keyboard.CODE_UNSPECIFIED; } @@ -380,7 +381,7 @@ public class Key { @Override public String toString() { String top = Keyboard.printableCode(mCode); - if (mLabel != null && mLabel.codePointCount(0, mLabel.length()) != 1) { + if (Utils.codePointCount(mLabel) != 1) { top += "/\"" + mLabel + '"'; } return String.format("%s %d,%d", top, mX, mY); diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index 0d271625b..bff491ffd 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -19,12 +19,14 @@ package com.android.inputmethod.keyboard; import android.util.Log; import java.util.Arrays; +import java.util.List; public class KeyDetector { private static final String TAG = KeyDetector.class.getSimpleName(); private static final boolean DEBUG = false; public static final int NOT_A_CODE = -1; + private static final int ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE = 2; private final int mKeyHysteresisDistanceSquared; @@ -154,8 +156,9 @@ public class KeyDetector { return distances.length; } - private void getNearbyKeyCodes(final int[] allCodes) { + private void getNearbyKeyCodes(final int primaryCode, final int[] allCodes) { final Key[] neighborKeys = mNeighborKeys; + final int maxCodesSize = allCodes.length; // allCodes[0] should always have the key code even if it is a non-letter key. if (neighborKeys[0] == null) { @@ -164,7 +167,7 @@ public class KeyDetector { } int numCodes = 0; - for (int j = 0; j < neighborKeys.length && numCodes < allCodes.length; j++) { + for (int j = 0; j < neighborKeys.length && numCodes < maxCodesSize; j++) { final Key key = neighborKeys[j]; if (key == null) break; @@ -174,6 +177,38 @@ public class KeyDetector { continue; allCodes[numCodes++] = code; } + if (maxCodesSize <= numCodes) { + return; + } + if (primaryCode != NOT_A_CODE) { + final List<Integer> additionalChars = + mKeyboard.getAdditionalProximityChars().get(primaryCode); + if (additionalChars == null || additionalChars.size() == 0) { + return; + } + int currentCodesSize = numCodes; + allCodes[numCodes++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE; + if (maxCodesSize <= numCodes) { + return; + } + // TODO: This is O(N^2). Assuming additionalChars.size() is up to 4 or 5. + for (int i = 0; i < additionalChars.size(); ++i) { + final int additionalChar = additionalChars.get(i); + boolean contains = false; + for (int j = 0; j < currentCodesSize; ++j) { + if (additionalChar == allCodes[j]) { + contains = true; + break; + } + } + if (!contains) { + allCodes[numCodes++] = additionalChar; + if (maxCodesSize <= numCodes) { + return; + } + } + } + } } /** @@ -205,7 +240,7 @@ public class KeyDetector { } if (allCodes != null && allCodes.length > 0) { - getNearbyKeyCodes(allCodes); + getNearbyKeyCodes(primaryKey != null ? primaryKey.mCode : NOT_A_CODE, allCodes); if (DEBUG) { Log.d(TAG, "x=" + x + " y=" + y + " primary=" + printableCode(primaryKey) diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 6653dec4b..10e0a5b01 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; @@ -38,10 +39,12 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -130,6 +133,8 @@ public class Keyboard { private final ProximityInfo mProximityInfo; + public final Map<Integer, List<Integer>> mAdditionalProximityChars; + public Keyboard(Params params) { mId = params.mId; mThemeId = params.mThemeId; @@ -146,10 +151,12 @@ public class Keyboard { mKeys = Collections.unmodifiableSet(params.mKeys); mShiftKeys = Collections.unmodifiableSet(params.mShiftKeys); mIconsSet = params.mIconsSet; + mAdditionalProximityChars = params.mAdditionalProximityChars; mProximityInfo = new ProximityInfo( params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight, - mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection); + mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection, + params.mAdditionalProximityChars); } public ProximityInfo getProximityInfo() { @@ -227,6 +234,9 @@ public class Keyboard { public final Set<Key> mKeys = new HashSet<Key>(); public final Set<Key> mShiftKeys = new HashSet<Key>(); public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet(); + // TODO: Should be in Key instead of Keyboard.Params? + public final Map<Integer, List<Integer>> mAdditionalProximityChars = + new HashMap<Integer, List<Integer>>(); public KeyboardSet.KeysCache mKeysCache; @@ -358,6 +368,10 @@ public class Keyboard { return mProximityInfo.getNearestKeys(adjustedX, adjustedY); } + public Map<Integer, List<Integer>> getAdditionalProximityChars() { + return mAdditionalProximityChars; + } + public static String printableCode(int code) { switch (code) { case CODE_SHIFT: return "shift"; @@ -614,6 +628,7 @@ public class Keyboard { mParams = params; setTouchPositionCorrectionData(context, params); + setAdditionalProximityChars(context, params); params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width); params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height); @@ -636,6 +651,25 @@ public class Keyboard { params.mTouchPositionCorrection.load(data); } + private static void setAdditionalProximityChars(Context context, Params params) { + final String[] additionalChars = + context.getResources().getStringArray(R.array.additional_proximitychars); + int currentPrimaryIndex = 0; + for (int i = 0; i < additionalChars.length; ++i) { + final String additionalChar = additionalChars[i]; + if (TextUtils.isEmpty(additionalChar)) { + currentPrimaryIndex = 0; + } else if (currentPrimaryIndex == 0) { + currentPrimaryIndex = additionalChar.charAt(0); + params.mAdditionalProximityChars.put( + currentPrimaryIndex, new ArrayList<Integer>()); + } else if (currentPrimaryIndex != 0) { + final int c = additionalChar.charAt(0); + params.mAdditionalProximityChars.get(currentPrimaryIndex).add(c); + } + } + } + public void setAutoGenerate(KeyboardSet.KeysCache keysCache) { mParams.mKeysCache = keysCache; } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 2cbd132ca..c6fb75489 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -41,6 +41,7 @@ import com.android.inputmethod.compat.FrameLayoutCompatUtils; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StaticInnerHandlerWrapper; +import com.android.inputmethod.latin.Utils; import java.util.HashMap; @@ -851,7 +852,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { if (key.mLabel != null) { // TODO Should take care of temporaryShiftLabel here. previewText.setCompoundDrawables(null, null, null, null); - if (key.mLabel.length() > 1) { + if (Utils.codePointCount(key.mLabel) > 1) { previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mKeyLetterSize); previewText.setTypeface(Typeface.DEFAULT_BOLD); } else { diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java index 433bd0d75..4648da1c1 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java @@ -18,7 +18,7 @@ package com.android.inputmethod.keyboard; import android.graphics.Paint; -import com.android.inputmethod.keyboard.internal.MoreKeySpecParser; +import com.android.inputmethod.keyboard.internal.KeySpecParser; import com.android.inputmethod.latin.R; public class MiniKeyboard extends Keyboard { @@ -235,7 +235,7 @@ public class MiniKeyboard extends Keyboard { Paint paint = null; int maxWidth = minKeyWidth; for (String moreKeySpec : moreKeys) { - final String label = MoreKeySpecParser.getLabel(moreKeySpec); + final String label = KeySpecParser.getLabel(moreKeySpec); // If the label is single letter, minKeyWidth is enough to hold the label. if (label != null && label.length() > 1) { if (paint == null) { diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index c1dae0601..2d1a0083d 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -24,6 +24,9 @@ import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Set; public class ProximityInfo { @@ -44,7 +47,8 @@ public class ProximityInfo { private final Key[][] mGridNeighbors; ProximityInfo(int gridWidth, int gridHeight, int minWidth, int height, int keyWidth, - int keyHeight, Set<Key> keys, TouchPositionCorrection touchPositionCorrection) { + int keyHeight, Set<Key> keys, TouchPositionCorrection touchPositionCorrection, + Map<Integer, List<Integer>> additionalProximityChars) { mGridWidth = gridWidth; mGridHeight = gridHeight; mGridSize = mGridWidth * mGridHeight; @@ -58,20 +62,20 @@ public class ProximityInfo { // No proximity required. Keyboard might be mini keyboard. return; } - computeNearestNeighbors(keyWidth, keys, touchPositionCorrection); + computeNearestNeighbors(keyWidth, keys, touchPositionCorrection, additionalProximityChars); } public static ProximityInfo createDummyProximityInfo() { - return new ProximityInfo(1, 1, 1, 1, 1, 1, Collections.<Key>emptySet(), null); + return new ProximityInfo(1, 1, 1, 1, 1, 1, Collections.<Key> emptySet(), + null, Collections.<Integer, List<Integer>> emptyMap()); } public static ProximityInfo createSpellCheckerProximityInfo(final int[] proximity) { final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo(); spellCheckerProximityInfo.mNativeProximityInfo = spellCheckerProximityInfo.setProximityInfoNative( - SpellCheckerProximityInfo.ROW_SIZE, - 480, 300, 11, 3, proximity, - 0, null, null, null, null, null, null, null, null); + SpellCheckerProximityInfo.ROW_SIZE, 480, 300, 11, 3, proximity, 0, + null, null, null, null, null, null, null, null); return spellCheckerProximityInfo; } @@ -79,11 +83,13 @@ public class ProximityInfo { static { Utils.loadNativeLibrary(); } + private native long setProximityInfoNative(int maxProximityCharsSize, int displayWidth, int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray, int keyCount, int[] keyXCoordinates, int[] keyYCoordinates, int[] keyWidths, int[] keyHeights, int[] keyCharCodes, float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii); + private native void releaseProximityInfoNative(long nativeProximityInfo); private final void setProximityInfo(Key[][] gridNeighborKeys, int keyboardWidth, @@ -138,7 +144,7 @@ public class ProximityInfo { final float radius = touchPositionCorrection.mRadii[row]; sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth; sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight; - sweetSpotRadii[i] = radius * (float)Math.sqrt( + sweetSpotRadii[i] = radius * (float) Math.sqrt( hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight); } } @@ -168,7 +174,12 @@ public class ProximityInfo { } private void computeNearestNeighbors(int defaultWidth, Set<Key> keys, - TouchPositionCorrection touchPositionCorrection) { + TouchPositionCorrection touchPositionCorrection, + Map<Integer, List<Integer>> additionalProximityChars) { + final Map<Integer, Key> keyCodeMap = new HashMap<Integer, Key>(); + for (final Key key : keys) { + keyCodeMap.put(key.mCode, key); + } final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE); final int threshold = thresholdBase * thresholdBase; // Round-up so we don't have any pixels outside the grid @@ -186,6 +197,27 @@ public class ProximityInfo { neighborKeys[count++] = key; } } + int currentCodesSize = count; + for (int i = 0; i < currentCodesSize; ++i) { + final int c = neighborKeys[i].mCode; + final List<Integer> additionalChars = additionalProximityChars.get(c); + if (additionalChars == null || additionalChars.size() == 0) { + continue; + } + for (int j = 0; j < additionalChars.size(); ++j) { + final int additionalChar = additionalChars.get(j); + boolean contains = false; + for (int k = 0; k < count; ++k) { + if(additionalChar == neighborKeys[k].mCode) { + contains = true; + break; + } + } + if (!contains) { + neighborKeys[count++] = keyCodeMap.get(additionalChar); + } + } + } mGridNeighbors[(y / mCellHeight) * mGridWidth + (x / mCellWidth)] = Arrays.copyOfRange(neighborKeys, 0, count); } @@ -199,7 +231,7 @@ public class ProximityInfo { return EMPTY_KEY_ARRAY; } if (x >= 0 && x < mKeyboardMinWidth && y >= 0 && y < mKeyboardHeight) { - int index = (y / mCellHeight) * mGridWidth + (x / mCellWidth); + int index = (y / mCellHeight) * mGridWidth + (x / mCellWidth); if (index < mGridSize) { return mGridNeighbors[index]; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index abebfec01..ba12676ad 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -30,23 +30,31 @@ import java.util.Arrays; /** * String parser of moreKeys attribute of Key. * The string is comma separated texts each of which represents one "more key". + * - String resource can be embedded into specification @string/name. This is done before parsing + * comma. * Each "more key" specification is one of the following: * - A single letter (Letter) * - Label optionally followed by keyOutputText or code (keyLabel|keyOutputText). * - Icon followed by keyOutputText or code (@icon/icon_name|@integer/key_code) - * Special character, comma ',' backslash '\', and bar '|' can be escaped by '\' - * character. + * Special character, comma ',' backslash '\', and bar '|' can be escaped by '\' character. * Note that the character '@' and '\' are also parsed by XML parser and CSV parser as well. - * See {@link KeyboardIconsSet} about icon_number. + * See {@link KeyboardIconsSet} about icon_name. */ -public class MoreKeySpecParser { +public class KeySpecParser { private static final boolean DEBUG = LatinImeLogger.sDBG; + + // Constants for parsing. + private static int COMMA = ','; + private static final char ESCAPE_CHAR = '\\'; + private static final char PREFIX_AT = '@'; + private static final char SUFFIX_SLASH = '/'; + private static final String PREFIX_STRING = PREFIX_AT + "string"; private static final char LABEL_END = '|'; - private static final String PREFIX_ICON = Utils.PREFIX_AT + "icon" + Utils.SUFFIX_SLASH; - private static final String PREFIX_CODE = Utils.PREFIX_AT + "integer" + Utils.SUFFIX_SLASH; + private static final String PREFIX_ICON = PREFIX_AT + "icon" + SUFFIX_SLASH; + private static final String PREFIX_CODE = PREFIX_AT + "integer" + SUFFIX_SLASH; private static final String ADDITIONAL_MORE_KEY_MARKER = "%"; - private MoreKeySpecParser() { + private KeySpecParser() { // Intentional empty constructor for utility class. } @@ -56,7 +64,7 @@ public class MoreKeySpecParser { if (end > 0) { return true; } - throw new MoreKeySpecParserError("outputText or code not specified: " + moreKeySpec); + throw new KeySpecParserError("outputText or code not specified: " + moreKeySpec); } return false; } @@ -71,15 +79,17 @@ public class MoreKeySpecParser { } private static String parseEscape(String text) { - if (text.indexOf(Utils.ESCAPE_CHAR) < 0) { + if (text.indexOf(ESCAPE_CHAR) < 0) { return text; } final int length = text.length(); final StringBuilder sb = new StringBuilder(); for (int pos = 0; pos < length; pos++) { final char c = text.charAt(pos); - if (c == Utils.ESCAPE_CHAR && pos + 1 < length) { - sb.append(text.charAt(++pos)); + if (c == ESCAPE_CHAR && pos + 1 < length) { + // Skip escape char + pos++; + sb.append(text.charAt(pos)); } else { sb.append(c); } @@ -88,17 +98,18 @@ public class MoreKeySpecParser { } private static int indexOfLabelEnd(String moreKeySpec, int start) { - if (moreKeySpec.indexOf(Utils.ESCAPE_CHAR, start) < 0) { + if (moreKeySpec.indexOf(ESCAPE_CHAR, start) < 0) { final int end = moreKeySpec.indexOf(LABEL_END, start); if (end == 0) { - throw new MoreKeySpecParserError(LABEL_END + " at " + start + ": " + moreKeySpec); + throw new KeySpecParserError(LABEL_END + " at " + start + ": " + moreKeySpec); } return end; } final int length = moreKeySpec.length(); for (int pos = start; pos < length; pos++) { final char c = moreKeySpec.charAt(pos); - if (c == Utils.ESCAPE_CHAR && pos + 1 < length) { + if (c == ESCAPE_CHAR && pos + 1 < length) { + // Skip escape char pos++; } else if (c == LABEL_END) { return pos; @@ -115,7 +126,7 @@ public class MoreKeySpecParser { final String label = (end > 0) ? parseEscape(moreKeySpec.substring(0, end)) : parseEscape(moreKeySpec); if (TextUtils.isEmpty(label)) { - throw new MoreKeySpecParserError("Empty label: " + moreKeySpec); + throw new KeySpecParserError("Empty label: " + moreKeySpec); } return label; } @@ -127,7 +138,7 @@ public class MoreKeySpecParser { final int end = indexOfLabelEnd(moreKeySpec, 0); if (end > 0) { if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) { - throw new MoreKeySpecParserError("Multiple " + LABEL_END + ": " + throw new KeySpecParserError("Multiple " + LABEL_END + ": " + moreKeySpec); } final String outputText = parseEscape( @@ -135,23 +146,23 @@ public class MoreKeySpecParser { if (!TextUtils.isEmpty(outputText)) { return outputText; } - throw new MoreKeySpecParserError("Empty outputText: " + moreKeySpec); + throw new KeySpecParserError("Empty outputText: " + moreKeySpec); } final String label = getLabel(moreKeySpec); if (label == null) { - throw new MoreKeySpecParserError("Empty label: " + moreKeySpec); + throw new KeySpecParserError("Empty label: " + moreKeySpec); } // Code is automatically generated for one letter label. See {@link getCode()}. - return (label.length() == 1) ? null : label; + return (Utils.codePointCount(label) == 1) ? null : label; } public static int getCode(Resources res, String moreKeySpec) { if (hasCode(moreKeySpec)) { final int end = indexOfLabelEnd(moreKeySpec, 0); if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) { - throw new MoreKeySpecParserError("Multiple " + LABEL_END + ": " + moreKeySpec); + throw new KeySpecParserError("Multiple " + LABEL_END + ": " + moreKeySpec); } - final int resId = Utils.getResourceId(res, + final int resId = getResourceId(res, moreKeySpec.substring(end + /* LABEL_END */1 + /* PREFIX_AT */1), R.string.english_ime_name); final int code = res.getInteger(resId); @@ -162,8 +173,8 @@ public class MoreKeySpecParser { } final String label = getLabel(moreKeySpec); // Code is automatically generated for one letter label. - if (label != null && label.length() == 1) { - return label.charAt(0); + if (Utils.codePointCount(label) == 1) { + return label.codePointAt(0); } return Keyboard.CODE_OUTPUT_TEXT; } @@ -248,9 +259,128 @@ public class MoreKeySpecParser { } @SuppressWarnings("serial") - public static class MoreKeySpecParserError extends RuntimeException { - public MoreKeySpecParserError(String message) { + public static class KeySpecParserError extends RuntimeException { + public KeySpecParserError(String message) { super(message); } } + + private static int getResourceId(Resources res, String name, int packageNameResId) { + String packageName = res.getResourcePackageName(packageNameResId); + int resId = res.getIdentifier(name, null, packageName); + if (resId == 0) { + throw new RuntimeException("Unknown resource: " + name); + } + return resId; + } + + private static String resolveStringResource(String text, Resources res, int packageNameResId) { + final int size = text.length(); + if (size < PREFIX_STRING.length()) { + return text; + } + + StringBuilder sb = null; + for (int pos = 0; pos < size; pos++) { + final char c = text.charAt(pos); + if (c == PREFIX_AT && text.startsWith(PREFIX_STRING, pos)) { + if (sb == null) { + sb = new StringBuilder(text.substring(0, pos)); + } + final int end = searchResourceNameEnd(text, pos + PREFIX_STRING.length()); + final String resName = text.substring(pos + 1, end); + final int resId = getResourceId(res, resName, packageNameResId); + sb.append(res.getString(resId)); + pos = end - 1; + } else if (c == ESCAPE_CHAR) { + pos++; + if (sb != null) { + sb.append(c); + if (pos < size) { + sb.append(text.charAt(pos)); + } + } + } else if (sb != null) { + sb.append(c); + } + } + return (sb == null) ? text : sb.toString(); + } + + private static int searchResourceNameEnd(String text, int start) { + final int size = text.length(); + if (start >= size || text.charAt(start) != SUFFIX_SLASH) { + throw new RuntimeException("Resource name not specified"); + } + for (int pos = start + 1; pos < size; pos++) { + final char c = text.charAt(pos); + // String resource name should be consisted of [a-z_0-9]. + if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')) { + continue; + } + return pos; + } + return size; + } + + public static String[] parseCsvString(String rawText, Resources res, int packageNameResId) { + final String text = resolveStringResource(rawText, res, packageNameResId); + final int size = text.length(); + if (size == 0) { + return null; + } + if (Utils.codePointCount(text) == 1) { + return new String[] { text }; + } + + final StringBuilder sb = new StringBuilder(); + ArrayList<String> list = null; + int start = 0; + for (int pos = 0; pos < size; pos++) { + final char c = text.charAt(pos); + if (c == COMMA) { + if (list == null) { + list = new ArrayList<String>(); + } + if (sb.length() == 0) { + list.add(text.substring(start, pos)); + } else { + list.add(sb.toString()); + sb.setLength(0); + } + // Skip comma + start = pos + 1; + continue; + } + // TODO: Only parse escaped comma. Other escaped character should be passed through + // with escaped character prefixed. + // Skip escaped sequence. + if (c == ESCAPE_CHAR) { + if (start == pos) { + // Skip escaping comma at the beginning of the text. + start++; + pos++; + } else { + if (start < pos && sb.length() == 0) { + sb.append(text.substring(start, pos)); + } + // Skip comma + pos++; + if (pos < size) { + sb.append(text.charAt(pos)); + } + } + } else if (sb.length() > 0) { + sb.append(c); + } + } + if (list == null) { + return new String[] { + sb.length() > 0 ? sb.toString() : text.substring(start) + }; + } else { + list.add(sb.length() > 0 ? sb.toString() : text.substring(start)); + return list.toArray(new String[list.size()]); + } + } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java index 25a2c23c4..6ec56ca9f 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java @@ -21,7 +21,6 @@ import android.util.Log; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.XmlParseUtils; import org.xmlpull.v1.XmlPullParser; @@ -72,7 +71,7 @@ public class KeyStyles { protected static String[] parseStringArray(TypedArray a, int index) { if (!a.hasValue(index)) return null; - return Utils.parseCsvString( + return KeySpecParser.parseCsvString( a.getString(index), a.getResources(), R.string.english_ime_name); } } diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 5f9cb8df6..8e2f605c4 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -25,7 +25,7 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.compat.InputTypeCompatUtils; import com.android.inputmethod.compat.VibratorCompatWrapper; -import com.android.inputmethod.keyboard.internal.MoreKeySpecParser; +import com.android.inputmethod.keyboard.internal.KeySpecParser; import java.util.Arrays; import java.util.Locale; @@ -100,7 +100,7 @@ public class SettingsValues { } } } - final String[] suggestPuncsSpec = Utils.parseCsvString( + final String[] suggestPuncsSpec = KeySpecParser.parseCsvString( res.getString(R.string.suggested_punctuations), res, R.string.english_ime_name); mSuggestPuncs = createSuggestPuncs(suggestPuncsSpec); mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec); @@ -158,7 +158,7 @@ public class SettingsValues { final StringBuilder sb = new StringBuilder(); if (puncs != null) { for (final String puncSpec : puncs) { - sb.append(MoreKeySpecParser.getLabel(puncSpec)); + sb.append(KeySpecParser.getLabel(puncSpec)); } } return sb.toString(); @@ -168,7 +168,7 @@ public class SettingsValues { final SuggestedWords.Builder builder = new SuggestedWords.Builder(); if (puncs != null) { for (final String puncSpec : puncs) { - builder.addWord(MoreKeySpecParser.getLabel(puncSpec)); + builder.addWord(KeySpecParser.getLabel(puncSpec)); } } return builder.setIsPunctuationSuggestions().build(); @@ -178,11 +178,11 @@ public class SettingsValues { final SuggestedWords.Builder builder = new SuggestedWords.Builder(); if (puncs != null) { for (final String puncSpec : puncs) { - final String outputText = MoreKeySpecParser.getOutputText(puncSpec); + final String outputText = KeySpecParser.getOutputText(puncSpec); if (outputText != null) { builder.addWord(outputText); } else { - builder.addWord(MoreKeySpecParser.getLabel(puncSpec)); + builder.addWord(KeySpecParser.getLabel(puncSpec)); } } } diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index d1b808fc8..3975dddeb 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -62,12 +62,6 @@ public class Utils { private static boolean DBG = LatinImeLogger.sDBG; private static boolean DBG_EDIT_DISTANCE = false; - // Constants for resource name parsing. - public static final char ESCAPE_CHAR = '\\'; - public static final char PREFIX_AT = '@'; - public static final char SUFFIX_SLASH = '/'; - private static final String PREFIX_STRING = PREFIX_AT + "string"; - private Utils() { // Intentional empty constructor for utility class. } @@ -800,116 +794,8 @@ public class Utils { } } - public static int getResourceId(Resources res, String name, int packageNameResId) { - String packageName = res.getResourcePackageName(packageNameResId); - int resId = res.getIdentifier(name, null, packageName); - if (resId == 0) { - throw new RuntimeException("Unknown resource: " + name); - } - return resId; - } - - public static String resolveStringResource(String text, Resources res, int packageNameResId) { - final int size = text.length(); - if (size < PREFIX_STRING.length()) { - return text; - } - - StringBuilder sb = null; - for (int pos = 0; pos < size; pos++) { - final char c = text.charAt(pos); - if (c == PREFIX_AT && text.startsWith(PREFIX_STRING, pos)) { - if (sb == null) { - sb = new StringBuilder(text.substring(0, pos)); - } - final int end = Utils.searchResourceNameEnd(text, pos + PREFIX_STRING.length()); - final String resName = text.substring(pos + 1, end); - final int resId = getResourceId(res, resName, packageNameResId); - sb.append(res.getString(resId)); - pos = end - 1; - } else if (c == ESCAPE_CHAR) { - pos++; - if (sb != null) { - sb.append(c); - if (pos < size) { - sb.append(text.charAt(pos)); - } - } - } else if (sb != null) { - sb.append(c); - } - } - return (sb == null) ? text : sb.toString(); - } - - private static int searchResourceNameEnd(String text, int start) { - final int size = text.length(); - if (start >= size || text.charAt(start) != SUFFIX_SLASH) { - throw new RuntimeException("Resource name not specified"); - } - for (int pos = start + 1; pos < size; pos++) { - final char c = text.charAt(pos); - // String resource name should be consisted of [a-z_0-9]. - if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')) { - continue; - } - return pos; - } - return size; - } - - public static String[] parseCsvString(String rawText, Resources res, int packageNameResId) { - final String text = resolveStringResource(rawText, res, packageNameResId); - final int size = text.length(); - if (size == 0) { - return null; - } - if (size == 1) { - return new String[] { text }; - } - - final StringBuilder sb = new StringBuilder(); - ArrayList<String> list = null; - int start = 0; - for (int pos = 0; pos < size; pos++) { - final char c = text.charAt(pos); - if (c == ',') { - if (list == null) { - list = new ArrayList<String>(); - } - if (sb.length() == 0) { - list.add(text.substring(start, pos)); - } else { - list.add(sb.toString()); - sb.setLength(0); - } - start = pos + 1; - continue; - } else if (c == ESCAPE_CHAR) { - if (start == pos) { - // Skip escape character at the beginning of the value. - start++; - pos++; - } else { - if (start < pos && sb.length() == 0) { - sb.append(text.subSequence(start, pos)); - } - pos++; - if (pos < size) { - sb.append(text.charAt(pos)); - } - } - } else if (sb.length() > 0) { - sb.append(c); - } - } - if (list == null) { - return new String[] { - sb.length() > 0 ? sb.toString() : text.substring(start) - }; - } else { - list.add(sb.length() > 0 ? sb.toString() : text.substring(start)); - return list.toArray(new String[list.size()]); - } + public static int codePointCount(String text) { + if (TextUtils.isEmpty(text)) return 0; + return text.codePointCount(0, text.length()); } } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 230c2916b..bd244b913 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -16,8 +16,6 @@ package com.android.inputmethod.latin; -import android.text.TextUtils; - import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; @@ -33,7 +31,7 @@ public class WordComposer { public static final int NOT_A_CODE = KeyDetector.NOT_A_CODE; public static final int NOT_A_COORDINATE = -1; - final int N = BinaryDictionary.MAX_WORD_LENGTH; + final static int N = BinaryDictionary.MAX_WORD_LENGTH; private ArrayList<int[]> mCodes; private int[] mXCoordinates; |