MainKeyboardView.java revision 31c94cea82f1788e3a04f2a1e012945f35497f0a
15f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/*
25f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Copyright (C) 2011 The Android Open Source Project
35f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
45f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
55f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * you may not use this file except in compliance with the License.
65f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * You may obtain a copy of the License at
75f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
85f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
95f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * See the License for the specific language governing permissions and
145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * limitations under the License.
155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */
165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokapackage com.android.inputmethod.keyboard;
185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
19d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaokaimport android.animation.AnimatorInflater;
2031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaokaimport android.animation.ObjectAnimator;
215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.Context;
225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.pm.PackageManager;
234112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.content.res.TypedArray;
246dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport android.graphics.Canvas;
2522b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaokaimport android.graphics.Paint;
264112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.Paint.Align;
27bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaokaimport android.graphics.Typeface;
284112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.drawable.Drawable;
295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.os.Message;
3015d4793911fa305e0a58aced925961e948582979satokimport android.text.TextUtils;
315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.AttributeSet;
325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.Log;
335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.LayoutInflater;
345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.MotionEvent;
355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.View;
365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.ViewConfiguration;
37b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaokaimport android.view.ViewGroup;
385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.widget.PopupWindow;
395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils;
415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
426dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.deprecated.VoiceProxy;
43f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
442321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
45e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeySpecParser;
466dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinIME;
4715d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.LatinImeLogger;
485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.StaticInnerHandlerWrapper;
50cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaokaimport com.android.inputmethod.latin.StringUtils;
51cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaokaimport com.android.inputmethod.latin.SubtypeUtils;
526dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.latin.Utils;
5315d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils;
545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
554112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport java.util.Locale;
565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport java.util.WeakHashMap;
575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/**
595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * A view that is responsible for detecting key presses and touch movements.
605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_keyHysteresisDistance
625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_verticalCorrection
635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_popupLayout
645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */
65c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaokapublic class LatinKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
66c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        SuddenJumpingTouchEventHandler.ProcessMotionEvent {
67c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka    private static final String TAG = LatinKeyboardView.class.getSimpleName();
685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
69bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // TODO: Kill process when the usability study mode was changed.
70bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
71bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka
72bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /** Listener for {@link KeyboardActionListener}. */
73bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private KeyboardActionListener mKeyboardActionListener;
74bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka
75bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /* Space key and its icons */
764112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Key mSpaceKey;
774112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Drawable mSpaceIcon;
78bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw language name on spacebar.
7931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private final int mLanguageOnSpacebarFinalAlpha;
8031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
81aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka    private static final int ALPHA_OPAQUE = 255;
82bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mNeedsToDisplayLanguage;
83bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private Locale mSpacebarLocale;
8431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private int mLanguageOnSpacebarAnimAlpha = ALPHA_OPAQUE;
854112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final float mSpacebarTextRatio;
864112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private float mSpacebarTextSize;
874112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final int mSpacebarTextColor;
884112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final int mSpacebarTextShadowColor;
894112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    // If the full language name needs to be smaller than this value to be drawn on space key,
904112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    // its short language name will be used instead.
914112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private static final float MINIMUM_SCALE_OF_LANGUAGE_NAME = 0.8f;
92bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw auto correction LED on spacebar.
93bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mAutoCorrectionSpacebarLedOn;
94bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final boolean mAutoCorrectionSpacebarLedEnabled;
95bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final Drawable mAutoCorrectionSpacebarLedIcon;
96bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private static final int SPACE_LED_LENGTH_PERCENT = 80;
9715d4793911fa305e0a58aced925961e948582979satok
9873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    // Stuff to draw altCodeWhileTyping keys.
9931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
10031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
101b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka    private int mAltCodeKeyWhileTypingAnimAlpha = ALPHA_OPAQUE;
10273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
1032affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    // More keys keyboard
1049d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private PopupWindow mMoreKeysWindow;
1059d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private MoreKeysPanel mMoreKeysPanel;
1069d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private int mMoreKeysPanelPointerTrackerId;
1079d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private final WeakHashMap<Key, MoreKeysPanel> mMoreKeysPanelCache =
1089d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            new WeakHashMap<Key, MoreKeysPanel>();
1092affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
1105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
111160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    private final PointerTrackerParams mPointerTrackerParams;
112bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final boolean mIsSpacebarTriggeringPopupByLongPress;
113bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final SuddenJumpingTouchEventHandler mTouchScreenRegulator;
1145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
115bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    protected KeyDetector mKeyDetector;
11606b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka    private boolean mHasDistinctMultitouch;
1175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private int mOldPointerCount = 1;
118e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mOldKey;
1195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
120160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    private final KeyTimerHandler mKeyTimerHandler;
1215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
122c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka    private static class KeyTimerHandler extends StaticInnerHandlerWrapper<LatinKeyboardView>
1232321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka            implements TimerProxy {
124f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_REPEAT_KEY = 1;
125f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_LONGPRESS_KEY = 2;
1260ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        private static final int MSG_DOUBLE_TAP = 3;
12773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        private static final int MSG_TYPING_STATE_EXPIRED = 4;
1285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
129a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        private final KeyTimerParams mParams;
1305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        private boolean mInKeyRepeat;
1315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
132a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public KeyTimerHandler(LatinKeyboardView outerInstance, KeyTimerParams params) {
1335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            super(outerInstance);
134a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mParams = params;
1355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        @Override
1385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void handleMessage(Message msg) {
139c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka            final LatinKeyboardView keyboardView = getOuterInstance();
1405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            final PointerTracker tracker = (PointerTracker) msg.obj;
1415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            switch (msg.what) {
1425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_REPEAT_KEY:
143e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                tracker.onRepeatKey(tracker.getKey());
14432c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka                startKeyRepeatTimer(tracker, mParams.mKeyRepeatInterval);
1455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
1465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_LONGPRESS_KEY:
147a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                if (tracker != null) {
1482affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                    keyboardView.openMoreKeysKeyboardIfRequired(tracker.getKey(), tracker);
149a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                } else {
150a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    KeyboardSwitcher.getInstance().onLongPressTimeout(msg.arg1);
151a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                }
1525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
15373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            case MSG_TYPING_STATE_EXPIRED:
154b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka                cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator,
155b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka                        keyboardView.mAltCodeKeyWhileTypingFadeinAnimator);
15673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                break;
1575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
1585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
16032c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka        private void startKeyRepeatTimer(PointerTracker tracker, long delay) {
16132c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, tracker), delay);
16232c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka        }
16332c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka
1642321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
165a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startKeyRepeatTimer(PointerTracker tracker) {
1665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            mInKeyRepeat = true;
16732c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka            startKeyRepeatTimer(tracker, mParams.mKeyRepeatStartTimeout);
1685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void cancelKeyRepeatTimer() {
1715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            mInKeyRepeat = false;
1725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_REPEAT_KEY);
1735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public boolean isInKeyRepeat() {
1765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return mInKeyRepeat;
1775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1792321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
180a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(int code) {
18198b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka            cancelLongPressTimer();
182a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            final int delay;
183a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            switch (code) {
184a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            case Keyboard.CODE_SHIFT:
18540e9012276b1df9be8c1a9069eaeb16027549a85Tadashi G. Takaoka                delay = mParams.mLongPressShiftKeyTimeout;
186a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                break;
187a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            default:
188a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                delay = 0;
189a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                break;
190a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
191a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            if (delay > 0) {
192a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, code, 0), delay);
193a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
194a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        }
195a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka
196a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        @Override
197a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public void startLongPressTimer(PointerTracker tracker) {
198a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            cancelLongPressTimer();
199a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            if (tracker != null) {
200a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                final Key key = tracker.getKey();
201a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                final int delay;
202a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                switch (key.mCode) {
203a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                case Keyboard.CODE_SHIFT:
204a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    delay = mParams.mLongPressShiftKeyTimeout;
205a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    break;
206a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                case Keyboard.CODE_SPACE:
207a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    delay = mParams.mLongPressSpaceKeyTimeout;
208a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    break;
209a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                default:
210a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    if (KeyboardSwitcher.getInstance().isInMomentarySwitchState()) {
211a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                        // We use longer timeout for sliding finger input started from the symbols
212a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                        // mode key.
213a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                        delay = mParams.mLongPressKeyTimeout * 3;
214a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    } else {
215a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                        delay = mParams.mLongPressKeyTimeout;
216a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    }
217a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    break;
218a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                }
219a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                if (delay > 0) {
220a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, tracker), delay);
221a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                }
222a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
2235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2252321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
22698b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer() {
2275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_LONGPRESS_KEY);
2285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
23031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        public static void cancelAndStartAnimators(ObjectAnimator animatorToCancel,
23131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                ObjectAnimator animatorToStart) {
232b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            if (animatorToCancel != null && animatorToCancel.isStarted()) {
233b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka                animatorToCancel.cancel();
234b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            }
235b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            // TODO: Start the animation with an initial value that is the same as the final value
236b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            // of the above animation when it gets cancelled.
237b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            if (animatorToStart != null && !animatorToStart.isStarted()) {
238b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka                animatorToStart.start();
239b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            }
240b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka        }
241b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka
242b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka        private void cancelTypingStateTimer() {
243b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            removeMessages(MSG_TYPING_STATE_EXPIRED);
244b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka        }
245b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka
2462321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
24773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public void startTypingStateTimer() {
24873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            final boolean isTyping = isTypingState();
249b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            cancelTypingStateTimer();
25073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            sendMessageDelayed(
25173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                    obtainMessage(MSG_TYPING_STATE_EXPIRED), mParams.mIgnoreAltCodeKeyTimeout);
25273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            if (isTyping) {
25373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                return;
25473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            }
255b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            final LatinKeyboardView keyboardView = getOuterInstance();
256b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator,
257b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka                    keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator);
25893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
25993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
26093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        @Override
26173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState() {
26273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            return hasMessages(MSG_TYPING_STATE_EXPIRED);
26393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
26493246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
26593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        @Override
2660ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void startDoubleTapTimer() {
2670ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP),
2685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                    ViewConfiguration.getDoubleTapTimeout());
2695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2710ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        @Override
272beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void cancelDoubleTapTimer() {
273beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            removeMessages(MSG_DOUBLE_TAP);
274beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        }
275beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka
276beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        @Override
2770ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public boolean isInDoubleTapTimeout() {
2780ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            return hasMessages(MSG_DOUBLE_TAP);
2795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
280c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
281c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        @Override
2820ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void cancelKeyTimers() {
2830ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelKeyRepeatTimer();
2840ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelLongPressTimer();
285c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
286c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
2870ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void cancelAllMessages() {
2880ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelKeyTimers();
289b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            cancelTypingStateTimer();
290c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
291c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka    }
292c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
293160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    public static class PointerTrackerParams {
294160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        public final boolean mSlidingKeyInputEnabled;
295160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        public final int mTouchNoiseThresholdTime;
296160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        public final float mTouchNoiseThresholdDistance;
297160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
298160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
299160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
300160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        private PointerTrackerParams() {
301160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mSlidingKeyInputEnabled = false;
302160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mTouchNoiseThresholdTime =0;
303160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mTouchNoiseThresholdDistance = 0;
304160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        }
305160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
306160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        public PointerTrackerParams(TypedArray latinKeyboardViewAttr) {
307160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mSlidingKeyInputEnabled = latinKeyboardViewAttr.getBoolean(
308160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka                    R.styleable.LatinKeyboardView_slidingKeyInputEnable, false);
309a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTouchNoiseThresholdTime = latinKeyboardViewAttr.getInt(
310a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    R.styleable.LatinKeyboardView_touchNoiseThresholdTime, 0);
311a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mTouchNoiseThresholdDistance = latinKeyboardViewAttr.getDimension(
312a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    R.styleable.LatinKeyboardView_touchNoiseThresholdDistance, 0);
313a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        }
314a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka    }
315a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka
316a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka    static class KeyTimerParams {
317a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public final int mKeyRepeatStartTimeout;
318a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public final int mKeyRepeatInterval;
319a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public final int mLongPressKeyTimeout;
320a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public final int mLongPressShiftKeyTimeout;
321a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public final int mLongPressSpaceKeyTimeout;
32273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public final int mIgnoreAltCodeKeyTimeout;
323a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka
324a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        public KeyTimerParams(TypedArray latinKeyboardViewAttr) {
325160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mKeyRepeatStartTimeout = latinKeyboardViewAttr.getInt(
326160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka                    R.styleable.LatinKeyboardView_keyRepeatStartTimeout, 0);
327a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            mKeyRepeatInterval = latinKeyboardViewAttr.getInt(
328a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    R.styleable.LatinKeyboardView_keyRepeatInterval, 0);
329160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mLongPressKeyTimeout = latinKeyboardViewAttr.getInt(
330160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka                    R.styleable.LatinKeyboardView_longPressKeyTimeout, 0);
331160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mLongPressShiftKeyTimeout = latinKeyboardViewAttr.getInt(
332160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka                    R.styleable.LatinKeyboardView_longPressShiftKeyTimeout, 0);
333160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka            mLongPressSpaceKeyTimeout = latinKeyboardViewAttr.getInt(
334160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka                    R.styleable.LatinKeyboardView_longPressSpaceKeyTimeout, 0);
33573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            mIgnoreAltCodeKeyTimeout = latinKeyboardViewAttr.getInt(
33673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                    R.styleable.LatinKeyboardView_ignoreAltCodeKeyTimeout, 0);
337160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        }
338160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    }
339160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
340c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka    public LatinKeyboardView(Context context, AttributeSet attrs) {
3415afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka        this(context, attrs, R.attr.latinKeyboardViewStyle);
3425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
344c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka    public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) {
3455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super(context, attrs, defStyle);
3465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
347c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this);
348c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
3495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mHasDistinctMultitouch = context.getPackageManager()
3505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
351906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka
352a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        PointerTracker.init(mHasDistinctMultitouch);
35322b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
3544112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final TypedArray a = context.obtainStyledAttributes(
3554112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                attrs, R.styleable.LatinKeyboardView, defStyle, R.style.LatinKeyboardView);
3564112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedEnabled = a.getBoolean(
3574112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedEnabled, false);
3584112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedIcon = a.getDrawable(
3594112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedIcon);
3604112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextRatio = a.getFraction(R.styleable.LatinKeyboardView_spacebarTextRatio,
3614112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                1000, 1000, 1) / 1000.0f;
3624112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboardView_spacebarTextColor, 0);
3634112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextShadowColor = a.getColor(
3644112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                R.styleable.LatinKeyboardView_spacebarTextShadowColor, 0);
36531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFinalAlpha = a.getInt(
36631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                R.styleable.LatinKeyboardView_languageOnSpacebarFinalAlpha, ALPHA_OPAQUE);
367d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        final int languageOnSpacebarFadeoutAnimatorResId = a.getResourceId(
368d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka                R.styleable.LatinKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
36973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeoutAnimatorResId = a.getResourceId(
37073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                R.styleable.LatinKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
37173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeinAnimatorResId = a.getResourceId(
37273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                R.styleable.LatinKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
373160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
374a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        final KeyTimerParams keyTimerParams = new KeyTimerParams(a);
375160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        mPointerTrackerParams = new PointerTrackerParams(a);
376a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        mIsSpacebarTriggeringPopupByLongPress = (keyTimerParams.mLongPressSpaceKeyTimeout > 0);
377160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
378160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        final float keyHysteresisDistance = a.getDimension(
379160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka                R.styleable.LatinKeyboardView_keyHysteresisDistance, 0);
380160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        mKeyDetector = new KeyDetector(keyHysteresisDistance);
381a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        mKeyTimerHandler = new KeyTimerHandler(this, keyTimerParams);
3822affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        mConfigShowMoreKeysKeyboardAtTouchedPoint = a.getBoolean(
3832affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                R.styleable.LatinKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
3844112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        a.recycle();
385160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
386160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        PointerTracker.setParameters(mPointerTrackerParams);
3874c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka
38831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator(
38931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                languageOnSpacebarFadeoutAnimatorResId, this);
39031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator(
39131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeoutAnimatorResId, this);
39231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator(
39331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeinAnimatorResId, this);
39431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
39531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
39631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator loadObjectAnimator(int resId, Object target) {
39731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        if (resId == 0) return null;
39831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
39931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                getContext(), resId);
400d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator != null) {
40131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            animator.setTarget(target);
40273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
40331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return animator;
40431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
40573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
40631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    // Getter/setter methods for {@link ObjectAnimator}.
40731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getLanguageOnSpacebarAnimAlpha() {
40831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mLanguageOnSpacebarAnimAlpha;
409d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka    }
410d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka
41131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public void setLanguageOnSpacebarAnimAlpha(int alpha) {
41231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarAnimAlpha = alpha;
41331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        invalidateKey(mSpaceKey);
41431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
41531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
41631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getAltCodeKeyWhileTypingAnimAlpha() {
41731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mAltCodeKeyWhileTypingAnimAlpha;
41831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
41931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
42031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public void setAltCodeKeyWhileTypingAnimAlpha(int alpha) {
42131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingAnimAlpha = alpha;
42231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        updateAltCodeKeyWhileTyping();
4235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4255a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka    public void setKeyboardActionListener(KeyboardActionListener listener) {
4265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyboardActionListener = listener;
4275c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        PointerTracker.setKeyboardActionListener(listener);
4285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
4315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns the {@link KeyboardActionListener} object.
4325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return the listener attached to this keyboard
4335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
4340efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
4350efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyboardActionListener getKeyboardActionListener() {
4365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mKeyboardActionListener;
4375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
439bb4be5444b845655c0eb80bcfbb66f93603802eaTadashi G. Takaoka    @Override
4400efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyDetector getKeyDetector() {
4410efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyDetector;
4420efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
4430efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
4440efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
445f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public DrawingProxy getDrawingProxy() {
446f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        return this;
447f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
448f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
449f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    @Override
4500efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public TimerProxy getTimerProxy() {
4510efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyTimerHandler;
4520efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
4530efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
4545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
4555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Attaches a keyboard to this view. The keyboard can be switched at any time and the
4565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * view will re-layout itself to accommodate the keyboard.
4575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see Keyboard
4585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see #getKeyboard()
4595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param keyboard the keyboard to display in this view
4605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
4615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
4625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void setKeyboard(Keyboard keyboard) {
4635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Remove any pending messages, except dismissing preview
464f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        mKeyTimerHandler.cancelKeyTimers();
4655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.setKeyboard(keyboard);
4665a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyDetector.setKeyboard(
4675a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka                keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
4685c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        PointerTracker.setKeyDetector(mKeyDetector);
469c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        mTouchScreenRegulator.setKeyboard(keyboard);
4709d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanelCache.clear();
4714112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
4724112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE);
473b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        mSpaceIcon = (mSpaceKey != null) ? mSpaceKey.getIcon(keyboard.mIconsSet) : null;
4744112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
4754112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
4764112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarLocale = keyboard.mId.mLocale;
4775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
4805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns whether the device has distinct multi-touch panel.
4815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return true if the device has distinct multi-touch panel.
4825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
4835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean hasDistinctMultitouch() {
4845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mHasDistinctMultitouch;
4855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
48706b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka    public void setDistinctMultitouch(boolean hasDistinctMultitouch) {
48806b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka        mHasDistinctMultitouch = hasDistinctMultitouch;
48906b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka    }
49006b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka
4915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
4925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * When enabled, calls to {@link KeyboardActionListener#onCodeInput} will include key
4935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * codes for adjacent keys.  When disabled, only the primary key code will be
4945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * reported.
4955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param enabled whether or not the proximity correction is enabled
4965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
4975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void setProximityCorrectionEnabled(boolean enabled) {
4985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyDetector.setProximityCorrectionEnabled(enabled);
4995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
5025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns true if proximity correction is enabled.
5035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
5045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean isProximityCorrectionEnabled() {
5055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mKeyDetector.isProximityCorrectionEnabled();
5065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
5095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void cancelAllMessages() {
510f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        mKeyTimerHandler.cancelAllMessages();
5115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.cancelAllMessages();
5125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5142affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    private boolean openMoreKeysKeyboardIfRequired(Key parentKey, PointerTracker tracker) {
5155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Check if we have a popup layout specified first.
5169d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysLayout == 0) {
5175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return false;
5185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
5195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
52063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        // Check if we are already displaying popup panel.
5219d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysPanel != null)
52263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return false;
5235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (parentKey == null)
5245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return false;
52563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        return onLongPress(parentKey, tracker);
5265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5289d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    // This default implementation returns a more keys panel.
5299d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    protected MoreKeysPanel onCreateMoreKeysPanel(Key parentKey) {
5309d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (parentKey.mMoreKeys == null)
5315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return null;
5325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5339d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        final View container = LayoutInflater.from(getContext()).inflate(mMoreKeysLayout, null);
5345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (container == null)
5355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            throw new NullPointerException();
5365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5372affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        final MoreKeysKeyboardView moreKeysKeyboardView =
5382affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view);
5395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final Keyboard parentKeyboard = getKeyboard();
5402affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        final Keyboard moreKeysKeyboard = new MoreKeysKeyboard.Builder(
5419d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                this, parentKeyboard.mMoreKeysTemplate, parentKey, parentKeyboard).build();
5422affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
543b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka        container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
5445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5452affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        return moreKeysKeyboardView;
5465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
5492affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka     * Called when a key is long pressed. By default this will open more keys keyboard associated
5505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * with this key.
5515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param parentKey the key that was long pressed
5525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param tracker the pointer tracker which pressed the parent key
5535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return true if the long press is handled, false otherwise. Subclasses should call the
5545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * method on the base class if the subclass doesn't wish to handle the call.
5555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
5565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    protected boolean onLongPress(Key parentKey, PointerTracker tracker) {
5576dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        final int primaryCode = parentKey.mCode;
558e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka        if (parentKey.hasEmbeddedMoreKey()) {
559e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka            final int embeddedCode = KeySpecParser.getCode(getResources(), parentKey.mMoreKeys[0]);
5603708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            tracker.onLongPressed();
561e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka            invokeCodeInput(embeddedCode);
5623708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            invokeReleaseKey(primaryCode);
563a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            KeyboardSwitcher.getInstance().hapticAndAudioFeedback(primaryCode);
5643708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            return true;
5653708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        }
56681d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        if (primaryCode == Keyboard.CODE_SPACE || primaryCode == Keyboard.CODE_LANGUAGE_SWITCH) {
5673708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            // Long pressing the space key invokes IME switcher dialog.
5683708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) {
5696dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tracker.onLongPressed();
57042e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka                invokeReleaseKey(primaryCode);
57142e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka                return true;
5726dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
5736dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
57417dc10724bf0db04d0a4bfb2b8be0739ad9e60c6Tadashi G. Takaoka        return openMoreKeysPanel(parentKey, tracker);
5756dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
5766dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
57742e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    private boolean invokeCustomRequest(int code) {
5782a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        return mKeyboardActionListener.onCustomRequest(code);
57942e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    }
58042e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka
58142e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    private void invokeCodeInput(int primaryCode) {
582ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok        mKeyboardActionListener.onCodeInput(primaryCode,
5836dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                KeyboardActionListener.NOT_A_TOUCH_COORDINATE,
5846dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                KeyboardActionListener.NOT_A_TOUCH_COORDINATE);
58542e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    }
58642e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka
58742e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    private void invokeReleaseKey(int primaryCode) {
5882a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        mKeyboardActionListener.onReleaseKey(primaryCode, false);
5896dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
5906dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
5919d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private boolean openMoreKeysPanel(Key parentKey, PointerTracker tracker) {
5929d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        MoreKeysPanel moreKeysPanel = mMoreKeysPanelCache.get(parentKey);
5939d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (moreKeysPanel == null) {
5949d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            moreKeysPanel = onCreateMoreKeysPanel(parentKey);
5959d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            if (moreKeysPanel == null)
5965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                return false;
5979d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysPanelCache.put(parentKey, moreKeysPanel);
5985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
5999d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysWindow == null) {
6009d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysWindow = new PopupWindow(getContext());
6019d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysWindow.setBackgroundDrawable(null);
6022affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka            mMoreKeysWindow.setAnimationStyle(R.style.MoreKeysKeyboardAnimation);
6035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6049d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanel = moreKeysPanel;
6059d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanelPointerTrackerId = tracker.mPointerId;
60663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka
607f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
6082affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint) ? tracker.getLastX()
609f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka                : parentKey.mX + parentKey.mWidth / 2;
610f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka        final int pointY = parentKey.mY - keyboard.mVerticalGap;
6119d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        moreKeysPanel.showMoreKeysPanel(
6122a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                this, this, pointX, pointY, mMoreKeysWindow, mKeyboardActionListener);
6139d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        final int translatedX = moreKeysPanel.translateX(tracker.getLastX());
6149d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        final int translatedY = moreKeysPanel.translateY(tracker.getLastY());
615e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaoka        tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel);
6161b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka        dimEntireKeyboard(true);
6175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
6185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
6219d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysPanel != null) {
62263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return true;
62363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        } else {
6245c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            return PointerTracker.isAnyInSlidingKeyInput();
6255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public int getPointerCount() {
6295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mOldPointerCount;
6305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
6335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean onTouchEvent(MotionEvent me) {
63446286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        if (getKeyboard() == null) {
63546286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka            return false;
63646286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        }
637c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        return mTouchScreenRegulator.onTouchEvent(me);
638c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    }
639c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
640c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    @Override
641c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    public boolean processMotionEvent(MotionEvent me) {
642f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        final boolean nonDistinctMultitouch = !mHasDistinctMultitouch;
6435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int action = me.getActionMasked();
6445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int pointerCount = me.getPointerCount();
6455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int oldPointerCount = mOldPointerCount;
6465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mOldPointerCount = pointerCount;
6475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
6495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // If the device does not have distinct multi-touch support panel, ignore all multi-touch
6505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // events except a transition from/to single-touch.
651f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
6525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
6535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final long eventTime = me.getEventTime();
6565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int index = me.getActionIndex();
6575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int id = me.getPointerId(index);
65863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        final int x, y;
6599d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysPanel != null && id == mMoreKeysPanelPointerTrackerId) {
6609d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            x = mMoreKeysPanel.translateX((int)me.getX(index));
6619d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            y = mMoreKeysPanel.translateY((int)me.getY(index));
66263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        } else {
66363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            x = (int)me.getX(index);
66463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            y = (int)me.getY(index);
6655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
66615d4793911fa305e0a58aced925961e948582979satok        if (ENABLE_USABILITY_STUDY_LOG) {
66715d4793911fa305e0a58aced925961e948582979satok            final String eventTag;
66815d4793911fa305e0a58aced925961e948582979satok            switch (action) {
66915d4793911fa305e0a58aced925961e948582979satok                case MotionEvent.ACTION_UP:
67015d4793911fa305e0a58aced925961e948582979satok                    eventTag = "[Up]";
67115d4793911fa305e0a58aced925961e948582979satok                    break;
67215d4793911fa305e0a58aced925961e948582979satok                case MotionEvent.ACTION_DOWN:
67315d4793911fa305e0a58aced925961e948582979satok                    eventTag = "[Down]";
67415d4793911fa305e0a58aced925961e948582979satok                    break;
67515d4793911fa305e0a58aced925961e948582979satok                case MotionEvent.ACTION_POINTER_UP:
67615d4793911fa305e0a58aced925961e948582979satok                    eventTag = "[PointerUp]";
67715d4793911fa305e0a58aced925961e948582979satok                    break;
67815d4793911fa305e0a58aced925961e948582979satok                case MotionEvent.ACTION_POINTER_DOWN:
67915d4793911fa305e0a58aced925961e948582979satok                    eventTag = "[PointerDown]";
68015d4793911fa305e0a58aced925961e948582979satok                    break;
6813425852be4229b8937c3fd9a82d709d9bd8c4b4eKen Wakasa                case MotionEvent.ACTION_MOVE: // Skip this as being logged below
6823425852be4229b8937c3fd9a82d709d9bd8c4b4eKen Wakasa                    eventTag = "";
6833425852be4229b8937c3fd9a82d709d9bd8c4b4eKen Wakasa                    break;
68415d4793911fa305e0a58aced925961e948582979satok                default:
68515d4793911fa305e0a58aced925961e948582979satok                    eventTag = "[Action" + action + "]";
68615d4793911fa305e0a58aced925961e948582979satok                    break;
68715d4793911fa305e0a58aced925961e948582979satok            }
68815d4793911fa305e0a58aced925961e948582979satok            if (!TextUtils.isEmpty(eventTag)) {
68915d4793911fa305e0a58aced925961e948582979satok                UsabilityStudyLogUtils.getInstance().write(
6906029b43a3df1fa9a9baa339e2dbb984c6669d214Ken Wakasa                        eventTag + eventTime + "," + id + "," + x + "," + y + ","
6916029b43a3df1fa9a9baa339e2dbb984c6669d214Ken Wakasa                        + me.getSize(index) + "," + me.getPressure(index));
69215d4793911fa305e0a58aced925961e948582979satok            }
69315d4793911fa305e0a58aced925961e948582979satok        }
6945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
695f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (mKeyTimerHandler.isInKeyRepeat()) {
696e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
6975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Key repeating timer will be canceled if 2 or more keys are in action, and current
6985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // event (UP or DOWN) is non-modifier key.
6995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount > 1 && !tracker.isModifier()) {
700f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka                mKeyTimerHandler.cancelKeyRepeatTimer();
7015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
7025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Up event will pass through.
7035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
7065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Translate mutli-touch event to single-touch events on the device that has no distinct
7075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // multi-touch panel.
708f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch) {
7095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Use only main (id=0) pointer tracker.
710e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
7115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount == 1 && oldPointerCount == 2) {
7125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Multi-touch to single touch transition.
7135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send a down event for the latest pointer if the key is different from the
7145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // previous key.
715e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                final Key newKey = tracker.getKeyOn(x, y);
716e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                if (mOldKey != newKey) {
7170efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka                    tracker.onDownEvent(x, y, eventTime, this);
7185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                    if (action == MotionEvent.ACTION_UP)
719906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka                        tracker.onUpEvent(x, y, eventTime);
7205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                }
7215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 2 && oldPointerCount == 1) {
7225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Single-touch to multi-touch transition.
7235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send an up event for the last pointer.
7245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                final int lastX = tracker.getLastX();
7255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                final int lastY = tracker.getLastY();
726e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                mOldKey = tracker.getKeyOn(lastX, lastY);
727906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka                tracker.onUpEvent(lastX, lastY, eventTime);
7285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 1 && oldPointerCount == 1) {
7298ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka                tracker.processMotionEvent(action, x, y, eventTime, this);
7305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else {
7315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
7325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                        + " (old " + oldPointerCount + ")");
7335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
7345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
7355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (action == MotionEvent.ACTION_MOVE) {
7385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            for (int i = 0; i < pointerCount; i++) {
739e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka                final PointerTracker tracker = PointerTracker.getPointerTracker(
740e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka                        me.getPointerId(i), this);
74163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                final int px, py;
7429d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                if (mMoreKeysPanel != null
7439d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                        && tracker.mPointerId == mMoreKeysPanelPointerTrackerId) {
7449d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                    px = mMoreKeysPanel.translateX((int)me.getX(i));
7459d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                    py = mMoreKeysPanel.translateY((int)me.getY(i));
74663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                } else {
74763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                    px = (int)me.getX(i);
74863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                    py = (int)me.getY(i);
74963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                }
75063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                tracker.onMoveEvent(px, py, eventTime);
75115d4793911fa305e0a58aced925961e948582979satok                if (ENABLE_USABILITY_STUDY_LOG) {
75215d4793911fa305e0a58aced925961e948582979satok                    UsabilityStudyLogUtils.getInstance().write("[Move]"  + eventTime + ","
7536029b43a3df1fa9a9baa339e2dbb984c6669d214Ken Wakasa                            + me.getPointerId(i) + "," + px + "," + py + ","
7546029b43a3df1fa9a9baa339e2dbb984c6669d214Ken Wakasa                            + me.getSize(i) + "," + me.getPressure(i));
75515d4793911fa305e0a58aced925961e948582979satok                }
7565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
7575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        } else {
758e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
759e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            tracker.processMotionEvent(action, x, y, eventTime, this);
7605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
7635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
7645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
7665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void closing() {
7675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.closing();
7689d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        dismissMoreKeysPanel();
7699d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanelCache.clear();
7705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
7715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7729ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    @Override
7739d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public boolean dismissMoreKeysPanel() {
7749d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysWindow != null && mMoreKeysWindow.isShowing()) {
7759d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysWindow.dismiss();
7769d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysPanel = null;
7779d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysPanelPointerTrackerId = -1;
7781b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka            dimEntireKeyboard(false);
7795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
7805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return false;
7825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
7835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean handleBack() {
7859d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        return dismissMoreKeysPanel();
7865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
7875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
7896dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    public void draw(Canvas c) {
7906dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        Utils.GCUtils.getInstance().reset();
7916dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        boolean tryGC = true;
7926dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
7936dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            try {
7946dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                super.draw(c);
7956dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tryGC = false;
7966dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            } catch (OutOfMemoryError e) {
797dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka                tryGC = Utils.GCUtils.getInstance().tryGCOrWait(TAG, e);
7986dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
7996dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
8006dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
8016dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
8026dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    @Override
8036dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    protected void onAttachedToWindow() {
8046dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        // Token is available from here.
8056dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        VoiceProxy.getInstance().onAttachedToWindow();
8066dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
8076dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
808586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette    /**
809586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * Receives hover events from the input framework. This method overrides
810586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * View.dispatchHoverEvent(MotionEvent) on SDK version ICS or higher. On
811586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * lower SDK versions, this method is never called.
812586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *
813586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @param event The motion event to be dispatched.
814586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @return {@code true} if the event was handled by the view, {@code false}
815586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *         otherwise
816586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     */
817ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok    //Should not annotate @override
818586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette    public boolean dispatchHoverEvent(MotionEvent event) {
8195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
820e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
821586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette            return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
8225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
8235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
824586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette        // Reflection doesn't support calling superclass methods.
8255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return false;
8265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
82722b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
8285afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka    public void updateShortcutKey(boolean available) {
8297bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
8307bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        if (keyboard == null) return;
8317bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        final Key shortcutKey = keyboard.getKey(Keyboard.CODE_SHORTCUT);
8327bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        if (shortcutKey == null) return;
8337bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        shortcutKey.setEnabled(available);
8347bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        invalidateKey(shortcutKey);
8355afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka    }
8365afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka
83773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    private void updateAltCodeKeyWhileTyping() {
83873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
83973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        if (keyboard == null) return;
84073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        for (final Key key : keyboard.mAltCodeKeysWhileTyping) {
84173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            invalidateKey(key);
84273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
84373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    }
84473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
845dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka    public void startDisplayLanguageOnSpacebar(boolean subtypeChanged,
8464c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka            boolean needsToDisplayLanguage) {
84731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator;
8484112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mNeedsToDisplayLanguage = needsToDisplayLanguage;
849d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator == null) {
850aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            mNeedsToDisplayLanguage = false;
8514c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        } else {
852dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            if (subtypeChanged && needsToDisplayLanguage) {
85331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                setLanguageOnSpacebarAnimAlpha(ALPHA_OPAQUE);
85431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (animator.isStarted()) {
85531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    animator.cancel();
85631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
857d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka                animator.start();
858dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            } else {
85931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (!animator.isStarted()) {
86031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    mLanguageOnSpacebarAnimAlpha = mLanguageOnSpacebarFinalAlpha;
86131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
862dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            }
8634c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        }
8644112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
8654112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
8664112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
8674112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    public void updateAutoCorrectionState(boolean isAutoCorrection) {
8684112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        if (!mAutoCorrectionSpacebarLedEnabled) return;
8694112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedOn = isAutoCorrection;
8704112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
87122b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
87222b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
87322b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    @Override
874f9521c6f378e3f2aa13d9e382ae13708e3ae6317Tadashi G. Takaoka    protected void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint, KeyDrawParams params) {
8756bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.altCodeWhileTyping() && key.isEnabled()) {
87673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
87773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
8784112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        if (key.mCode == Keyboard.CODE_SPACE) {
879bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawSpacebar(key, canvas, paint);
880bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka
8814112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Whether space key needs to show the "..." popup hint for special purposes
8824112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            if (mIsSpacebarTriggeringPopupByLongPress
883cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka                    && SubtypeUtils.hasMultipleEnabledIMEsOrSubtypes(
884cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka                            true /* include aux subtypes */)) {
885bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka                drawKeyPopupHint(key, canvas, paint, params);
8864112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            }
88781d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        } else if (key.mCode == Keyboard.CODE_LANGUAGE_SWITCH) {
88881d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
889cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka            if (SubtypeUtils.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) {
89081d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka                drawKeyPopupHint(key, canvas, paint, params);
89181d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            }
892b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        } else {
893b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
89422b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka        }
89522b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
8964112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
8974112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    // Compute width of text with specified text size using paint.
898bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private int getTextWidth(Paint paint, String text, float textSize) {
8994112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        paint.setTextSize(textSize);
900bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        return (int)getLabelWidth(text, paint);
9014112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
9024112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
9034015a64a263b07178b99d3e3e864bd75da017638Tadashi G. Takaoka    // Layout locale language name on spacebar.
904bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private String layoutLanguageOnSpacebar(Paint paint, Locale locale, int width,
9054112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            float origTextSize) {
906bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        paint.setTextAlign(Align.CENTER);
907bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        paint.setTypeface(Typeface.DEFAULT);
9084112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        // Estimate appropriate language name text size to fit in maxTextWidth.
909cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        String language = StringUtils.getFullDisplayName(locale, true);
910bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        int textWidth = getTextWidth(paint, language, origTextSize);
9114112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        // Assuming text width and text size are proportional to each other.
9124112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        float textSize = origTextSize * Math.min(width / textWidth, 1.0f);
9134112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        // allow variable text size
914bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        textWidth = getTextWidth(paint, language, textSize);
9154112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        // If text size goes too small or text does not fit, use middle or short name
9164112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final boolean useMiddleName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME)
9174112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                || (textWidth > width);
9184112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
9194112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final boolean useShortName;
9204112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        if (useMiddleName) {
921cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka            language = StringUtils.getMiddleDisplayLanguage(locale);
922bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            textWidth = getTextWidth(paint, language, origTextSize);
9234112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            textSize = origTextSize * Math.min(width / textWidth, 1.0f);
9244112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            useShortName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME)
9254112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka                    || (textWidth > width);
9264112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        } else {
9274112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            useShortName = false;
9284112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9294112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
9304112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        if (useShortName) {
931cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka            language = StringUtils.getShortDisplayLanguage(locale);
932bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            textWidth = getTextWidth(paint, language, origTextSize);
9334112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            textSize = origTextSize * Math.min(width / textWidth, 1.0f);
9344112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9354112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        paint.setTextSize(textSize);
9364112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
9374112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        return language;
9384112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
9394112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
940bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private void drawSpacebar(Key key, Canvas canvas, Paint paint) {
941bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        final int width = key.mWidth;
942b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        final int height = key.mHeight;
9434112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
944aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka        // If input subtypes are explicitly selected.
945bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mNeedsToDisplayLanguage) {
946bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            final String language = layoutLanguageOnSpacebar(paint, mSpacebarLocale, width,
947bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka                    mSpacebarTextSize);
9484112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Draw language text with shadow
9494112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // In case there is no space icon, we will place the language text at the center of
9504112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // spacebar.
9514112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float descent = paint.descent();
9524112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float textHeight = -paint.ascent() + descent;
953b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            final float baseline = height / 2 + textHeight / 2;
954aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            paint.setColor(mSpacebarTextShadowColor);
95531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
9564112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent - 1, paint);
957aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            paint.setColor(mSpacebarTextColor);
95831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
9594112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent, paint);
9604112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9614112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
9624112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        // Draw the spacebar icon at the bottom
963bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mAutoCorrectionSpacebarLedOn) {
9644112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100;
9654112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight();
9664112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
9674112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
968bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight);
9694112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        } else if (mSpaceIcon != null) {
9704112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = mSpaceIcon.getIntrinsicWidth();
9714112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mSpaceIcon.getIntrinsicHeight();
9724112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
9734112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
974bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mSpaceIcon, x, y, iconWidth, iconHeight);
9754112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9764112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
9775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka}
978