diff options
Diffstat (limited to 'java/src')
11 files changed, 377 insertions, 298 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 154f4af91..a31911d60 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -134,7 +134,7 @@ public class KeyCodeDescriptionMapper { return context.getString(mKeyLabelMap.get(label)); } else if (label.length() == 1 || (keyboard.isManualTemporaryUpperCase() && !TextUtils - .isEmpty(key.mHintLetter))) { + .isEmpty(key.mHintLabel))) { return getDescriptionForKeyCode(context, keyboard, key); } else { return label; @@ -181,8 +181,8 @@ public class KeyCodeDescriptionMapper { * @return the key code for the specified key */ private int getCorrectKeyCode(Keyboard keyboard, Key key) { - if (keyboard.isManualTemporaryUpperCase() && !TextUtils.isEmpty(key.mHintLetter)) { - return key.mHintLetter.charAt(0); + if (keyboard.isManualTemporaryUpperCase() && !TextUtils.isEmpty(key.mHintLabel)) { + return key.mHintLabel.charAt(0); } else { return key.mCode; } diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 2850c95df..872fbf823 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -19,17 +19,18 @@ package com.android.inputmethod.keyboard; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.text.TextUtils; 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.KeyboardParser; +import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException; import com.android.inputmethod.keyboard.internal.PopupCharactersParser; import com.android.inputmethod.keyboard.internal.Row; -import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle; -import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException; import com.android.inputmethod.latin.R; import java.util.ArrayList; @@ -45,18 +46,22 @@ public class Key { /** Label to display */ public final CharSequence mLabel; - /** Hint letter to display on the key in conjunction with the label */ - public final CharSequence mHintLetter; + /** Hint label to display on the key in conjunction with the label */ + public final CharSequence mHintLabel; /** Option of the label */ public final int mLabelOption; public static final int LABEL_OPTION_ALIGN_LEFT = 0x01; public static final int LABEL_OPTION_ALIGN_RIGHT = 0x02; - public static final int LABEL_OPTION_ALIGN_BOTTOM = 0x08; - public static final int LABEL_OPTION_FONT_NORMAL = 0x10; - public static final int LABEL_OPTION_FONT_FIXED_WIDTH = 0x20; - public static final int LABEL_OPTION_FOLLOW_KEY_LETTER_RATIO = 0x40; - private static final int LABEL_OPTION_POPUP_HINT = 0x80; - private static final int LABEL_OPTION_HAS_UPPERCASE_LETTER = 0x100; + public static final int LABEL_OPTION_ALIGN_BOTTOM = 0x04; + public static final int LABEL_OPTION_ALIGN_LEFT_OF_CENTER = 0x08; + private static final int LABEL_OPTION_LARGE_LETTER = 0x10; + private static final int LABEL_OPTION_FONT_NORMAL = 0x20; + private static final int LABEL_OPTION_FONT_MONO_SPACE = 0x40; + private static final int LABEL_OPTION_FOLLOW_KEY_LETTER_RATIO = 0x80; + private static final int LABEL_OPTION_FOLLOW_KEY_HINT_LABEL_RATIO = 0x100; + private static final int LABEL_OPTION_HAS_POPUP_HINT = 0x200; + private static final int LABEL_OPTION_HAS_UPPERCASE_LETTER = 0x400; + private static final int LABEL_OPTION_HAS_HINT_LABEL = 0x800; /** Icon to display instead of a label. Icon takes precedence over a label */ private Drawable mIcon; @@ -160,7 +165,7 @@ public class Key { mVisualInsetsLeft = mVisualInsetsRight = 0; mWidth = width - mGap; mEdgeFlags = edgeFlags; - mHintLetter = null; + mHintLabel = null; mLabelOption = 0; mFunctional = false; mSticky = false; @@ -282,7 +287,7 @@ public class Key { keyAttr, R.styleable.Keyboard_Key_keyIcon, KeyboardIconsSet.ICON_UNDEFINED)); Keyboard.setDefaultBounds(mIcon); - mHintLetter = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLetter); + mHintLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel); mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel); mLabelOption = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption, 0); @@ -309,14 +314,43 @@ public class Key { } } + public Typeface selectTypeface(Typeface defaultTypeface) { + // TODO: Handle "bold" here too? + if ((mLabelOption & LABEL_OPTION_FONT_NORMAL) != 0) { + return Typeface.DEFAULT; + } else if ((mLabelOption & LABEL_OPTION_FONT_MONO_SPACE) != 0) { + return Typeface.MONOSPACE; + } else { + return defaultTypeface; + } + } + + public int selectTextSize(int letter, int largeLetter, int label, int hintLabel) { + if (mLabel.length() > 1 + && (mLabelOption & (LABEL_OPTION_FOLLOW_KEY_LETTER_RATIO + | LABEL_OPTION_FOLLOW_KEY_HINT_LABEL_RATIO)) == 0) { + return label; + } else if ((mLabelOption & LABEL_OPTION_FOLLOW_KEY_HINT_LABEL_RATIO) != 0) { + return hintLabel; + } else if ((mLabelOption & LABEL_OPTION_LARGE_LETTER) != 0) { + return largeLetter; + } else { + return letter; + } + } + public boolean hasPopupHint() { - return (mLabelOption & LABEL_OPTION_POPUP_HINT) != 0; + return (mLabelOption & LABEL_OPTION_HAS_POPUP_HINT) != 0; } public boolean hasUppercaseLetter() { return (mLabelOption & LABEL_OPTION_HAS_UPPERCASE_LETTER) != 0; } + public boolean hasHintLabel() { + return (mLabelOption & LABEL_OPTION_HAS_HINT_LABEL) != 0; + } + private static boolean isDigitPopupCharacter(CharSequence label) { return label != null && label.length() == 1 && Character.isDigit(label.charAt(0)); } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 6a60a9ddf..da738b24c 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -66,16 +66,20 @@ import java.util.WeakHashMap; * @attr ref R.styleable#KeyboardView_keyBackground * @attr ref R.styleable#KeyboardView_keyHysteresisDistance * @attr ref R.styleable#KeyboardView_keyLetterRatio + * @attr ref R.styleable#KeyboardView_keyLargeLetterRatio * @attr ref R.styleable#KeyboardView_keyLabelRatio * @attr ref R.styleable#KeyboardView_keyHintLetterRatio * @attr ref R.styleable#KeyboardView_keyUppercaseLetterRatio + * @attr ref R.styleable#KeyboardView_keyHintLabelRatio * @attr ref R.styleable#KeyboardView_keyTextStyle * @attr ref R.styleable#KeyboardView_keyPreviewLayout + * @attr ref R.styleable#KeyboardView_keyPreviewTextRatio * @attr ref R.styleable#KeyboardView_keyPreviewOffset * @attr ref R.styleable#KeyboardView_keyPreviewHeight * @attr ref R.styleable#KeyboardView_keyTextColor * @attr ref R.styleable#KeyboardView_keyTextColorDisabled * @attr ref R.styleable#KeyboardView_keyHintLetterColor + * @attr ref R.styleable#KeyboardView_keyHintLabelColor * @attr ref R.styleable#KeyboardView_keyUppercaseLetterInactivatedColor * @attr ref R.styleable#KeyboardView_keyUppercaseLetterActivatedColor * @attr ref R.styleable#KeyboardView_verticalCorrection @@ -96,42 +100,51 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { // Miscellaneous constants private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable }; - private static final int HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL = -1; // XML attribute - private final float mKeyLetterRatio; private final int mKeyTextColor; private final int mKeyTextInactivatedColor; private final Typeface mKeyTextStyle; + private final float mKeyLetterRatio; + private final float mKeyLargeLetterRatio; private final float mKeyLabelRatio; private final float mKeyHintLetterRatio; private final float mKeyUppercaseLetterRatio; + private final float mKeyHintLabelRatio; private final int mShadowColor; private final float mShadowRadius; private final Drawable mKeyBackground; private final float mBackgroundDimAmount; private final float mKeyHysteresisDistance; private final float mVerticalCorrection; + private final Drawable mPreviewBackground; + private final Drawable mPreviewLeftBackground; + private final Drawable mPreviewRightBackground; + private final Drawable mPreviewSpacebarBackground; + private final int mPreviewTextColor; + private final float mPreviewTextRatio; private final int mPreviewOffset; private final int mPreviewHeight; private final int mPopupLayout; - private final Drawable mKeyPopupHintIcon; private final int mKeyHintLetterColor; + private final int mKeyHintLabelColor; private final int mKeyUppercaseLetterInactivatedColor; private final int mKeyUppercaseLetterActivatedColor; + // HORIZONTAL ELLIPSIS "...", character for popup hint. + private static final String POPUP_HINT_CHAR = "\u2026"; + // Main keyboard private Keyboard mKeyboard; private int mKeyLetterSize; + private int mKeyLargeLetterSize; private int mKeyLabelSize; private int mKeyHintLetterSize; private int mKeyUppercaseLetterSize; + private int mKeyHintLabelSize; // Key preview - private boolean mInForeground; - private TextView mPreviewText; - private Drawable mPreviewBackground; - private float mPreviewTextRatio; + private final TextView mPreviewText; private int mPreviewTextSize; private boolean mShowKeyPreviewPopup = true; private final int mDelayBeforePreview; @@ -182,7 +195,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { private Canvas mCanvas; private final Paint mPaint = new Paint(); private final Rect mPadding = new Rect(); - private final Rect mTextBounds = new Rect(); // This map caches key label text height in pixel as value and key label text size as map key. private final HashMap<Integer, Integer> mTextHeightCache = new HashMap<Integer, Integer>(); // This map caches key label text width in pixel as value and key label text size as map key. @@ -193,6 +205,9 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { private static final String KEY_LABEL_REFERENCE_CHAR = "M"; private final int mKeyLabelHorizontalPadding; + private static final int MEASURESPEC_UNSPECIFIED = MeasureSpec.makeMeasureSpec( + 0, MeasureSpec.UNSPECIFIED); + private final UIHandler mHandler = new UIHandler(this); public static class UIHandler extends StaticInnerHandlerWrapper<KeyboardView> { @@ -334,19 +349,35 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { R.styleable.KeyboardView_keyHysteresisDistance, 0); mVerticalCorrection = a.getDimensionPixelOffset( R.styleable.KeyboardView_verticalCorrection, 0); + mPreviewTextColor = a.getColor(R.styleable.KeyboardView_keyPreviewTextColor, 0); final int previewLayout = a.getResourceId(R.styleable.KeyboardView_keyPreviewLayout, 0); + if (previewLayout != 0) { + mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null); + mPreviewText.setTextColor(mPreviewTextColor); + } else { + mPreviewText = null; + mShowKeyPreviewPopup = false; + } + mPreviewBackground = a.getDrawable(R.styleable.KeyboardView_keyPreviewBackground); + mPreviewLeftBackground = a.getDrawable(R.styleable.KeyboardView_keyPreviewLeftBackground); + mPreviewRightBackground = a.getDrawable(R.styleable.KeyboardView_keyPreviewRightBackground); + mPreviewSpacebarBackground = a.getDrawable( + R.styleable.KeyboardView_keyPreviewSpacebarBackground); mPreviewOffset = a.getDimensionPixelOffset(R.styleable.KeyboardView_keyPreviewOffset, 0); mPreviewHeight = a.getDimensionPixelSize(R.styleable.KeyboardView_keyPreviewHeight, 80); mKeyLetterRatio = getRatio(a, R.styleable.KeyboardView_keyLetterRatio); + mKeyLargeLetterRatio = getRatio(a, R.styleable.KeyboardView_keyLargeLetterRatio); mKeyLabelRatio = getRatio(a, R.styleable.KeyboardView_keyLabelRatio); mKeyHintLetterRatio = getRatio(a, R.styleable.KeyboardView_keyHintLetterRatio); mKeyUppercaseLetterRatio = getRatio(a, R.styleable.KeyboardView_keyUppercaseLetterRatio); + mKeyHintLabelRatio = getRatio(a, R.styleable.KeyboardView_keyHintLabelRatio); + mPreviewTextRatio = getRatio(a, R.styleable.KeyboardView_keyPreviewTextRatio); mKeyTextColor = a.getColor(R.styleable.KeyboardView_keyTextColor, 0xFF000000); mKeyTextInactivatedColor = a.getColor( R.styleable.KeyboardView_keyTextInactivatedColor, 0xFF000000); - mKeyPopupHintIcon = a.getDrawable(R.styleable.KeyboardView_keyPopupHintIcon); mKeyHintLetterColor = a.getColor(R.styleable.KeyboardView_keyHintLetterColor, 0); + mKeyHintLabelColor = a.getColor(R.styleable.KeyboardView_keyHintLabelColor, 0); mKeyUppercaseLetterInactivatedColor = a.getColor( R.styleable.KeyboardView_keyUppercaseLetterInactivatedColor, 0); mKeyUppercaseLetterActivatedColor = a.getColor( @@ -362,13 +393,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { final Resources res = getResources(); - if (previewLayout != 0) { - mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null); - mPreviewBackground = mPreviewText.getBackground(); - mPreviewTextRatio = getRatio(res, R.fraction.key_preview_text_ratio); - } else { - mShowKeyPreviewPopup = false; - } mDelayBeforePreview = res.getInteger(R.integer.config_delay_before_preview); mDelayAfterPreview = res.getInteger(R.integer.config_delay_after_preview); mKeyLabelHorizontalPadding = (int)res.getDimension( @@ -462,11 +486,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { return a.getFraction(index, 1000, 1000, 1) / 1000.0f; } - // Read fraction value in resource as float. - private static float getRatio(Resources res, int id) { - return res.getFraction(id, 1000, 1000) / 1000.0f; - } - public void startIgnoringDoubleTap() { if (ENABLE_CAPSLOCK_BY_DOUBLETAP) mHandler.startIgnoringDoubleTap(); @@ -521,10 +540,12 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { mPopupPanelCache.clear(); final int keyHeight = keyboard.getRowHeight() - keyboard.getVerticalGap(); mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio); + mKeyLargeLetterSize = (int)(keyHeight * mKeyLargeLetterRatio); mKeyLabelSize = (int)(keyHeight * mKeyLabelRatio); mKeyHintLetterSize = (int)(keyHeight * mKeyHintLetterRatio); mKeyUppercaseLetterSize = (int)( keyHeight * mKeyUppercaseLetterRatio); + mKeyHintLabelSize = (int)(keyHeight * mKeyHintLabelRatio); mPreviewTextSize = (int)(keyHeight * mPreviewTextRatio); } @@ -587,7 +608,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { protected CharSequence adjustCase(CharSequence label) { if (mKeyboard.isShiftedOrShiftLocked() && label != null && label.length() < 3 && Character.isLowerCase(label.charAt(0))) { - return label.toString().toUpperCase(); + return label.toString().toUpperCase(mKeyboard.mId.mLocale); } return label; } @@ -689,6 +710,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { final int kbdPaddingTop = getPaddingTop(); final int keyDrawX = key.mX + key.mVisualInsetsLeft; final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight; + final int centerX = (keyDrawWidth + padding.left - padding.right) / 2; + final float centerY = (key.mHeight + padding.top - padding.bottom) / 2; final int rowHeight = padding.top + key.mHeight; final boolean isManualTemporaryUpperCase = mKeyboard.isManualTemporaryUpperCase(); @@ -704,47 +727,48 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { keyBackground.draw(canvas); // Draw key label. + int positionX = centerX; if (key.mLabel != null) { // Switch the character to uppercase if shift is pressed - final String label = key.mLabel == null ? null : adjustCase(key.mLabel).toString(); - // For characters, use large font. For labels like "Done", use small font. - final int labelSize = getLabelSizeAndSetPaint(label, key.mLabelOption, paint); - final int labelCharHeight = getLabelCharHeight(labelSize, paint); + final CharSequence label = key.mLabel == null ? null : adjustCase(key.mLabel); + // For characters, use large font. For labels like "Done", use smaller font. + paint.setTypeface(key.selectTypeface(mKeyTextStyle)); + final int labelSize = key.selectTextSize(mKeyLetterSize, mKeyLargeLetterSize, + mKeyLabelSize, mKeyHintLabelSize); + paint.setTextSize(labelSize); + final int labelCharHeight = getLabelCharHeight(paint); + final int labelCharWidth = getLabelCharWidth(paint); // Vertical label text alignment. final float baseline; + // TODO: Generalize the following calculations. if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_BOTTOM) != 0) { baseline = key.mHeight - labelCharHeight * KEY_LABEL_VERTICAL_PADDING_FACTOR; - if (DEBUG_SHOW_ALIGN) - drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000, - new Paint()); } else { // Align center - final float centerY = (key.mHeight + padding.top - padding.bottom) / 2; baseline = centerY + labelCharHeight * KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER; - if (DEBUG_SHOW_ALIGN) - drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000, - new Paint()); } + // Horizontal label text alignment - final int positionX; if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_LEFT) != 0) { - positionX = mKeyLabelHorizontalPadding + padding.left; + positionX = padding.left + mKeyLabelHorizontalPadding; paint.setTextAlign(Align.LEFT); - if (DEBUG_SHOW_ALIGN) - drawVerticalLine(canvas, positionX, rowHeight, 0xc0800080, new Paint()); } else if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_RIGHT) != 0) { positionX = keyDrawWidth - mKeyLabelHorizontalPadding - padding.right; paint.setTextAlign(Align.RIGHT); - if (DEBUG_SHOW_ALIGN) - drawVerticalLine(canvas, positionX, rowHeight, 0xc0808000, new Paint()); + } else if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_LEFT_OF_CENTER) != 0) { + // TODO: Parameterise this? + positionX = centerX - labelCharWidth * 7 / 4; + paint.setTextAlign(Align.LEFT); } else { - positionX = (keyDrawWidth + padding.left - padding.right) / 2; + positionX = centerX; paint.setTextAlign(Align.CENTER); - if (DEBUG_SHOW_ALIGN) { - if (label.length() > 1) - drawVerticalLine(canvas, positionX, rowHeight, 0xc0008080, new Paint()); - } } + if (DEBUG_SHOW_ALIGN) { + final Paint line = new Paint(); + drawHorizontalLine(canvas, (int)baseline, keyDrawWidth, 0xc0008000, line); + drawVerticalLine(canvas, positionX, rowHeight, 0xc0800080, line); + } + if (key.hasUppercaseLetter() && isManualTemporaryUpperCase) { paint.setColor(mKeyTextInactivatedColor); } else { @@ -757,124 +781,128 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { // Make label invisible paint.setColor(Color.TRANSPARENT); } - canvas.drawText(label, positionX, baseline, paint); + canvas.drawText(label, 0, label.length(), positionX, baseline, paint); // Turn off drop shadow paint.setShadowLayer(0, 0, 0, 0); + } - // Draw hint letter. - if (key.mHintLetter != null) { - final String label = key.mHintLetter.toString(); - final int textColor; - final int textSize; + // Draw hint label. + if (key.mHintLabel != null) { + final CharSequence hint = key.mHintLabel; + final int hintColor; + final int hintSize; if (key.hasUppercaseLetter()) { - textColor = isManualTemporaryUpperCase ? mKeyUppercaseLetterActivatedColor + hintColor = isManualTemporaryUpperCase ? mKeyUppercaseLetterActivatedColor : mKeyUppercaseLetterInactivatedColor; - textSize = mKeyUppercaseLetterSize; + hintSize = mKeyUppercaseLetterSize; + } else if (key.hasHintLabel()) { + hintColor = mKeyHintLabelColor; + hintSize = mKeyHintLabelSize; + paint.setTypeface(Typeface.DEFAULT); } else { - textColor = mKeyHintLetterColor; - textSize = mKeyHintLetterSize; + hintColor = mKeyHintLetterColor; + hintSize = mKeyHintLetterSize; } - paint.setColor(textColor); - paint.setTextSize(textSize); + paint.setColor(hintColor); + paint.setTextSize(hintSize); // Note: padding.right for drawX? - final float drawX = keyDrawWidth - getLabelCharWidth(textSize, paint); - final float drawY = -paint.ascent() + padding.top; - canvas.drawText(label, drawX, drawY, paint); + final float hintX, hintY; + if (key.hasHintLabel()) { + // TODO: Generalize the following calculations. + hintX = positionX + getLabelCharWidth(paint) * 2; + hintY = centerY + getLabelCharHeight(paint) / 2; + } else { + hintX = keyDrawWidth - getLabelCharWidth(paint); + hintY = -paint.ascent() + padding.top; + } + canvas.drawText(hint, 0, hint.length(), hintX, hintY, paint); } // Draw key icon. final Drawable icon = key.getIcon(); if (key.mLabel == null && icon != null) { - final int drawableWidth = icon.getIntrinsicWidth(); - final int drawableHeight = icon.getIntrinsicHeight(); - final int drawableX; - final int drawableY = (key.mHeight + padding.top - padding.bottom - drawableHeight) / 2; + final int iconWidth = icon.getIntrinsicWidth(); + final int iconHeight = icon.getIntrinsicHeight(); + final int iconX, alignX; + final int iconY = (key.mHeight + padding.top - padding.bottom - iconHeight) / 2; if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_LEFT) != 0) { - drawableX = padding.left + mKeyLabelHorizontalPadding; - if (DEBUG_SHOW_ALIGN) - drawVerticalLine(canvas, drawableX, rowHeight, 0xc0800080, new Paint()); + iconX = padding.left + mKeyLabelHorizontalPadding; + alignX = iconX; } else if ((key.mLabelOption & Key.LABEL_OPTION_ALIGN_RIGHT) != 0) { - drawableX = keyDrawWidth - padding.right - mKeyLabelHorizontalPadding - - drawableWidth; - if (DEBUG_SHOW_ALIGN) - drawVerticalLine(canvas, drawableX + drawableWidth, rowHeight, - 0xc0808000, new Paint()); + iconX = keyDrawWidth - padding.right - mKeyLabelHorizontalPadding - iconWidth; + alignX = iconX + iconWidth; } else { // Align center - drawableX = (keyDrawWidth + padding.left - padding.right - drawableWidth) / 2; - if (DEBUG_SHOW_ALIGN) - drawVerticalLine(canvas, drawableX + drawableWidth / 2, rowHeight, - 0xc0008080, new Paint()); + iconX = (keyDrawWidth + padding.left - padding.right - iconWidth) / 2; + alignX = iconX + iconWidth / 2; + } + drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); + if (DEBUG_SHOW_ALIGN) { + final Paint line = new Paint(); + drawVerticalLine(canvas, alignX, rowHeight, 0xc0800080, line); + drawRectangle(canvas, iconX, iconY, iconWidth, iconHeight, 0x80c00000, line); } - drawIcon(canvas, icon, drawableX, drawableY, drawableWidth, drawableHeight); - if (DEBUG_SHOW_ALIGN) - drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight, - 0x80c00000, new Paint()); } - // Draw popup hint icon "...". - // TODO: Draw "..." by text. + // Draw popup hint "..." at the bottom right corner of the key. if (key.hasPopupHint()) { - final int drawableWidth = keyDrawWidth; - final int drawableHeight = key.mHeight; - final int drawableX = 0; - final int drawableY = HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL; - final Drawable hintIcon = mKeyPopupHintIcon; - drawIcon(canvas, hintIcon, drawableX, drawableY, drawableWidth, drawableHeight); - if (DEBUG_SHOW_ALIGN) - drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight, - 0x80c0c000, new Paint()); + paint.setTextSize(mKeyHintLetterSize); + paint.setColor(mKeyHintLabelColor); + final int hintX = keyDrawWidth - getLabelCharWidth(paint); + // Using y-coordinate "key.mHeight - paint.descent()" draws "..." just on the bottom + // edge of the key. So we use slightly higher position by multiply descent length by 2. + final int hintY = key.mHeight - (int)paint.descent() * 2; + canvas.drawText(POPUP_HINT_CHAR, hintX, hintY, paint); } canvas.translate(-keyDrawX - kbdPaddingLeft, -key.mY - kbdPaddingTop); } - public int getLabelSizeAndSetPaint(CharSequence label, int keyLabelOption, Paint paint) { + // This method is currently being used only by MiniKeyboardBuilder + public int getDefaultLabelSizeAndSetPaint(Paint paint) { // For characters, use large font. For labels like "Done", use small font. - final int labelSize; - final Typeface labelStyle; - if ((keyLabelOption & Key.LABEL_OPTION_FONT_NORMAL) != 0) { - labelStyle = Typeface.DEFAULT; - } else if ((keyLabelOption & Key.LABEL_OPTION_FONT_FIXED_WIDTH) != 0) { - labelStyle = Typeface.MONOSPACE; - } else { - labelStyle = mKeyTextStyle; - } - if (label.length() > 1) { - labelSize = (keyLabelOption & Key.LABEL_OPTION_FOLLOW_KEY_LETTER_RATIO) != 0 - ? mKeyLetterSize : mKeyLabelSize; - } else { - labelSize = mKeyLetterSize; - } + final int labelSize = mKeyLabelSize; paint.setTextSize(labelSize); - paint.setTypeface(labelStyle); + paint.setTypeface(mKeyTextStyle); return labelSize; } - private int getLabelCharHeight(int labelSize, Paint paint) { - Integer labelHeightValue = mTextHeightCache.get(labelSize); - final int labelCharHeight; - if (labelHeightValue != null) { - labelCharHeight = labelHeightValue; - } else { - paint.getTextBounds(KEY_LABEL_REFERENCE_CHAR, 0, 1, mTextBounds); - labelCharHeight = mTextBounds.height(); - mTextHeightCache.put(labelSize, labelCharHeight); - } - return labelCharHeight; + private final Rect mTextBounds = new Rect(); + + private int getLabelCharHeight(Paint paint) { + final int labelSize = (int)paint.getTextSize(); + final Integer cachedValue = mTextHeightCache.get(labelSize); + if (cachedValue != null) + return cachedValue; + + paint.getTextBounds(KEY_LABEL_REFERENCE_CHAR, 0, 1, mTextBounds); + final int height = mTextBounds.height(); + mTextHeightCache.put(labelSize, height); + return height; } - private int getLabelCharWidth(int labelSize, Paint paint) { - Integer labelWidthValue = mTextWidthCache.get(labelSize); - final int labelCharWidth; - if (labelWidthValue != null) { - labelCharWidth = labelWidthValue; + private int getLabelCharWidth(Paint paint) { + final int labelSize = (int)paint.getTextSize(); + final Typeface face = paint.getTypeface(); + final Integer key; + if (face == Typeface.DEFAULT) { + key = labelSize; + } else if (face == Typeface.DEFAULT_BOLD) { + key = labelSize + 1000; + } else if (face == Typeface.MONOSPACE) { + key = labelSize + 2000; } else { - paint.getTextBounds(KEY_LABEL_REFERENCE_CHAR, 0, 1, mTextBounds); - labelCharWidth = mTextBounds.width(); - mTextWidthCache.put(labelSize, labelCharWidth); + key = labelSize; } - return labelCharWidth; + + final Integer cached = mTextWidthCache.get(key); + if (cached != null) + return cached; + + paint.getTextBounds(KEY_LABEL_REFERENCE_CHAR, 0, 1, mTextBounds); + final int width = mTextBounds.width(); + mTextWidthCache.put(key, width); + return width; } private static void drawIcon(Canvas canvas, Drawable icon, int x, int y, int width, @@ -909,10 +937,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { canvas.translate(-x, -y); } - public void setForeground(boolean foreground) { - mInForeground = foreground; - } - // TODO: clean up this method. private void dismissAllKeyPreviews() { for (PointerTracker tracker : mPointerTrackers) { @@ -921,6 +945,10 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { } } + public void cancelAllMessage() { + mHandler.cancelAllMessages(); + } + @Override public void showKeyPreview(int keyIndex, PointerTracker tracker) { if (mShowKeyPreviewPopup) { @@ -965,7 +993,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { // If keyIndex is invalid or IME is already closed, we must not show key preview. // Trying to show key preview while root window is closed causes // WindowManager.BadTokenException. - if (key == null || !mInForeground) + if (key == null) return; mHandler.cancelAllDismissKeyPreviews(); @@ -991,25 +1019,29 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { previewText.setText(null); } if (key.mCode == Keyboard.CODE_SPACE) { - previewText.setBackgroundColor(Color.TRANSPARENT); + previewText.setBackgroundDrawable(mPreviewSpacebarBackground); } else { previewText.setBackgroundDrawable(mPreviewBackground); } - // Set the preview background state - previewText.getBackground().setState( - key.mPopupCharacters != null ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET); - previewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + previewText.measure(MEASURESPEC_UNSPECIFIED, MEASURESPEC_UNSPECIFIED); final int previewWidth = Math.max(previewText.getMeasuredWidth(), keyDrawWidth + previewText.getPaddingLeft() + previewText.getPaddingRight()); final int previewHeight = mPreviewHeight; getLocationInWindow(mCoordinates); - final int previewX = keyDrawX - (previewWidth - keyDrawWidth) / 2 + mCoordinates[0]; + int previewX = keyDrawX - (previewWidth - keyDrawWidth) / 2 + mCoordinates[0]; final int previewY = key.mY - previewHeight + mCoordinates[1] + mPreviewOffset; + if (previewX < 0 && mPreviewLeftBackground != null) { + previewText.setBackgroundDrawable(mPreviewLeftBackground); + previewX = 0; + } else if (previewX + previewWidth > getWidth() && mPreviewRightBackground != null) { + previewText.setBackgroundDrawable(mPreviewRightBackground); + previewX = getWidth() - previewWidth; + } - // Place the key preview. - // TODO: Adjust position of key previews which touch screen edges + // Set the preview background state + previewText.getBackground().setState( + key.mPopupCharacters != null ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET); FrameLayoutCompatUtils.placeViewAt( previewText, previewX, previewY, previewWidth, previewHeight); previewText.setVisibility(VISIBLE); @@ -1126,7 +1158,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { miniKeyboardView.setKeyboard(keyboard); container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + MEASURESPEC_UNSPECIFIED); return miniKeyboardView; } diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java index 00bf348f2..9d58f69ff 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java @@ -23,9 +23,6 @@ import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.ColorFilter; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.PorterDuff; @@ -61,8 +58,6 @@ public class LatinKeyboard extends Keyboard { private final int mSpaceKeyIndex; private final boolean mAutoCorrectionSpacebarLedEnabled; private final Drawable mAutoCorrectionSpacebarLedIcon; - private final Drawable mSpacebarArrowLeftIcon; - private final Drawable mSpacebarArrowRightIcon; private final int mSpacebarTextColor; private final int mSpacebarTextShadowColor; private float mSpacebarTextFadeFactor = 0.0f; @@ -77,6 +72,11 @@ public class LatinKeyboard extends Keyboard { private final Drawable mEnabledShortcutIcon; private final Drawable mDisabledShortcutIcon; + // BLACK LEFT-POINTING TRIANGLE and two spaces. + public static final String ARROW_LEFT = "\u25C0 "; + // Two spaces and BLACK RIGHT-POINTING TRIANGLE. + public static final String ARROW_RIGHT = " \u25B6"; + // Minimum width of spacebar dragging to trigger the language switch (represented by the number // of the most common key width of this keyboard). private static final int SPACEBAR_DRAG_WIDTH = 3; @@ -131,10 +131,6 @@ public class LatinKeyboard extends Keyboard { mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboard_spacebarTextColor, 0); mSpacebarTextShadowColor = a.getColor( R.styleable.LatinKeyboard_spacebarTextShadowColor, 0); - mSpacebarArrowLeftIcon = a.getDrawable( - R.styleable.LatinKeyboard_spacebarArrowLeftIcon); - mSpacebarArrowRightIcon = a.getDrawable( - R.styleable.LatinKeyboard_spacebarArrowRightIcon); a.recycle(); // The threshold is "key width" x 1.25 @@ -165,12 +161,6 @@ public class LatinKeyboard extends Keyboard { return newColor; } - private static ColorFilter getSpacebarDrawableFilter(float fadeFactor) { - final ColorMatrix colorMatrix = new ColorMatrix(); - colorMatrix.setScale(1, 1, 1, fadeFactor); - return new ColorMatrixColorFilter(colorMatrix); - } - public void updateShortcutKey(boolean available, LatinKeyboardView view) { if (mShortcutKey == null) return; @@ -214,52 +204,40 @@ public class LatinKeyboard extends Keyboard { } // Layout local language name and left and right arrow on spacebar. - private static String layoutSpacebar(Paint paint, Locale locale, Drawable icon, Drawable lArrow, - Drawable rArrow, int width, int height, float origTextSize) { - final float arrowWidth = lArrow.getIntrinsicWidth(); - final float arrowHeight = lArrow.getIntrinsicHeight(); - final float maxTextWidth = width - (arrowWidth + arrowWidth); + private static String layoutSpacebar(Paint paint, Locale locale, int width, + float origTextSize) { final Rect bounds = new Rect(); // Estimate appropriate language name text size to fit in maxTextWidth. - String language = SubtypeSwitcher.getFullDisplayName(locale, true); + String language = ARROW_LEFT + SubtypeSwitcher.getFullDisplayName(locale, true) + + ARROW_RIGHT; int textWidth = getTextWidth(paint, language, origTextSize, bounds); // Assuming text width and text size are proportional to each other. - float textSize = origTextSize * Math.min(maxTextWidth / textWidth, 1.0f); + float textSize = origTextSize * Math.min(width / textWidth, 1.0f); // allow variable text size textWidth = getTextWidth(paint, language, textSize, bounds); // If text size goes too small or text does not fit, use middle or short name final boolean useMiddleName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME) - || (textWidth > maxTextWidth); + || (textWidth > width); final boolean useShortName; if (useMiddleName) { - language = SubtypeSwitcher.getMiddleDisplayLanguage(locale); + language = ARROW_LEFT + SubtypeSwitcher.getMiddleDisplayLanguage(locale) + ARROW_RIGHT; textWidth = getTextWidth(paint, language, origTextSize, bounds); - textSize = origTextSize * Math.min(maxTextWidth / textWidth, 1.0f); + textSize = origTextSize * Math.min(width / textWidth, 1.0f); useShortName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME) - || (textWidth > maxTextWidth); + || (textWidth > width); } else { useShortName = false; } if (useShortName) { - language = SubtypeSwitcher.getShortDisplayLanguage(locale); + language = ARROW_LEFT + SubtypeSwitcher.getShortDisplayLanguage(locale) + ARROW_RIGHT; textWidth = getTextWidth(paint, language, origTextSize, bounds); - textSize = origTextSize * Math.min(maxTextWidth / textWidth, 1.0f); + textSize = origTextSize * Math.min(width / textWidth, 1.0f); } paint.setTextSize(textSize); - // Place left and right arrow just before and after language text. - final float textHeight = -paint.ascent() + paint.descent(); - final float baseline = (icon != null) ? height * SPACEBAR_LANGUAGE_BASELINE - : height / 2 + textHeight / 2; - final int top = (int)(baseline - arrowHeight); - final float remains = (width - textWidth) / 2; - lArrow.setBounds((int)(remains - arrowWidth), top, (int)remains, (int)baseline); - rArrow.setBounds((int)(remains + textWidth), top, (int)(remains + textWidth + arrowWidth), - (int)baseline); - return language; } @@ -304,9 +282,8 @@ public class LatinKeyboard extends Keyboard { defaultTextSize = 14; } - final String language = layoutSpacebar(paint, inputLocale, mSpaceIcon, - mSpacebarArrowLeftIcon, mSpacebarArrowRightIcon, width, height, - getTextSizeFromTheme(mTheme, textStyle, defaultTextSize)); + final String language = layoutSpacebar(paint, inputLocale, width, getTextSizeFromTheme( + mTheme, textStyle, defaultTextSize)); // Draw language text with shadow // In case there is no space icon, we will place the language text at the center of @@ -319,18 +296,6 @@ public class LatinKeyboard extends Keyboard { canvas.drawText(language, width / 2, baseline - descent - 1, paint); paint.setColor(getSpacebarTextColor(mSpacebarTextColor, textFadeFactor)); canvas.drawText(language, width / 2, baseline - descent, paint); - - // Put arrows that are already laid out on either side of the text - // Because language switch is disabled on phone and number layouts, hide arrows. - // TODO: Sort out how to enable language switch on these layouts. - if (mSubtypeSwitcher.useSpacebarLanguageSwitcher() - && mSubtypeSwitcher.getEnabledKeyboardLocaleCount() > 1 - && !(isPhoneKeyboard() || isNumberKeyboard())) { - mSpacebarArrowLeftIcon.setColorFilter(getSpacebarDrawableFilter(textFadeFactor)); - mSpacebarArrowRightIcon.setColorFilter(getSpacebarDrawableFilter(textFadeFactor)); - mSpacebarArrowLeftIcon.draw(canvas); - mSpacebarArrowRightIcon.draw(canvas); - } } // Draw the spacebar icon at the bottom diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index c7620f946..8b03360bf 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -670,7 +670,7 @@ public class PointerTracker { // If keyboard is in manual temporary upper case state and key has manual temporary // uppercase letter as key hint letter, alternate character code should be sent. if (mKeyboard.isManualTemporaryUpperCase() && key.hasUppercaseLetter()) { - code = key.mHintLetter.charAt(0); + code = key.mHintLabel.charAt(0); codes[0] = code; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java index 983f0649d..30d9692a8 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java @@ -165,7 +165,7 @@ public class KeyStyles { readInt(keyAttr, R.styleable.Keyboard_Key_code); readText(keyAttr, R.styleable.Keyboard_Key_keyLabel); readText(keyAttr, R.styleable.Keyboard_Key_keyOutputText); - readText(keyAttr, R.styleable.Keyboard_Key_keyHintLetter); + readText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel); readTextArray(keyAttr, R.styleable.Keyboard_Key_popupCharacters); readFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption); readInt(keyAttr, R.styleable.Keyboard_Key_keyIcon); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index 37b36825a..1530fed6f 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -40,25 +40,15 @@ public class KeyboardIconsSet { private static final int ICON_RETURN_KEY = 8; private static final int ICON_SEARCH_KEY = 9; private static final int ICON_TAB_KEY = 10; - private static final int ICON_NUM1_KEY = 11; - private static final int ICON_NUM2_KEY = 12; - private static final int ICON_NUM3_KEY = 13; - private static final int ICON_NUM4_KEY = 14; - private static final int ICON_NUM5_KEY = 15; - private static final int ICON_NUM6_KEY = 16; - private static final int ICON_NUM7_KEY = 17; - private static final int ICON_NUM8_KEY = 18; - private static final int ICON_NUM9_KEY = 19; - private static final int ICON_NUM0_KEY = 20; // This should be aligned with Keyboard.keyIconShifted enum. - private static final int ICON_SHIFTED_SHIFT_KEY = 21; + private static final int ICON_SHIFTED_SHIFT_KEY = 11; // This should be aligned with Keyboard.keyIconPreview enum. - private static final int ICON_PREVIEW_SPACE_KEY = 22; - private static final int ICON_PREVIEW_TAB_KEY = 23; - private static final int ICON_PREVIEW_SETTINGS_KEY = 24; - private static final int ICON_PREVIEW_SHORTCUT_KEY = 25; + private static final int ICON_PREVIEW_SPACE_KEY = 12; + private static final int ICON_PREVIEW_TAB_KEY = 13; + private static final int ICON_PREVIEW_SETTINGS_KEY = 14; + private static final int ICON_PREVIEW_SHORTCUT_KEY = 15; - private static final int ICON_LAST = 25; + private static final int ICON_LAST = 15; private final Drawable mIcons[] = new Drawable[ICON_LAST + 1]; @@ -84,26 +74,6 @@ public class KeyboardIconsSet { return ICON_SEARCH_KEY; case R.styleable.Keyboard_iconTabKey: return ICON_TAB_KEY; - case R.styleable.Keyboard_iconNum1Key: - return ICON_NUM1_KEY; - case R.styleable.Keyboard_iconNum2Key: - return ICON_NUM2_KEY; - case R.styleable.Keyboard_iconNum3Key: - return ICON_NUM3_KEY; - case R.styleable.Keyboard_iconNum4Key: - return ICON_NUM4_KEY; - case R.styleable.Keyboard_iconNum5Key: - return ICON_NUM5_KEY; - case R.styleable.Keyboard_iconNum6Key: - return ICON_NUM6_KEY; - case R.styleable.Keyboard_iconNum7Key: - return ICON_NUM7_KEY; - case R.styleable.Keyboard_iconNum8Key: - return ICON_NUM8_KEY; - case R.styleable.Keyboard_iconNum9Key: - return ICON_NUM9_KEY; - case R.styleable.Keyboard_iconNum0Key: - return ICON_NUM0_KEY; case R.styleable.Keyboard_iconShiftedShiftKey: return ICON_SHIFTED_SHIFT_KEY; case R.styleable.Keyboard_iconPreviewSpaceKey: diff --git a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java index 040c16ded..1e67eec70 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java @@ -221,7 +221,7 @@ public class MiniKeyboardBuilder { paint = new Paint(); paint.setAntiAlias(true); } - final int labelSize = view.getLabelSizeAndSetPaint(label, 0, paint); + final int labelSize = view.getDefaultLabelSizeAndSetPaint(paint); paint.setTextSize(labelSize); if (bounds == null) bounds = new Rect(); paint.getTextBounds(label.toString(), 0, label.length(), bounds); diff --git a/java/src/com/android/inputmethod/keyboard/internal/SlidingLocaleDrawable.java b/java/src/com/android/inputmethod/keyboard/internal/SlidingLocaleDrawable.java index f8c81adfb..ef3ea4c12 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/SlidingLocaleDrawable.java +++ b/java/src/com/android/inputmethod/keyboard/internal/SlidingLocaleDrawable.java @@ -46,8 +46,7 @@ public class SlidingLocaleDrawable extends Drawable { private final int mSpacebarTextColor; private final TextPaint mTextPaint; private final int mMiddleX; - private final Drawable mLeftDrawable; - private final Drawable mRightDrawable; + private final boolean mDrawArrows; private final int mThreshold; private int mDiff; @@ -65,16 +64,17 @@ public class SlidingLocaleDrawable extends Drawable { textPaint.setTextSize(LatinKeyboard.getTextSizeFromTheme( context.getTheme(), android.R.style.TextAppearance_Medium, 18)); textPaint.setColor(Color.TRANSPARENT); - textPaint.setTextAlign(Align.CENTER); textPaint.setAntiAlias(true); mTextPaint = textPaint; mMiddleX = (background != null) ? (mWidth - mBackground.getIntrinsicWidth()) / 2 : 0; final TypedArray a = context.obtainStyledAttributes( - null, R.styleable.LatinKeyboard, R.attr.latinKeyboardStyle, R.style.LatinKeyboard); - mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboard_spacebarTextColor, 0); - mLeftDrawable = a.getDrawable(R.styleable.LatinKeyboard_spacebarArrowPreviewLeftIcon); - mRightDrawable = a.getDrawable(R.styleable.LatinKeyboard_spacebarArrowPreviewRightIcon); + null, R.styleable.KeyboardView, R.attr.keyboardViewStyle, R.style.KeyboardView); + mSpacebarTextColor = a.getColor(R.styleable.KeyboardView_keyPreviewTextColor, 0); + final int spacebarPreviewBackrgound = a.getResourceId( + R.styleable.KeyboardView_keyPreviewSpacebarBackground, 0); + // If spacebar preview background is transparent, we need not draw arrows. + mDrawArrows = (spacebarPreviewBackrgound != R.drawable.transparent); a.recycle(); mThreshold = ViewConfiguration.get(context).getScaledTouchSlop(); @@ -102,8 +102,6 @@ public class SlidingLocaleDrawable extends Drawable { final int width = mWidth; final int height = mHeight; final int diff = mDiff; - final Drawable lArrow = mLeftDrawable; - final Drawable rArrow = mRightDrawable; canvas.clipRect(0, 0, width, height); if (mCurrentLanguage == null) { SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance(); @@ -111,20 +109,19 @@ public class SlidingLocaleDrawable extends Drawable { mNextLanguage = subtypeSwitcher.getNextInputLanguageName(); mPrevLanguage = subtypeSwitcher.getPreviousInputLanguageName(); } - // Draw language text with shadow + // Draw language text. final float baseline = mHeight * LatinKeyboard.SPACEBAR_LANGUAGE_BASELINE - paint.descent(); paint.setColor(mSpacebarTextColor); + paint.setTextAlign(Align.CENTER); canvas.drawText(mCurrentLanguage, width / 2 + diff, baseline, paint); canvas.drawText(mNextLanguage, diff - width / 2, baseline, paint); canvas.drawText(mPrevLanguage, diff + width + width / 2, baseline, paint); - - if (lArrow != null && rArrow != null) { - Keyboard.setDefaultBounds(lArrow); - rArrow.setBounds(width - rArrow.getIntrinsicWidth(), 0, width, - rArrow.getIntrinsicHeight()); - lArrow.draw(canvas); - rArrow.draw(canvas); + if (mDrawArrows) { + paint.setTextAlign(Align.LEFT); + canvas.drawText(LatinKeyboard.ARROW_LEFT, 0, baseline, paint); + paint.setTextAlign(Align.RIGHT); + canvas.drawText(LatinKeyboard.ARROW_RIGHT, width, baseline, paint); } } if (mBackground != null) { diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java index 313555daa..a5bfea0f8 100644 --- a/java/src/com/android/inputmethod/latin/CandidateView.java +++ b/java/src/com/android/inputmethod/latin/CandidateView.java @@ -25,11 +25,11 @@ import android.os.Message; import android.text.Spannable; import android.text.SpannableString; import android.text.Spanned; +import android.text.TextPaint; import android.text.TextUtils; import android.text.style.BackgroundColorSpan; import android.text.style.CharacterStyle; import android.text.style.ForegroundColorSpan; -import android.text.style.StyleSpan; import android.text.style.UnderlineSpan; import android.util.AttributeSet; import android.view.Gravity; @@ -57,15 +57,15 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo public void pickSuggestionManually(int index, CharSequence word); } - private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}. private static final int MAX_SUGGESTIONS = 18; - private static final int UNSPECIFIED_MEASURESPEC = MeasureSpec.makeMeasureSpec( - 0, MeasureSpec.UNSPECIFIED); + private static final int MATCH_PARENT = MeasureSpec.makeMeasureSpec( + -1, MeasureSpec.UNSPECIFIED); private static final boolean DBG = LatinImeLogger.sDBG; + private final View mCandidatesStrip; private static final int NUM_CANDIDATES_IN_STRIP = 3; private final ImageView mExpandCandidatesPane; private final ImageView mCloseCandidatesPane; @@ -88,11 +88,17 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo private final PopupWindow mPreviewPopup; private final TextView mPreviewText; + private final View mTouchToSave; + private final TextView mWordToSave; + private Listener mListener; private SuggestedWords mSuggestions = SuggestedWords.EMPTY; private boolean mShowingAutoCorrectionInverted; private boolean mShowingAddToDictionary; + private static final float MIN_TEXT_XSCALE = 0.4f; + private static final String ELLIPSIS = "\u2026"; + private final UiHandler mHandler = new UiHandler(this); private static class UiHandler extends StaticInnerHandlerWrapper<CandidateView> { @@ -178,13 +184,13 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo mPreviewPopup.setContentView(mPreviewText); mPreviewPopup.setBackgroundDrawable(null); + mCandidatesStrip = findViewById(R.id.candidates_strip); mCandidateStripHeight = res.getDimensionPixelOffset(R.dimen.candidate_strip_height); for (int i = 0; i < MAX_SUGGESTIONS; i++) { final TextView word, info; switch (i) { case 0: word = (TextView)findViewById(R.id.word_left); - word.setPadding(res.getDimensionPixelOffset(R.dimen.candidate_padding), 0, 0, 0); info = (TextView)findViewById(R.id.info_left); break; case 1: @@ -208,11 +214,15 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo mInfos.add(info); if (i > 0) { final View divider = inflater.inflate(R.layout.candidate_divider, null); - divider.measure(UNSPECIFIED_MEASURESPEC, UNSPECIFIED_MEASURESPEC); + divider.measure(MATCH_PARENT, MATCH_PARENT); mDividers.add(divider); } } + mTouchToSave = findViewById(R.id.touch_to_save); + mWordToSave = (TextView)findViewById(R.id.word_to_save); + mWordToSave.setOnClickListener(this); + final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle); mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0); @@ -269,12 +279,14 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo } } - private CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect) { + private CharSequence getStyledCandidateWord(CharSequence word, TextView v, + boolean isAutoCorrect) { + v.setTypeface(Typeface.DEFAULT); if (!isAutoCorrect) return word; final Spannable spannedWord = new SpannableString(word); if ((mAutoCorrectHighlight & AUTO_CORRECT_BOLD) != 0) - spannedWord.setSpan(BOLD_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + v.setTypeface(Typeface.DEFAULT_BOLD); if ((mAutoCorrectHighlight & AUTO_CORRECT_UNDERLINE) != 0) spannedWord.setSpan(UNDERLINE_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); return spannedWord; @@ -311,7 +323,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo int fromIndex = NUM_CANDIDATES_IN_STRIP; final int count = Math.min(mWords.size(), suggestions.size()); closeCandidatesPane(); - mExpandCandidatesPane.setEnabled(count >= NUM_CANDIDATES_IN_STRIP); + mExpandCandidatesPane.setVisibility(count > NUM_CANDIDATES_IN_STRIP ? VISIBLE : GONE); for (int i = 0; i < count; i++) { final CharSequence suggestion = suggestions.getWord(i); if (suggestion == null) continue; @@ -332,11 +344,15 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo // the word when space is typed (valid typed word or auto corrected word). word.setTextColor(getCandidateTextColor(isAutoCorrect, isSuggestedCandidate || isPunctuationSuggestions, suggestionInfo)); - word.setText(getStyledCandidateWord(suggestion, isAutoCorrect)); - // TODO: call TextView.setTextScaleX() to fit the candidate in single line. - word.measure(UNSPECIFIED_MEASURESPEC, UNSPECIFIED_MEASURESPEC); - final int width = word.getMeasuredWidth(); - final int height = word.getMeasuredHeight(); + final CharSequence text = getStyledCandidateWord(suggestion, word, isAutoCorrect); + if (i < NUM_CANDIDATES_IN_STRIP) { + final View parent = (View)word.getParent(); + final int width = parent.getWidth() - word.getPaddingLeft() + - word.getPaddingRight(); + setTextWithAutoScaleAndEllipsis(text, width, word); + } else { + setTextWithAutoScaleAndEllipsis(text, paneWidth, word); + } final TextView info; if (DBG && suggestionInfo != null @@ -344,18 +360,23 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo info = mInfos.get(i); info.setText(suggestionInfo.getDebugString()); info.setVisibility(View.VISIBLE); - info.measure(UNSPECIFIED_MEASURESPEC, UNSPECIFIED_MEASURESPEC); } else { info = null; } if (i < NUM_CANDIDATES_IN_STRIP) { if (info != null) { + word.measure(MATCH_PARENT, MATCH_PARENT); + info.measure(MATCH_PARENT, MATCH_PARENT); + final int width = word.getMeasuredWidth(); final int infoWidth = info.getMeasuredWidth(); FrameLayoutCompatUtils.placeViewAt( info, width - infoWidth, 0, infoWidth, info.getMeasuredHeight()); } } else { + word.measure(MATCH_PARENT, MATCH_PARENT); + final int width = word.getMeasuredWidth(); + final int height = word.getMeasuredHeight(); // TODO: Handle overflow case. if (dividerWidth + x + width >= paneWidth) { centeringCandidates(fromIndex, i - 1, x, paneWidth); @@ -376,6 +397,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo word, x, y + (mCandidateStripHeight - height) / 2, width, height); if (info != null) { mCandidatesPane.addView(info); + info.measure(MATCH_PARENT, MATCH_PARENT); final int infoWidth = info.getMeasuredWidth(); FrameLayoutCompatUtils.placeViewAt( info, x + width - infoWidth, y, infoWidth, info.getMeasuredHeight()); @@ -414,6 +436,60 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo } } + private static void setTextWithAutoScaleAndEllipsis(CharSequence text, int w, TextView v) { + // To prevent partially rendered character at the end of text, subtract few extra pixels + // from the width. + final int width = w - 4; + + final TextPaint paint = v.getPaint(); + final int textWidth = getTextWidth(text, paint, 1.0f); + if (textWidth < width || textWidth == 0 || width <= 0) { + v.setTextScaleX(1.0f); + v.setText(text); + return; + } + + final float scaleX = Math.min((float)width / textWidth, 1.0f); + if (scaleX >= MIN_TEXT_XSCALE) { + v.setTextScaleX(scaleX); + v.setText(text); + return; + } + + final int truncatedWidth = width - getTextWidth(ELLIPSIS, paint, MIN_TEXT_XSCALE); + final CharSequence ellipsized = getTextEllipsizedAtStart(text, paint, truncatedWidth); + v.setTextScaleX(MIN_TEXT_XSCALE); + v.setText(ELLIPSIS); + v.append(ellipsized); + } + + private static int getTextWidth(CharSequence text, TextPaint paint, float scaleX) { + if (TextUtils.isEmpty(text)) return 0; + final int len = text.length(); + final float[] widths = new float[len]; + paint.setTextScaleX(scaleX); + final int count = paint.getTextWidths(text, 0, len, widths); + float width = 0; + for (int i = 0; i < count; i++) { + width += widths[i]; + } + return (int)Math.round(width + 0.5); + } + + private static CharSequence getTextEllipsizedAtStart(CharSequence text, TextPaint paint, + int maxWidth) { + final int len = text.length(); + final float[] widths = new float[len]; + final int count = paint.getTextWidths(text, 0, len, widths); + float width = 0; + for (int i = count - 1; i >= 0; i--) { + width += widths[i]; + if (width > maxWidth) + return text.subSequence(i + 1, len); + } + return text; + } + private void expandCandidatesPane() { mExpandCandidatesPane.setVisibility(View.GONE); mCloseCandidatesPane.setVisibility(View.VISIBLE); @@ -448,14 +524,10 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo } public void showAddToDictionaryHint(CharSequence word) { - SuggestedWords.Builder builder = new SuggestedWords.Builder() - .addWord(word) - .addWord(getContext().getText(R.string.hint_add_to_dictionary)); - setSuggestions(builder.build()); + mWordToSave.setText(word); mShowingAddToDictionary = true; - // Disable R.string.hint_add_to_dictionary button - TextView tv = mWords.get(1); - tv.setClickable(false); + mCandidatesStrip.setVisibility(View.GONE); + mTouchToSave.setVisibility(View.VISIBLE); } public boolean dismissAddToDictionaryHint() { @@ -475,6 +547,8 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo mWords.get(i).setText(null); mInfos.get(i).setVisibility(View.GONE); } + mTouchToSave.setVisibility(View.GONE); + mCandidatesStrip.setVisibility(View.VISIBLE); mCandidatesPane.removeAllViews(); } @@ -530,6 +604,12 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo @Override public void onClick(View view) { + if (view == mWordToSave) { + addToDictionary(((TextView)view).getText()); + clear(); + return; + } + final Object tag = view.getTag(); if (!(tag instanceof Integer)) return; @@ -538,11 +618,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo return; final CharSequence word = mSuggestions.getWord(index); - if (mShowingAddToDictionary && index == 0) { - addToDictionary(word); - } else { - mListener.pickSuggestionManually(index, word); - } + mListener.pickSuggestionManually(index, word); // Because some punctuation letters are not treated as word separator depending on locale, // {@link #setSuggestions} might not be called and candidates pane left opened. closeCandidatesPane(); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 52935ef7d..874d77f19 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -514,7 +514,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar super.setInputView(view); mCandidateViewContainer = view.findViewById(R.id.candidates_container); mCandidateView = (CandidateView) view.findViewById(R.id.candidates); - mCandidateView.setListener(this, view); + if (mCandidateView != null) + mCandidateView.setListener(this, view); mCandidateStripHeight = (int)mResources.getDimension(R.dimen.candidate_strip_height); } @@ -590,7 +591,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar inputView.setProximityCorrectionEnabled(true); // If we just entered a text field, maybe it has some old text that requires correction mRecorrection.checkRecorrectionOnStart(); - inputView.setForeground(true); voiceIme.onStartInputView(inputView.getWindowToken()); @@ -678,7 +678,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public void onFinishInputView(boolean finishingInput) { super.onFinishInputView(finishingInput); KeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); - if (inputView != null) inputView.setForeground(false); + if (inputView != null) inputView.cancelAllMessage(); // Remove pending messages related to update suggestions mHandler.cancelUpdateSuggestions(); mHandler.cancelUpdateOldSuggestions(); @@ -842,7 +842,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) { // TODO: Modify this if we support candidates with hard keyboard - if (onEvaluateInputViewShown()) { + if (onEvaluateInputViewShown() && mCandidateViewContainer != null) { final boolean shouldShowCandidates = shown && (needsInputViewShown ? mKeyboardSwitcher.isInputViewShown() : true); if (isExtractViewShown()) { @@ -1869,6 +1869,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Notify that language or mode have been changed and toggleLanguage will update KeyboardID // according to new language or mode. public void onRefreshKeyboard() { + if (!CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) { + // Before Honeycomb, Voice IME is in LatinIME and it changes the current input view, + // so that we need to re-create the keyboard input view here. + setInputView(mKeyboardSwitcher.onCreateInputView()); + } // Reload keyboard because the current language has been changed. mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSubtypeSwitcher.isShortcutImeEnabled() && mVoiceProxy.isVoiceButtonEnabled(), |