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