LatinKeyboard.java revision ef5dfc480c7a3e3e34a20b7aacc731942e7a0578
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17package com.android.inputmethod.keyboard; 18 19import android.content.Context; 20import android.content.res.Resources; 21import android.content.res.Resources.Theme; 22import android.content.res.TypedArray; 23import android.graphics.Bitmap; 24import android.graphics.Canvas; 25import android.graphics.Color; 26import android.graphics.Paint; 27import android.graphics.Paint.Align; 28import android.graphics.PorterDuff; 29import android.graphics.Rect; 30import android.graphics.drawable.BitmapDrawable; 31import android.graphics.drawable.Drawable; 32import android.text.TextUtils; 33 34import com.android.inputmethod.latin.R; 35import com.android.inputmethod.latin.SubtypeSwitcher; 36 37import java.lang.ref.SoftReference; 38import java.util.Arrays; 39import java.util.HashMap; 40import java.util.List; 41import java.util.Locale; 42 43// TODO: We should remove this class 44public class LatinKeyboard extends Keyboard { 45 private static final int SPACE_LED_LENGTH_PERCENT = 80; 46 47 private final Resources mRes; 48 private final Theme mTheme; 49 private final SubtypeSwitcher mSubtypeSwitcher = SubtypeSwitcher.getInstance(); 50 51 /* Space key and its icons, drawables and colors. */ 52 private final Key mSpaceKey; 53 private final Drawable mSpaceIcon; 54 private final boolean mAutoCorrectionSpacebarLedEnabled; 55 private final Drawable mAutoCorrectionSpacebarLedIcon; 56 private final int mSpacebarTextColor; 57 private final int mSpacebarTextShadowColor; 58 private float mSpacebarTextFadeFactor = 0.0f; 59 private final HashMap<Integer, SoftReference<BitmapDrawable>> mSpaceDrawableCache = 60 new HashMap<Integer, SoftReference<BitmapDrawable>>(); 61 62 /* Shortcut key and its icons if available */ 63 private final Key mShortcutKey; 64 private final Drawable mEnabledShortcutIcon; 65 private final Drawable mDisabledShortcutIcon; 66 67 // Height in space key the language name will be drawn. (proportional to space key height) 68 public static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f; 69 // If the full language name needs to be smaller than this value to be drawn on space key, 70 // its short language name will be used instead. 71 private static final float MINIMUM_SCALE_OF_LANGUAGE_NAME = 0.8f; 72 73 private static final String SMALL_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "small"; 74 private static final String MEDIUM_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "medium"; 75 76 public LatinKeyboard(Context context, KeyboardId id, int width) { 77 super(context, id.getXmlId(), id, width); 78 mRes = context.getResources(); 79 mTheme = context.getTheme(); 80 81 final List<Key> keys = getKeys(); 82 int spaceKeyIndex = -1; 83 int shortcutKeyIndex = -1; 84 final int keyCount = keys.size(); 85 for (int index = 0; index < keyCount; index++) { 86 // For now, assuming there are up to one space key and one shortcut key respectively. 87 switch (keys.get(index).mCode) { 88 case CODE_SPACE: 89 spaceKeyIndex = index; 90 break; 91 case CODE_SHORTCUT: 92 shortcutKeyIndex = index; 93 break; 94 } 95 } 96 97 // The index of space key is available only after Keyboard constructor has finished. 98 mSpaceKey = (spaceKeyIndex >= 0) ? keys.get(spaceKeyIndex) : null; 99 mSpaceIcon = (mSpaceKey != null) ? mSpaceKey.getIcon() : null; 100 101 mShortcutKey = (shortcutKeyIndex >= 0) ? keys.get(shortcutKeyIndex) : null; 102 mEnabledShortcutIcon = (mShortcutKey != null) ? mShortcutKey.getIcon() : null; 103 104 final TypedArray a = context.obtainStyledAttributes( 105 null, R.styleable.LatinKeyboard, R.attr.latinKeyboardStyle, R.style.LatinKeyboard); 106 mAutoCorrectionSpacebarLedEnabled = a.getBoolean( 107 R.styleable.LatinKeyboard_autoCorrectionSpacebarLedEnabled, false); 108 mAutoCorrectionSpacebarLedIcon = a.getDrawable( 109 R.styleable.LatinKeyboard_autoCorrectionSpacebarLedIcon); 110 mDisabledShortcutIcon = a.getDrawable(R.styleable.LatinKeyboard_disabledShortcutIcon); 111 mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboard_spacebarTextColor, 0); 112 mSpacebarTextShadowColor = a.getColor( 113 R.styleable.LatinKeyboard_spacebarTextShadowColor, 0); 114 a.recycle(); 115 } 116 117 public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboardView view) { 118 mSpacebarTextFadeFactor = fadeFactor; 119 updateSpacebarForLocale(false); 120 if (view != null) 121 view.invalidateKey(mSpaceKey); 122 } 123 124 private static int getSpacebarTextColor(int color, float fadeFactor) { 125 final int newColor = Color.argb((int)(Color.alpha(color) * fadeFactor), 126 Color.red(color), Color.green(color), Color.blue(color)); 127 return newColor; 128 } 129 130 public void updateShortcutKey(boolean available, LatinKeyboardView view) { 131 if (mShortcutKey == null) 132 return; 133 mShortcutKey.setEnabled(available); 134 mShortcutKey.setIcon(available ? mEnabledShortcutIcon : mDisabledShortcutIcon); 135 if (view != null) 136 view.invalidateKey(mShortcutKey); 137 } 138 139 public boolean needsAutoCorrectionSpacebarLed() { 140 return mAutoCorrectionSpacebarLedEnabled; 141 } 142 143 /** 144 * @return a key which should be invalidated. 145 */ 146 public Key onAutoCorrectionStateChanged(boolean isAutoCorrection) { 147 updateSpacebarForLocale(isAutoCorrection); 148 return mSpaceKey; 149 } 150 151 @Override 152 public CharSequence adjustLabelCase(CharSequence label) { 153 if (isAlphaKeyboard() && isShiftedOrShiftLocked() && !TextUtils.isEmpty(label) 154 && label.length() < 3 && Character.isLowerCase(label.charAt(0))) { 155 return label.toString().toUpperCase(mId.mLocale); 156 } 157 return label; 158 } 159 160 private void updateSpacebarForLocale(boolean isAutoCorrection) { 161 if (mSpaceKey == null) 162 return; 163 // If application locales are explicitly selected. 164 if (mSubtypeSwitcher.needsToDisplayLanguage()) { 165 mSpaceKey.setIcon(getSpaceDrawable( 166 mSubtypeSwitcher.getInputLocale(), isAutoCorrection)); 167 } else if (isAutoCorrection) { 168 mSpaceKey.setIcon(getSpaceDrawable(null, true)); 169 } else { 170 mSpaceKey.setIcon(mSpaceIcon); 171 } 172 } 173 174 // Compute width of text with specified text size using paint. 175 private static int getTextWidth(Paint paint, String text, float textSize, Rect bounds) { 176 paint.setTextSize(textSize); 177 paint.getTextBounds(text, 0, text.length(), bounds); 178 return bounds.width(); 179 } 180 181 // Layout local language name and left and right arrow on spacebar. 182 private static String layoutSpacebar(Paint paint, Locale locale, int width, 183 float origTextSize) { 184 final Rect bounds = new Rect(); 185 186 // Estimate appropriate language name text size to fit in maxTextWidth. 187 String language = SubtypeSwitcher.getFullDisplayName(locale, true); 188 int textWidth = getTextWidth(paint, language, origTextSize, bounds); 189 // Assuming text width and text size are proportional to each other. 190 float textSize = origTextSize * Math.min(width / textWidth, 1.0f); 191 // allow variable text size 192 textWidth = getTextWidth(paint, language, textSize, bounds); 193 // If text size goes too small or text does not fit, use middle or short name 194 final boolean useMiddleName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME) 195 || (textWidth > width); 196 197 final boolean useShortName; 198 if (useMiddleName) { 199 language = SubtypeSwitcher.getMiddleDisplayLanguage(locale); 200 textWidth = getTextWidth(paint, language, origTextSize, bounds); 201 textSize = origTextSize * Math.min(width / textWidth, 1.0f); 202 useShortName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME) 203 || (textWidth > width); 204 } else { 205 useShortName = false; 206 } 207 208 if (useShortName) { 209 language = SubtypeSwitcher.getShortDisplayLanguage(locale); 210 textWidth = getTextWidth(paint, language, origTextSize, bounds); 211 textSize = origTextSize * Math.min(width / textWidth, 1.0f); 212 } 213 paint.setTextSize(textSize); 214 215 return language; 216 } 217 218 private BitmapDrawable getSpaceDrawable(Locale locale, boolean isAutoCorrection) { 219 final Integer hashCode = Arrays.hashCode( 220 new Object[] { locale, isAutoCorrection, mSpacebarTextFadeFactor }); 221 final SoftReference<BitmapDrawable> ref = mSpaceDrawableCache.get(hashCode); 222 BitmapDrawable drawable = (ref == null) ? null : ref.get(); 223 if (drawable == null) { 224 drawable = new BitmapDrawable(mRes, drawSpacebar( 225 locale, isAutoCorrection, mSpacebarTextFadeFactor)); 226 mSpaceDrawableCache.put(hashCode, new SoftReference<BitmapDrawable>(drawable)); 227 } 228 return drawable; 229 } 230 231 private Bitmap drawSpacebar(Locale inputLocale, boolean isAutoCorrection, 232 float textFadeFactor) { 233 final int width = mSpaceKey.mWidth; 234 final int height = mSpaceIcon != null ? mSpaceIcon.getIntrinsicHeight() : mSpaceKey.mHeight; 235 final Bitmap buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 236 final Canvas canvas = new Canvas(buffer); 237 final Resources res = mRes; 238 canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); 239 240 // If application locales are explicitly selected. 241 if (inputLocale != null) { 242 final Paint paint = new Paint(); 243 paint.setAntiAlias(true); 244 paint.setTextAlign(Align.CENTER); 245 246 final String textSizeOfLanguageOnSpacebar = res.getString( 247 R.string.config_text_size_of_language_on_spacebar, 248 SMALL_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR); 249 final int textStyle; 250 final int defaultTextSize; 251 if (MEDIUM_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR.equals(textSizeOfLanguageOnSpacebar)) { 252 textStyle = android.R.style.TextAppearance_Medium; 253 defaultTextSize = 18; 254 } else { 255 textStyle = android.R.style.TextAppearance_Small; 256 defaultTextSize = 14; 257 } 258 259 final String language = layoutSpacebar(paint, inputLocale, width, getTextSizeFromTheme( 260 mTheme, textStyle, defaultTextSize)); 261 262 // Draw language text with shadow 263 // In case there is no space icon, we will place the language text at the center of 264 // spacebar. 265 final float descent = paint.descent(); 266 final float textHeight = -paint.ascent() + descent; 267 final float baseline = (mSpaceIcon != null) ? height * SPACEBAR_LANGUAGE_BASELINE 268 : height / 2 + textHeight / 2; 269 paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, textFadeFactor)); 270 canvas.drawText(language, width / 2, baseline - descent - 1, paint); 271 paint.setColor(getSpacebarTextColor(mSpacebarTextColor, textFadeFactor)); 272 canvas.drawText(language, width / 2, baseline - descent, paint); 273 } 274 275 // Draw the spacebar icon at the bottom 276 if (isAutoCorrection) { 277 final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100; 278 final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight(); 279 int x = (width - iconWidth) / 2; 280 int y = height - iconHeight; 281 mAutoCorrectionSpacebarLedIcon.setBounds(x, y, x + iconWidth, y + iconHeight); 282 mAutoCorrectionSpacebarLedIcon.draw(canvas); 283 } else if (mSpaceIcon != null) { 284 final int iconWidth = mSpaceIcon.getIntrinsicWidth(); 285 final int iconHeight = mSpaceIcon.getIntrinsicHeight(); 286 int x = (width - iconWidth) / 2; 287 int y = height - iconHeight; 288 mSpaceIcon.setBounds(x, y, x + iconWidth, y + iconHeight); 289 mSpaceIcon.draw(canvas); 290 } 291 return buffer; 292 } 293 294 @Override 295 public int[] getNearestKeys(int x, int y) { 296 // Avoid dead pixels at edges of the keyboard 297 return super.getNearestKeys(Math.max(0, Math.min(x, getMinWidth() - 1)), 298 Math.max(0, Math.min(y, getHeight() - 1))); 299 } 300 301 public static int getTextSizeFromTheme(Theme theme, int style, int defValue) { 302 TypedArray array = theme.obtainStyledAttributes( 303 style, new int[] { android.R.attr.textSize }); 304 int textSize = array.getDimensionPixelSize(array.getResourceId(0, 0), defValue); 305 return textSize; 306 } 307} 308