1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/* 2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project 35a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * 4923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * use this file except in compliance with the License. You may obtain a copy of 6923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * the License at 75a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * 8923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 95a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka * 10923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * License for the specific language governing permissions and limitations under 14923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * the License. 15923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 16923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 175a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokapackage com.android.inputmethod.keyboard; 185a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka 19923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Context; 20923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.res.Resources; 21887f11ee43ad621aa6ad93d535ab7f48dec73fc7Tadashi G. Takaokaimport android.content.res.Resources.Theme; 223f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasaniimport android.content.res.TypedArray; 2336fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasaniimport android.graphics.Bitmap; 2436fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasaniimport android.graphics.Canvas; 25de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaokaimport android.graphics.Color; 2636fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasaniimport android.graphics.Paint; 2712659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaokaimport android.graphics.Paint.Align; 2836fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasaniimport android.graphics.PorterDuff; 293f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasaniimport android.graphics.Rect; 3036fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasaniimport android.graphics.drawable.BitmapDrawable; 31923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.graphics.drawable.Drawable; 32d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaokaimport android.text.TextUtils; 33923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 3411b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasaimport com.android.inputmethod.compat.InputMethodManagerCompatWrapper; 351be29abab2e112f0253a8a5da3478740bb866d27Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyboardBuilder; 368da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyboardParams; 375a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaokaimport com.android.inputmethod.latin.R; 385a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaokaimport com.android.inputmethod.latin.SubtypeSwitcher; 3911b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasaimport com.android.inputmethod.latin.Utils; 405a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka 416fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaokaimport java.util.Arrays; 426fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaokaimport java.util.HashMap; 4312659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaokaimport java.util.Locale; 4412659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaoka 45571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka// TODO: We should remove this class 465a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaokapublic class LatinKeyboard extends Keyboard { 471bf265d185bc6b214588dcca4888691f4f8038d4satok private static final int SPACE_LED_LENGTH_PERCENT = 80; 481b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani 495a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka private final Resources mRes; 505a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka private final Theme mTheme; 516fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka private final SubtypeSwitcher mSubtypeSwitcher = SubtypeSwitcher.getInstance(); 525cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka 535cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka /* Space key and its icons, drawables and colors. */ 545cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka private final Key mSpaceKey; 555cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka private final Drawable mSpaceIcon; 569116bf18f9c83084f9d451e2e709eff32db27d36Tadashi G. Takaoka private final boolean mAutoCorrectionSpacebarLedEnabled; 57a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka private final Drawable mAutoCorrectionSpacebarLedIcon; 58de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka private final int mSpacebarTextColor; 59de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka private final int mSpacebarTextShadowColor; 60de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka private float mSpacebarTextFadeFactor = 0.0f; 6128d841534db4e11fbd215d78d399ce4ac75aff1dTadashi G. Takaoka private final HashMap<Integer, BitmapDrawable> mSpaceDrawableCache = 6228d841534db4e11fbd215d78d399ce4ac75aff1dTadashi G. Takaoka new HashMap<Integer, BitmapDrawable>(); 6311b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasa private final boolean mIsSpacebarTriggeringPopupByLongPress; 645cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka 655cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka /* Shortcut key and its icons if available */ 665cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka private final Key mShortcutKey; 675cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka private final Drawable mEnabledShortcutIcon; 685cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka private final Drawable mDisabledShortcutIcon; 695cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka 708243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka // Height in space key the language name will be drawn. (proportional to space key height) 715a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka public static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f; 728243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka // If the full language name needs to be smaller than this value to be drawn on space key, 738243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka // its short language name will be used instead. 748243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka private static final float MINIMUM_SCALE_OF_LANGUAGE_NAME = 0.8f; 753f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani 76183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa private static final String SMALL_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "small"; 77183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa private static final String MEDIUM_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "medium"; 78183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa 798da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka private LatinKeyboard(Context context, LatinKeyboardParams params) { 808da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka super(params); 815a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka mRes = context.getResources(); 825a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka mTheme = context.getTheme(); 835cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka 845cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka // The index of space key is available only after Keyboard constructor has finished. 858da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka mSpaceKey = params.mSpaceKey; 865cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka mSpaceIcon = (mSpaceKey != null) ? mSpaceKey.getIcon() : null; 875cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka 888da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka mShortcutKey = params.mShortcutKey; 895cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka mEnabledShortcutIcon = (mShortcutKey != null) ? mShortcutKey.getIcon() : null; 9011b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasa final int longPressSpaceKeyTimeout = 9111b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasa mRes.getInteger(R.integer.config_long_press_space_key_timeout); 9211b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasa mIsSpacebarTriggeringPopupByLongPress = (longPressSpaceKeyTimeout > 0); 935cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka 94a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka final TypedArray a = context.obtainStyledAttributes( 95a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka null, R.styleable.LatinKeyboard, R.attr.latinKeyboardStyle, R.style.LatinKeyboard); 969116bf18f9c83084f9d451e2e709eff32db27d36Tadashi G. Takaoka mAutoCorrectionSpacebarLedEnabled = a.getBoolean( 979116bf18f9c83084f9d451e2e709eff32db27d36Tadashi G. Takaoka R.styleable.LatinKeyboard_autoCorrectionSpacebarLedEnabled, false); 98a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka mAutoCorrectionSpacebarLedIcon = a.getDrawable( 99a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka R.styleable.LatinKeyboard_autoCorrectionSpacebarLedIcon); 100a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka mDisabledShortcutIcon = a.getDrawable(R.styleable.LatinKeyboard_disabledShortcutIcon); 101a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboard_spacebarTextColor, 0); 102a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka mSpacebarTextShadowColor = a.getColor( 103a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka R.styleable.LatinKeyboard_spacebarTextShadowColor, 0); 104a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka a.recycle(); 1058bfcac4d84ffcfd36f68bf42c179b2b46dd7462cTadashi G. Takaoka } 1068bfcac4d84ffcfd36f68bf42c179b2b46dd7462cTadashi G. Takaoka 1078da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka private static class LatinKeyboardParams extends KeyboardParams { 1088da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka public Key mSpaceKey = null; 1098da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka public Key mShortcutKey = null; 1108da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka 1118da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka @Override 1128da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka public void onAddKey(Key key) { 1138da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka super.onAddKey(key); 1148da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka 1158da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka switch (key.mCode) { 1168da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka case Keyboard.CODE_SPACE: 1178da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka mSpaceKey = key; 1188da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka break; 1198da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka case Keyboard.CODE_SHORTCUT: 1208da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka mShortcutKey = key; 1218da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka break; 1228da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka } 1238da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka } 1248da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka } 1258da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka 1261be29abab2e112f0253a8a5da3478740bb866d27Tadashi G. Takaoka public static class Builder extends KeyboardBuilder<LatinKeyboardParams> { 1278da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka public Builder(Context context) { 1288da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka super(context, new LatinKeyboardParams()); 1298da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka } 1308da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka 1318da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka @Override 1328da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka public Builder load(KeyboardId id) { 1338da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka super.load(id); 1348da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka return this; 1358da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka } 1368da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka 1378da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka @Override 1388da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka public LatinKeyboard build() { 1398da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka return new LatinKeyboard(mContext, mParams); 1408da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka } 1418da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka } 1428da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka 143af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka public void setSpacebarTextFadeFactor(float fadeFactor, KeyboardView view) { 144de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka mSpacebarTextFadeFactor = fadeFactor; 145de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka updateSpacebarForLocale(false); 146de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka if (view != null) 147de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka view.invalidateKey(mSpaceKey); 148de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka } 149de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka 150de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka private static int getSpacebarTextColor(int color, float fadeFactor) { 151de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka final int newColor = Color.argb((int)(Color.alpha(color) * fadeFactor), 152de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka Color.red(color), Color.green(color), Color.blue(color)); 153de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka return newColor; 154de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka } 155de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka 156af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka public void updateShortcutKey(boolean available, KeyboardView view) { 1575cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka if (mShortcutKey == null) 1585cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka return; 159e7759091ddb5ec18268945d70d9212195bf6497bTadashi G. Takaoka mShortcutKey.setEnabled(available); 1605cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka mShortcutKey.setIcon(available ? mEnabledShortcutIcon : mDisabledShortcutIcon); 1615cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka if (view != null) 1625cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka view.invalidateKey(mShortcutKey); 1635cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka } 1645cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka 165a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka public boolean needsAutoCorrectionSpacebarLed() { 1669116bf18f9c83084f9d451e2e709eff32db27d36Tadashi G. Takaoka return mAutoCorrectionSpacebarLedEnabled; 167a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka } 168a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka 16941feaaadb758a8b31d3e436063b4b5faed104d4dsatok /** 17041feaaadb758a8b31d3e436063b4b5faed104d4dsatok * @return a key which should be invalidated. 17141feaaadb758a8b31d3e436063b4b5faed104d4dsatok */ 1721b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka public Key onAutoCorrectionStateChanged(boolean isAutoCorrection) { 173de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka updateSpacebarForLocale(isAutoCorrection); 17441feaaadb758a8b31d3e436063b4b5faed104d4dsatok return mSpaceKey; 17541feaaadb758a8b31d3e436063b4b5faed104d4dsatok } 17641feaaadb758a8b31d3e436063b4b5faed104d4dsatok 177d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka @Override 178d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka public CharSequence adjustLabelCase(CharSequence label) { 179d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka if (isAlphaKeyboard() && isShiftedOrShiftLocked() && !TextUtils.isEmpty(label) 180d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka && label.length() < 3 && Character.isLowerCase(label.charAt(0))) { 181d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka return label.toString().toUpperCase(mId.mLocale); 182d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka } 183d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka return label; 184d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka } 185d773bf38a3c8f49ea56de67d3b828f8126f46ed2Tadashi G. Takaoka 186de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka private void updateSpacebarForLocale(boolean isAutoCorrection) { 18711b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasa if (mSpaceKey == null) return; 18811b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasa final InputMethodManagerCompatWrapper imm = InputMethodManagerCompatWrapper.getInstance(); 18911b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasa if (imm == null) return; 19011b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasa // The "..." popup hint for triggering something by a long-pressing the spacebar 19111b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasa final boolean shouldShowInputMethodPicker = mIsSpacebarTriggeringPopupByLongPress 19211b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasa && Utils.hasMultipleEnabledIMEsOrSubtypes(imm, true /* include aux subtypes */); 19311b7febc0bea46a6afb30d7fa040b841eadd7410Ken Wakasa mSpaceKey.setNeedsSpecialPopupHint(shouldShowInputMethodPicker); 1941bf265d185bc6b214588dcca4888691f4f8038d4satok // If application locales are explicitly selected. 195af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka if (mSubtypeSwitcher.needsToDisplayLanguage(mId.mLocale)) { 196af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka mSpaceKey.setIcon(getSpaceDrawable(mId.mLocale, isAutoCorrection)); 1976fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka } else if (isAutoCorrection) { 1986fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka mSpaceKey.setIcon(getSpaceDrawable(null, true)); 19936fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani } else { 2006fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka mSpaceKey.setIcon(mSpaceIcon); 20136fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani } 20236fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani } 20336fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani 2048243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka // Compute width of text with specified text size using paint. 2058243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka private static int getTextWidth(Paint paint, String text, float textSize, Rect bounds) { 2068243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka paint.setTextSize(textSize); 2078243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka paint.getTextBounds(text, 0, text.length(), bounds); 2088243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka return bounds.width(); 2098243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka } 2108243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka 211de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka // Layout local language name and left and right arrow on spacebar. 21287089985b1ca396557d1350e9433c958a39adf11Tadashi G. Takaoka private static String layoutSpacebar(Paint paint, Locale locale, int width, 21387089985b1ca396557d1350e9433c958a39adf11Tadashi G. Takaoka float origTextSize) { 2148243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka final Rect bounds = new Rect(); 2158243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka 2168243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka // Estimate appropriate language name text size to fit in maxTextWidth. 21757f05f44123fd458203a6f776f2ff904889d2616Tadashi G. Takaoka String language = Utils.getFullDisplayName(locale, true); 2188243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka int textWidth = getTextWidth(paint, language, origTextSize, bounds); 2198243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka // Assuming text width and text size are proportional to each other. 22087089985b1ca396557d1350e9433c958a39adf11Tadashi G. Takaoka float textSize = origTextSize * Math.min(width / textWidth, 1.0f); 221cae6b1060e0c8547f9f7f64fbbf3a18a76035a53satok // allow variable text size 222cae6b1060e0c8547f9f7f64fbbf3a18a76035a53satok textWidth = getTextWidth(paint, language, textSize, bounds); 223cae6b1060e0c8547f9f7f64fbbf3a18a76035a53satok // If text size goes too small or text does not fit, use middle or short name 224cae6b1060e0c8547f9f7f64fbbf3a18a76035a53satok final boolean useMiddleName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME) 22587089985b1ca396557d1350e9433c958a39adf11Tadashi G. Takaoka || (textWidth > width); 2268243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka 2278243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka final boolean useShortName; 228cae6b1060e0c8547f9f7f64fbbf3a18a76035a53satok if (useMiddleName) { 22957f05f44123fd458203a6f776f2ff904889d2616Tadashi G. Takaoka language = Utils.getMiddleDisplayLanguage(locale); 230cae6b1060e0c8547f9f7f64fbbf3a18a76035a53satok textWidth = getTextWidth(paint, language, origTextSize, bounds); 23187089985b1ca396557d1350e9433c958a39adf11Tadashi G. Takaoka textSize = origTextSize * Math.min(width / textWidth, 1.0f); 232cae6b1060e0c8547f9f7f64fbbf3a18a76035a53satok useShortName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME) 23387089985b1ca396557d1350e9433c958a39adf11Tadashi G. Takaoka || (textWidth > width); 2348243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka } else { 235cae6b1060e0c8547f9f7f64fbbf3a18a76035a53satok useShortName = false; 2368243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka } 237cae6b1060e0c8547f9f7f64fbbf3a18a76035a53satok 2388243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka if (useShortName) { 23957f05f44123fd458203a6f776f2ff904889d2616Tadashi G. Takaoka language = Utils.getShortDisplayLanguage(locale); 2408243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka textWidth = getTextWidth(paint, language, origTextSize, bounds); 24187089985b1ca396557d1350e9433c958a39adf11Tadashi G. Takaoka textSize = origTextSize * Math.min(width / textWidth, 1.0f); 2428243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka } 2438243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka paint.setTextSize(textSize); 2448243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka 2458243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka return language; 2468243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka } 2478243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka 2486fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka private BitmapDrawable getSpaceDrawable(Locale locale, boolean isAutoCorrection) { 2496fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka final Integer hashCode = Arrays.hashCode( 2506fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka new Object[] { locale, isAutoCorrection, mSpacebarTextFadeFactor }); 25128d841534db4e11fbd215d78d399ce4ac75aff1dTadashi G. Takaoka final BitmapDrawable cached = mSpaceDrawableCache.get(hashCode); 25228d841534db4e11fbd215d78d399ce4ac75aff1dTadashi G. Takaoka if (cached != null) { 25328d841534db4e11fbd215d78d399ce4ac75aff1dTadashi G. Takaoka return cached; 2546fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka } 25528d841534db4e11fbd215d78d399ce4ac75aff1dTadashi G. Takaoka final BitmapDrawable drawable = new BitmapDrawable(mRes, drawSpacebar( 25628d841534db4e11fbd215d78d399ce4ac75aff1dTadashi G. Takaoka locale, isAutoCorrection, mSpacebarTextFadeFactor)); 25728d841534db4e11fbd215d78d399ce4ac75aff1dTadashi G. Takaoka mSpaceDrawableCache.put(hashCode, drawable); 2586fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka return drawable; 2596fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka } 2606fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka 2616fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka private Bitmap drawSpacebar(Locale inputLocale, boolean isAutoCorrection, 2626fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka float textFadeFactor) { 263571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka final int width = mSpaceKey.mWidth; 264715189fe6eaa1795e38b461ed4b5860097598275Ken Wakasa final int height = mSpaceIcon != null ? mSpaceIcon.getIntrinsicHeight() : mSpaceKey.mHeight; 2658243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka final Bitmap buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 2668243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka final Canvas canvas = new Canvas(buffer); 2675a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka final Resources res = mRes; 2686fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); 2698243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka 2701bf265d185bc6b214588dcca4888691f4f8038d4satok // If application locales are explicitly selected. 2716fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka if (inputLocale != null) { 2728243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka final Paint paint = new Paint(); 2738243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka paint.setAntiAlias(true); 2741bf265d185bc6b214588dcca4888691f4f8038d4satok paint.setTextAlign(Align.CENTER); 275979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 276183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa final String textSizeOfLanguageOnSpacebar = res.getString( 277183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa R.string.config_text_size_of_language_on_spacebar, 278183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa SMALL_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR); 279183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa final int textStyle; 280183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa final int defaultTextSize; 281183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa if (MEDIUM_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR.equals(textSizeOfLanguageOnSpacebar)) { 282183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa textStyle = android.R.style.TextAppearance_Medium; 283183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa defaultTextSize = 18; 284183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa } else { 285183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa textStyle = android.R.style.TextAppearance_Small; 286183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa defaultTextSize = 14; 287183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa } 288183fbbb0dbf786e10dc3bf17b9377ed6598d4605Ken Wakasa 28987089985b1ca396557d1350e9433c958a39adf11Tadashi G. Takaoka final String language = layoutSpacebar(paint, inputLocale, width, getTextSizeFromTheme( 29087089985b1ca396557d1350e9433c958a39adf11Tadashi G. Takaoka mTheme, textStyle, defaultTextSize)); 2918243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka 2928243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka // Draw language text with shadow 29366432cfc9b7680a653bcf19d0d4250db21155eceTadashi G. Takaoka // In case there is no space icon, we will place the language text at the center of 29466432cfc9b7680a653bcf19d0d4250db21155eceTadashi G. Takaoka // spacebar. 2958243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka final float descent = paint.descent(); 29666432cfc9b7680a653bcf19d0d4250db21155eceTadashi G. Takaoka final float textHeight = -paint.ascent() + descent; 29766432cfc9b7680a653bcf19d0d4250db21155eceTadashi G. Takaoka final float baseline = (mSpaceIcon != null) ? height * SPACEBAR_LANGUAGE_BASELINE 29866432cfc9b7680a653bcf19d0d4250db21155eceTadashi G. Takaoka : height / 2 + textHeight / 2; 2996fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, textFadeFactor)); 3008243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka canvas.drawText(language, width / 2, baseline - descent - 1, paint); 3016fb97bf71cee2a0775410a05478ed6a667aa847fTadashi G. Takaoka paint.setColor(getSpacebarTextColor(mSpacebarTextColor, textFadeFactor)); 3028243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka canvas.drawText(language, width / 2, baseline - descent, paint); 303676fb5b665127f43b7062dfa5ebeb36b1e9f9280Amith Yamasani } 3048243c7a5e52d4b03bb456b591be6af43363f0831Tadashi G. Takaoka 305676fb5b665127f43b7062dfa5ebeb36b1e9f9280Amith Yamasani // Draw the spacebar icon at the bottom 3061b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka if (isAutoCorrection) { 3071bf265d185bc6b214588dcca4888691f4f8038d4satok final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100; 308a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight(); 30941feaaadb758a8b31d3e436063b4b5faed104d4dsatok int x = (width - iconWidth) / 2; 31041feaaadb758a8b31d3e436063b4b5faed104d4dsatok int y = height - iconHeight; 311a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka mAutoCorrectionSpacebarLedIcon.setBounds(x, y, x + iconWidth, y + iconHeight); 312a61967330e6086a99373e21ad03323af81aa17edTadashi G. Takaoka mAutoCorrectionSpacebarLedIcon.draw(canvas); 313715189fe6eaa1795e38b461ed4b5860097598275Ken Wakasa } else if (mSpaceIcon != null) { 31441feaaadb758a8b31d3e436063b4b5faed104d4dsatok final int iconWidth = mSpaceIcon.getIntrinsicWidth(); 31541feaaadb758a8b31d3e436063b4b5faed104d4dsatok final int iconHeight = mSpaceIcon.getIntrinsicHeight(); 31641feaaadb758a8b31d3e436063b4b5faed104d4dsatok int x = (width - iconWidth) / 2; 31741feaaadb758a8b31d3e436063b4b5faed104d4dsatok int y = height - iconHeight; 31841feaaadb758a8b31d3e436063b4b5faed104d4dsatok mSpaceIcon.setBounds(x, y, x + iconWidth, y + iconHeight); 31941feaaadb758a8b31d3e436063b4b5faed104d4dsatok mSpaceIcon.draw(canvas); 32041feaaadb758a8b31d3e436063b4b5faed104d4dsatok } 32141feaaadb758a8b31d3e436063b4b5faed104d4dsatok return buffer; 322676fb5b665127f43b7062dfa5ebeb36b1e9f9280Amith Yamasani } 323676fb5b665127f43b7062dfa5ebeb36b1e9f9280Amith Yamasani 3243f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani @Override 3253f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani public int[] getNearestKeys(int x, int y) { 326c5c57b506e97b334a394d23ed73c9597cb55707aTadashi G. Takaoka // Avoid dead pixels at edges of the keyboard 3278da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka return super.getNearestKeys(Math.max(0, Math.min(x, mOccupiedWidth - 1)), 3288da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka Math.max(0, Math.min(y, mOccupiedHeight - 1))); 3293f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani } 3303f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani 331cc02aa8af35b85cb564503c7b54e1eae6a6b6169Tadashi G. Takaoka private static final int[] ATTR_TEXT_SIZE = { android.R.attr.textSize }; 332cc02aa8af35b85cb564503c7b54e1eae6a6b6169Tadashi G. Takaoka 3335a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka public static int getTextSizeFromTheme(Theme theme, int style, int defValue) { 334cc02aa8af35b85cb564503c7b54e1eae6a6b6169Tadashi G. Takaoka final TypedArray a = theme.obtainStyledAttributes(style, ATTR_TEXT_SIZE); 335cc02aa8af35b85cb564503c7b54e1eae6a6b6169Tadashi G. Takaoka final int textSize = a.getDimensionPixelSize(a.getResourceId(0, 0), defValue); 336cc02aa8af35b85cb564503c7b54e1eae6a6b6169Tadashi G. Takaoka a.recycle(); 3373f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani return textSize; 3383f7d75060afb2bb7e74879bcbbdcb9700ec5c2d6Amith Yamasani } 339923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project} 340