aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/keyboard/Key.java5
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java14
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java11
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java68
4 files changed, 72 insertions, 26 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 4ccb27e4c..06d248e3a 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -64,6 +64,7 @@ public class Key {
private static final int LABEL_OPTION_HAS_HINT_LABEL = 0x800;
private static final int LABEL_OPTION_WITH_ICON_LEFT = 0x1000;
private static final int LABEL_OPTION_WITH_ICON_RIGHT = 0x2000;
+ private static final int LABEL_OPTION_AUTO_X_SCALE = 0x4000;
/** Icon to display instead of a label. Icon takes precedence over a label */
private Drawable mIcon;
@@ -439,6 +440,10 @@ public class Key {
return (mLabelOption & LABEL_OPTION_WITH_ICON_RIGHT) != 0;
}
+ public boolean needsXScale() {
+ return (mLabelOption & LABEL_OPTION_AUTO_X_SCALE) != 0;
+ }
+
public Drawable getIcon() {
return mIcon;
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index acb76cc78..5a44460a1 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -91,6 +91,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
// TODO: Use resource parameter for this value.
private static final float LABEL_ICON_MARGIN = 0.05f;
+ // The maximum key label width in the proportion to the key width.
+ private static final float MAX_LABEL_RATIO = 0.90f;
+
// Main keyboard
private Keyboard mKeyboard;
private final KeyDrawParams mKeyDrawParams;
@@ -572,18 +575,22 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
paint.setTextAlign(Align.LEFT);
} else if (key.hasLabelWithIconLeft() && icon != null) {
labelWidth = getLabelWidth(label, paint) + icon.getIntrinsicWidth()
- + (int)(LABEL_ICON_MARGIN * keyWidth);
+ + LABEL_ICON_MARGIN * keyWidth;
positionX = centerX + labelWidth / 2;
paint.setTextAlign(Align.RIGHT);
} else if (key.hasLabelWithIconRight() && icon != null) {
labelWidth = getLabelWidth(label, paint) + icon.getIntrinsicWidth()
- + (int)(LABEL_ICON_MARGIN * keyWidth);
+ + LABEL_ICON_MARGIN * keyWidth;
positionX = centerX - labelWidth / 2;
paint.setTextAlign(Align.LEFT);
} else {
positionX = centerX;
paint.setTextAlign(Align.CENTER);
}
+ if (key.needsXScale()) {
+ paint.setTextScaleX(
+ Math.min(1.0f, (keyWidth * MAX_LABEL_RATIO) / getLabelWidth(label, paint)));
+ }
if (key.hasUppercaseLetter() && isManualTemporaryUpperCase) {
paint.setColor(params.mKeyTextInactivatedColor);
@@ -598,8 +605,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
paint.setColor(Color.TRANSPARENT);
}
canvas.drawText(label, 0, label.length(), positionX, baseline, paint);
- // Turn off drop shadow
+ // Turn off drop shadow and reset x-scale.
paint.setShadowLayer(0, 0, 0, 0);
+ paint.setTextScaleX(1.0f);
if (icon != null) {
final int iconWidth = icon.getIntrinsicWidth();
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 6263ebefa..c35273edd 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -757,10 +757,19 @@ public class Utils {
return toTitleCase(locale.getLanguage(), locale);
}
- private static String toTitleCase(String s, Locale locale) {
+ public static String toTitleCase(String s, Locale locale) {
if (s.length() <= 1) {
+ // TODO: is this really correct? Shouldn't this be s.toUpperCase()?
return s;
}
+ // TODO: fix the bugs below
+ // - This does not work for Greek, because it returns upper case instead of title case.
+ // - It does not work for Serbian, because it fails to account for the "lj" character,
+ // which should be "Lj" in title case and "LJ" in upper case.
+ // - It does not work for Dutch, because it fails to account for the "ij" digraph, which
+ // are two different characters but both should be capitalized as "IJ" as if they were
+ // a single letter.
+ // - It also does not work with unicode surrogate code points.
return s.toUpperCase(locale).charAt(0) + s.substring(1);
}
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index ae47ab22b..3cf8788aa 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -55,6 +55,10 @@ public class AndroidSpellCheckerService extends SpellCheckerService {
private static final boolean DBG = false;
private static final int POOL_SIZE = 2;
+ private static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
+ private static final int CAPITALIZE_FIRST = 1; // First only
+ private static final int CAPITALIZE_ALL = 2; // All caps
+
private final static String[] EMPTY_STRING_ARRAY = new String[0];
private final static SuggestionsInfo EMPTY_SUGGESTIONS_INFO =
new SuggestionsInfo(0, EMPTY_STRING_ARRAY);
@@ -139,7 +143,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService {
return true;
}
- public Result getResults(final CharSequence originalText, final double threshold) {
+ public Result getResults(final CharSequence originalText, final double threshold,
+ final int capitalizeType, final Locale locale) {
final String[] gatheredSuggestions;
final boolean looksLikeTypo;
if (0 == mLength) {
@@ -166,6 +171,19 @@ public class AndroidSpellCheckerService extends SpellCheckerService {
}
Collections.reverse(mSuggestions);
Utils.removeDupes(mSuggestions);
+ if (CAPITALIZE_ALL == capitalizeType) {
+ for (int i = 0; i < mSuggestions.size(); ++i) {
+ // get(i) returns a CharSequence which is actually a String so .toString()
+ // should return the same object.
+ mSuggestions.set(i, mSuggestions.get(i).toString().toUpperCase(locale));
+ }
+ } else if (CAPITALIZE_FIRST == capitalizeType) {
+ for (int i = 0; i < mSuggestions.size(); ++i) {
+ // Likewise
+ mSuggestions.set(i, Utils.toTitleCase(mSuggestions.get(i).toString(),
+ locale));
+ }
+ }
// This returns a String[], while toArray() returns an Object[] which cannot be cast
// into a String[].
gatheredSuggestions = mSuggestions.toArray(EMPTY_STRING_ARRAY);
@@ -226,6 +244,25 @@ public class AndroidSpellCheckerService extends SpellCheckerService {
return new DictAndProximity(dictionaryCollection, proximityInfo);
}
+ // This method assumes the text is not empty or null.
+ private static int getCapitalizationType(String text) {
+ // If the first char is not uppercase, then the word is either all lower case,
+ // and in either case we return CAPITALIZE_NONE.
+ if (!Character.isUpperCase(text.codePointAt(0))) return CAPITALIZE_NONE;
+ final int len = text.codePointCount(0, text.length());
+ int capsCount = 1;
+ for (int i = 1; i < len; ++i) {
+ if (1 != capsCount && i != capsCount) break;
+ if (Character.isUpperCase(text.codePointAt(i))) ++capsCount;
+ }
+ // We know the first char is upper case. So we want to test if either everything
+ // else is lower case, or if everything else is upper case. If the string is
+ // exactly one char long, then we will arrive here with capsCount 1, and this is
+ // correct, too.
+ if (1 == capsCount) return CAPITALIZE_FIRST;
+ return (len == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE);
+ }
+
private static class AndroidSpellCheckerSession extends Session {
// Immutable, but need the locale which is not available in the constructor yet
private DictionaryPool mDictionaryPool;
@@ -276,31 +313,18 @@ public class AndroidSpellCheckerService extends SpellCheckerService {
WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
}
+ final int capitalizeType = getCapitalizationType(text);
boolean isInDict = true;
try {
final DictAndProximity dictInfo = mDictionaryPool.take();
dictInfo.mDictionary.getWords(composer, suggestionsGatherer,
dictInfo.mProximityInfo);
isInDict = dictInfo.mDictionary.isValidWord(text);
- if (!isInDict && Character.isUpperCase(text.codePointAt(0))) {
- // If the first char is not uppercase, then the word is either all lower case,
- // in which case we already tested it, or mixed case, in which case we don't
- // want to test a lower-case version of it. Hence the test above.
- // Also note that by isEmpty() test at the top of the method codePointAt(0) is
- // guaranteed to be there.
- final int len = text.codePointCount(0, text.length());
- int capsCount = 1;
- for (int i = 1; i < len; ++i) {
- if (1 != capsCount && i != capsCount) break;
- if (Character.isUpperCase(text.codePointAt(i))) ++capsCount;
- }
- // We know the first char is upper case. So we want to test if either everything
- // else is lower case, or if everything else is upper case. If the string is
- // exactly one char long, then we will arrive here with capsCount 0, and this is
- // correct, too.
- if (1 == capsCount || len == capsCount) {
- isInDict = dictInfo.mDictionary.isValidWord(text.toLowerCase(mLocale));
- }
+ if (!isInDict && CAPITALIZE_NONE != capitalizeType) {
+ // We want to test the word again if it's all caps or first caps only.
+ // If it's fully down, we already tested it, if it's mixed case, we don't
+ // want to test a lowercase version of it.
+ isInDict = dictInfo.mDictionary.isValidWord(text.toLowerCase(mLocale));
}
if (!mDictionaryPool.offer(dictInfo)) {
Log.e(TAG, "Can't re-insert a dictionary into its pool");
@@ -310,8 +334,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService {
return EMPTY_SUGGESTIONS_INFO;
}
- final SuggestionsGatherer.Result result =
- suggestionsGatherer.getResults(text, mService.mTypoThreshold);
+ final SuggestionsGatherer.Result result = suggestionsGatherer.getResults(text,
+ mService.mTypoThreshold, capitalizeType, mLocale);
if (DBG) {
Log.i(TAG, "Spell checking results for " + text + " with suggestion limit "