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