MainKeyboardView.java revision 915f348b35cb66ed9696a51c9250f9b25799fb82
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;
2213ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaokaimport android.content.SharedPreferences;
235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.pm.PackageManager;
2427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaokaimport android.content.res.Resources;
254112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.content.res.TypedArray;
266dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport android.graphics.Canvas;
2722b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaokaimport android.graphics.Paint;
284112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.Paint.Align;
29bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaokaimport android.graphics.Typeface;
304112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.drawable.Drawable;
315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.os.Message;
3272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaokaimport android.os.SystemClock;
3313ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaokaimport android.preference.PreferenceManager;
345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.AttributeSet;
355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.Log;
365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.LayoutInflater;
375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.MotionEvent;
385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.View;
395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.ViewConfiguration;
40b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaokaimport android.view.ViewGroup;
41f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaokaimport android.view.inputmethod.InputMethodSubtype;
425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils;
445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
4515f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaokaimport com.android.inputmethod.annotations.ExternallyReferenced;
46f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
472321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
4849d8af8a4e900f3c68c333aba7fde0a11fd368b1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyDrawParams;
49081fedc42269845a31e962b2ac1aa017b3a2efeeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.TouchScreenRegulator;
504daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyangimport com.android.inputmethod.latin.Constants;
51547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaokaimport com.android.inputmethod.latin.CoordinateUtils;
5213ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaokaimport com.android.inputmethod.latin.DebugSettings;
536dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinIME;
5415d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.LatinImeLogger;
555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
569879f65651a748e4c0a45715eb7d5663652f1127Tadashi G. Takaokaimport com.android.inputmethod.latin.ResourceUtils;
575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.StaticInnerHandlerWrapper;
5827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaokaimport com.android.inputmethod.latin.StringUtils;
593bf57a5624679a20db26df912077a53b9f90ad36Tadashi G. Takaokaimport com.android.inputmethod.latin.SubtypeLocale;
6015d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils;
61c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasaimport com.android.inputmethod.latin.define.ProductionFlag;
626b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaokaimport java.util.Locale;
655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport java.util.WeakHashMap;
665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/**
685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * A view that is responsible for detecting key presses and touch movements.
695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
70dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedEnabled
71dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedIcon
72dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextRatio
73dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextColor
74dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextShadowColor
75dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha
76dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator
77dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator
78dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator
79dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyHysteresisDistance
80dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdTime
81dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdDistance
82dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_slidingKeyInputEnable
83dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyRepeatStartTimeout
84dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyRepeatInterval
85dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout
86dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout
87dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout
88dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint
893623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping
903623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold
913623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration
923623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom
933623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo
943623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom
953623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo
963623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureSamplingMinimumDistance
973623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureRecognitionMinimumTime
983623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold
993623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
1005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */
101a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
102081fedc42269845a31e962b2ac1aa017b3a2efeeTadashi G. Takaoka        TouchScreenRegulator.ProcessMotionEvent {
103c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka    private static final String TAG = MainKeyboardView.class.getSimpleName();
1045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
105d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge    // TODO: Kill process when the usability study mode was changed.
106d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge    private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
107d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge
108bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /** Listener for {@link KeyboardActionListener}. */
109bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private KeyboardActionListener mKeyboardActionListener;
110bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka
111bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /* Space key and its icons */
1124112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Key mSpaceKey;
1134112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Drawable mSpaceIcon;
114bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw language name on spacebar.
11531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private final int mLanguageOnSpacebarFinalAlpha;
11631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
117bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mNeedsToDisplayLanguage;
118fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka    private boolean mHasMultipleEnabledIMEsOrSubtypes;
1194daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
1204112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final float mSpacebarTextRatio;
1214112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private float mSpacebarTextSize;
1224112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final int mSpacebarTextColor;
1234112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final int mSpacebarTextShadowColor;
124bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // The minimum x-scale to fit the language name on spacebar.
125bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f;
126bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw auto correction LED on spacebar.
127bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mAutoCorrectionSpacebarLedOn;
128bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final boolean mAutoCorrectionSpacebarLedEnabled;
129bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final Drawable mAutoCorrectionSpacebarLedIcon;
130bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private static final int SPACE_LED_LENGTH_PERCENT = 80;
13115d4793911fa305e0a58aced925961e948582979satok
13273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    // Stuff to draw altCodeWhileTyping keys.
13331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
13431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
1354daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
13673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
1372affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    // More keys keyboard
1389d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private final WeakHashMap<Key, MoreKeysPanel> mMoreKeysPanelCache =
1399d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            new WeakHashMap<Key, MoreKeysPanel>();
1402affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
1415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
142081fedc42269845a31e962b2ac1aa017b3a2efeeTadashi G. Takaoka    private final TouchScreenRegulator mTouchScreenRegulator;
1435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
144bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    protected KeyDetector mKeyDetector;
14513ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka    private final boolean mHasDistinctMultitouch;
1465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private int mOldPointerCount = 1;
147e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mOldKey;
1485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
149160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    private final KeyTimerHandler mKeyTimerHandler;
1505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
151a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    private static final class KeyTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView>
1522321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka            implements TimerProxy {
15327e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        private static final int MSG_TYPING_STATE_EXPIRED = 0;
154f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_REPEAT_KEY = 1;
155f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_LONGPRESS_KEY = 2;
1560ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        private static final int MSG_DOUBLE_TAP = 3;
15772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        private static final int MSG_UPDATE_BATCH_INPUT = 4;
1585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1595509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mKeyRepeatStartTimeout;
1605509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mKeyRepeatInterval;
1615509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mLongPressKeyTimeout;
1625509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mLongPressShiftKeyTimeout;
1635509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mIgnoreAltCodeKeyTimeout;
16472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        private final int mGestureRecognitionUpdateTime;
1655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1665509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public KeyTimerHandler(final MainKeyboardView outerInstance,
1675509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                final TypedArray mainKeyboardViewAttr) {
1685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            super(outerInstance);
1695509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1705509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mKeyRepeatStartTimeout = mainKeyboardViewAttr.getInt(
1715509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0);
1725509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mKeyRepeatInterval = mainKeyboardViewAttr.getInt(
1735509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_keyRepeatInterval, 0);
1745509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mLongPressKeyTimeout = mainKeyboardViewAttr.getInt(
1755509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_longPressKeyTimeout, 0);
1765509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mLongPressShiftKeyTimeout = mainKeyboardViewAttr.getInt(
1775509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_longPressShiftKeyTimeout, 0);
1785509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
1795509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
18072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            mGestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
18172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0);
1825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        @Override
185dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void handleMessage(final Message msg) {
186c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            final MainKeyboardView keyboardView = getOuterInstance();
1875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            final PointerTracker tracker = (PointerTracker) msg.obj;
1885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            switch (msg.what) {
18927e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            case MSG_TYPING_STATE_EXPIRED:
19027e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka                startWhileTypingFadeinAnimation(keyboardView);
19127e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka                break;
1925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_REPEAT_KEY:
1938a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                final Key currentKey = tracker.getKey();
1948a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                if (currentKey != null && currentKey.mCode == msg.arg1) {
1958a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                    tracker.onRegisterKey(currentKey);
1965509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    startKeyRepeatTimer(tracker, mKeyRepeatInterval);
1978a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                }
1985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
1995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_LONGPRESS_KEY:
200a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                if (tracker != null) {
2012affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                    keyboardView.openMoreKeysKeyboardIfRequired(tracker.getKey(), tracker);
202a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                } else {
203a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    KeyboardSwitcher.getInstance().onLongPressTimeout(msg.arg1);
204a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                }
2055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
20672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            case MSG_UPDATE_BATCH_INPUT:
20772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                tracker.updateBatchInputByTimer(SystemClock.uptimeMillis());
20872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                startUpdateBatchInputTimer(tracker);
20972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                break;
2105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
2115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
213dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        private void startKeyRepeatTimer(final PointerTracker tracker, final long delay) {
2148a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            final Key key = tracker.getKey();
21520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            if (key == null) {
21620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka                return;
21720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            }
2188a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
21932c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka        }
22032c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka
2212321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
222dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startKeyRepeatTimer(final PointerTracker tracker) {
2235509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            startKeyRepeatTimer(tracker, mKeyRepeatStartTimeout);
2245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void cancelKeyRepeatTimer() {
2275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_REPEAT_KEY);
2285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2300d9d37cec2b3c4b4c3747baeb529bd2a70cbafb8Tadashi G. Takaoka        // TODO: Suppress layout changes in key repeat mode
2315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public boolean isInKeyRepeat() {
2320d9d37cec2b3c4b4c3747baeb529bd2a70cbafb8Tadashi G. Takaoka            return hasMessages(MSG_REPEAT_KEY);
2335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2352321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
236dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startLongPressTimer(final int code) {
23798b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka            cancelLongPressTimer();
238a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            final int delay;
239a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            switch (code) {
240240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            case Constants.CODE_SHIFT:
2415509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                delay = mLongPressShiftKeyTimeout;
242a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                break;
243a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            default:
244a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                delay = 0;
245a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                break;
246a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
247a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            if (delay > 0) {
248a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, code, 0), delay);
249a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
250a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        }
251a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka
252a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        @Override
253dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startLongPressTimer(final PointerTracker tracker) {
254a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            cancelLongPressTimer();
2557b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            if (tracker == null) {
2567b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                return;
2577b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            }
2587b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            final Key key = tracker.getKey();
2597b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            final int delay;
2607b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            switch (key.mCode) {
261240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            case Constants.CODE_SHIFT:
2625509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                delay = mLongPressShiftKeyTimeout;
2637b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                break;
2647b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            default:
2657b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                if (KeyboardSwitcher.getInstance().isInMomentarySwitchState()) {
2667b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                    // We use longer timeout for sliding finger input started from the symbols
2677b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                    // mode key.
2685509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    delay = mLongPressKeyTimeout * 3;
2697b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                } else {
2705509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    delay = mLongPressKeyTimeout;
271a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                }
2727b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                break;
2737b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            }
2747b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            if (delay > 0) {
2757b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, tracker), delay);
276a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
2775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2792321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
28098b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer() {
2815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_LONGPRESS_KEY);
2825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
284d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
28545213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka                final ObjectAnimator animatorToStart) {
28645213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            float startFraction = 0.0f;
28745213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            if (animatorToCancel.isStarted()) {
288b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka                animatorToCancel.cancel();
28945213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka                startFraction = 1.0f - animatorToCancel.getAnimatedFraction();
290b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            }
29145213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            final long startTime = (long)(animatorToStart.getDuration() * startFraction);
29245213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            animatorToStart.start();
29345213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            animatorToStart.setCurrentPlayTime(startTime);
294b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka        }
295b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka
296d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void startWhileTypingFadeinAnimation(final MainKeyboardView keyboardView) {
297d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator,
298d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                    keyboardView.mAltCodeKeyWhileTypingFadeinAnimator);
299d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        }
300d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
301d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void startWhileTypingFadeoutAnimation(final MainKeyboardView keyboardView) {
302d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator,
303d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                    keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator);
304d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        }
305d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
3062321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
307dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startTypingStateTimer(final Key typedKey) {
308d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) {
309d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                return;
310d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            }
311d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
31273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            final boolean isTyping = isTypingState();
313f1678ba8024606349bc184cfeaead2be059f7b5bTadashi G. Takaoka            removeMessages(MSG_TYPING_STATE_EXPIRED);
314d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            final MainKeyboardView keyboardView = getOuterInstance();
315d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
316d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            // When user hits the space or the enter key, just cancel the while-typing timer.
317d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            final int typedCode = typedKey.mCode;
318240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) {
319d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                startWhileTypingFadeinAnimation(keyboardView);
320d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                return;
321d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            }
322d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
32373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            sendMessageDelayed(
3245509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    obtainMessage(MSG_TYPING_STATE_EXPIRED), mIgnoreAltCodeKeyTimeout);
32573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            if (isTyping) {
32673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                return;
32773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            }
328d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            startWhileTypingFadeoutAnimation(keyboardView);
32993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
33093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
33193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        @Override
33273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState() {
33373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            return hasMessages(MSG_TYPING_STATE_EXPIRED);
33493246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
33593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
3369f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka        @Override
3370ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void startDoubleTapTimer() {
3380ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP),
3395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                    ViewConfiguration.getDoubleTapTimeout());
3405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
3415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3420ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        @Override
343beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void cancelDoubleTapTimer() {
344beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            removeMessages(MSG_DOUBLE_TAP);
345beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        }
346beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka
347beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        @Override
3480ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public boolean isInDoubleTapTimeout() {
3490ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            return hasMessages(MSG_DOUBLE_TAP);
3505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
351c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
352c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        @Override
3530ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void cancelKeyTimers() {
3540ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelKeyRepeatTimer();
3550ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelLongPressTimer();
356c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
357c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
35872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        @Override
35972fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        public void startUpdateBatchInputTimer(final PointerTracker tracker) {
36072fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            if (mGestureRecognitionUpdateTime <= 0) {
36172fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                return;
36272fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            }
36372fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
36472fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_UPDATE_BATCH_INPUT, tracker),
36572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka                    mGestureRecognitionUpdateTime);
36672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        }
36772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
36872fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        @Override
369915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        public void cancelUpdateBatchInputTimer(final PointerTracker tracker) {
370915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka            removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
371915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        }
372915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka
373915f348b35cb66ed9696a51c9250f9b25799fb82Tadashi G. Takaoka        @Override
3742db9e1c447a71f0aec3067697cf294f711a9e4e0Tadashi G. Takaoka        public void cancelAllUpdateBatchInputTimers() {
37572fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka            removeMessages(MSG_UPDATE_BATCH_INPUT);
37672fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka        }
37772fd0968e5227ffc383b1f9d096872ba39cfdce8Tadashi G. Takaoka
3780ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void cancelAllMessages() {
3790ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelKeyTimers();
3802db9e1c447a71f0aec3067697cf294f711a9e4e0Tadashi G. Takaoka            cancelAllUpdateBatchInputTimers();
381c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
382c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka    }
383c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
384dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs) {
385c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka        this(context, attrs, R.attr.mainKeyboardViewStyle);
3865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
388dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
3895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super(context, attrs, defStyle);
3905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
391081fedc42269845a31e962b2ac1aa017b3a2efeeTadashi G. Takaoka        mTouchScreenRegulator = new TouchScreenRegulator(context, this);
392c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
39313ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
39413ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final boolean forceNonDistinctMultitouch = prefs.getBoolean(
395a5ff9f0c77005769f92ca1131882bb4e3ca18980Tadashi G. Takaoka                DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, false);
39613ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final boolean hasDistinctMultitouch = context.getPackageManager()
3975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
39813ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        mHasDistinctMultitouch = hasDistinctMultitouch && !forceNonDistinctMultitouch;
399918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka        final Resources res = getResources();
400d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka        final boolean needsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
401fa7f08b37327c825baf0a6455d6f7fdf8b37da57Tadashi G. Takaoka                ResourceUtils.getDeviceOverrideValue(
402fa7f08b37327c825baf0a6455d6f7fdf8b37da57Tadashi G. Takaoka                        res, R.array.phantom_sudden_move_event_device_list));
40393b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        PointerTracker.init(needsPhantomSuddenMoveEventHack);
40422b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
4054112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final TypedArray a = context.obtainStyledAttributes(
406c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
4074112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedEnabled = a.getBoolean(
408c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false);
4094112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedIcon = a.getDrawable(
410c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon);
411ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka        mSpacebarTextRatio = a.getFraction(
412ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                R.styleable.MainKeyboardView_spacebarTextRatio, 1, 1, 1.0f);
413c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka        mSpacebarTextColor = a.getColor(R.styleable.MainKeyboardView_spacebarTextColor, 0);
4144112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextShadowColor = a.getColor(
415c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_spacebarTextShadowColor, 0);
41631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFinalAlpha = a.getInt(
417c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha,
4184daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                Constants.Color.ALPHA_OPAQUE);
419d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        final int languageOnSpacebarFadeoutAnimatorResId = a.getResourceId(
420c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
42173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeoutAnimatorResId = a.getResourceId(
422c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
42373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeinAnimatorResId = a.getResourceId(
424c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
425160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
426160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        final float keyHysteresisDistance = a.getDimension(
427c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_keyHysteresisDistance, 0);
428f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        final float keyHysteresisDistanceForSlidingModifier = a.getDimension(
429f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0);
430f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mKeyDetector = new KeyDetector(
431f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka                keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
4325509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        mKeyTimerHandler = new KeyTimerHandler(this, a);
4332affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        mConfigShowMoreKeysKeyboardAtTouchedPoint = a.getBoolean(
434c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
4355509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        PointerTracker.setParameters(a);
4364112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        a.recycle();
437160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
43831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator(
43931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                languageOnSpacebarFadeoutAnimatorResId, this);
44031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator(
44131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeoutAnimatorResId, this);
44231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator(
44331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeinAnimatorResId, this);
44431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
44531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
446dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private ObjectAnimator loadObjectAnimator(final int resId, final Object target) {
44720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (resId == 0) {
44820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return null;
44920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
45031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
45131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                getContext(), resId);
452d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator != null) {
45331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            animator.setTarget(target);
45473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
45531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return animator;
45631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
45773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
45815f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
45931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getLanguageOnSpacebarAnimAlpha() {
46031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mLanguageOnSpacebarAnimAlpha;
461d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka    }
462d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka
46315f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
464dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setLanguageOnSpacebarAnimAlpha(final int alpha) {
46531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarAnimAlpha = alpha;
46631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        invalidateKey(mSpaceKey);
46731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
46831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
46915f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
47031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getAltCodeKeyWhileTypingAnimAlpha() {
47131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mAltCodeKeyWhileTypingAnimAlpha;
47231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
47331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
47415f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
475dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) {
47631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingAnimAlpha = alpha;
47731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        updateAltCodeKeyWhileTyping();
4785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
480dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboardActionListener(final KeyboardActionListener listener) {
4815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyboardActionListener = listener;
4825c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        PointerTracker.setKeyboardActionListener(listener);
4835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
4865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns the {@link KeyboardActionListener} object.
4875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return the listener attached to this keyboard
4885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
4890efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
4900efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyboardActionListener getKeyboardActionListener() {
4915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mKeyboardActionListener;
4925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
494bb4be5444b845655c0eb80bcfbb66f93603802eaTadashi G. Takaoka    @Override
4950efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyDetector getKeyDetector() {
4960efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyDetector;
4970efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
4980efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
4990efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
500f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public DrawingProxy getDrawingProxy() {
501f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        return this;
502f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
503f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
504f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    @Override
5050efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public TimerProxy getTimerProxy() {
5060efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyTimerHandler;
5070efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
5080efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
5095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
5105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Attaches a keyboard to this view. The keyboard can be switched at any time and the
5115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * view will re-layout itself to accommodate the keyboard.
5125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see Keyboard
5135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see #getKeyboard()
5145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param keyboard the keyboard to display in this view
5155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
5165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
517dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboard(final Keyboard keyboard) {
5188a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        // Remove any pending messages, except dismissing preview and key repeat.
5198a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        mKeyTimerHandler.cancelLongPressTimer();
5205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.setKeyboard(keyboard);
5215a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyDetector.setKeyboard(
5225a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka                keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
5238335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setKeyDetector(mKeyDetector);
524081fedc42269845a31e962b2ac1aa017b3a2efeeTadashi G. Takaoka        mTouchScreenRegulator.setKeyboardGeometry(keyboard.mOccupiedWidth);
5259d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanelCache.clear();
5264112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
527240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        mSpaceKey = keyboard.getKey(Constants.CODE_SPACE);
5281f2d0aa6c9b343848ee51e5bc13ccaaadf3ba4feTadashi G. Takaoka        mSpaceIcon = (mSpaceKey != null)
5294daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null;
5304112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
5314112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
53248a7681e064ae259b840f0e757da2d716043d893Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
533c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            ResearchLogger.mainKeyboardView_setKeyboard(keyboard);
53448a7681e064ae259b840f0e757da2d716043d893Kurt Partridge        }
535f147794fd41491a3383e6aca6d49007f58124068alanv
536f147794fd41491a3383e6aca6d49007f58124068alanv        // This always needs to be set since the accessibility state can
537f147794fd41491a3383e6aca6d49007f58124068alanv        // potentially change without the keyboard being set again.
538b6ca354431367b625daf9fff5fbe4b1f5ef996abKen Wakasa        AccessibleKeyboardViewProxy.getInstance().setKeyboard();
5395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5418335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
542dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
5438335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable);
5448335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
5458335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
546dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
5478335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser);
5480657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    }
5490657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka
5505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
5514331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    protected void onAttachedToWindow() {
5524331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        super.onAttachedToWindow();
5534331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        // Notify the research logger that the keyboard view has been attached.  This is needed
5544331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        // to properly show the splash screen, which requires that the window token of the
5554331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        // KeyboardView be non-null.
5564331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
557c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka            ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(this);
558c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        }
559c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    }
560c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka
561c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    @Override
562c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    protected void onDetachedFromWindow() {
563c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        super.onDetachedFromWindow();
564c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        // Notify the research logger that the keyboard view has been detached.  This is needed
565c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        // to invalidate the reference of {@link MainKeyboardView} to null.
566c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        if (ProductionFlag.IS_EXPERIMENTAL) {
567c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka            ResearchLogger.getInstance().mainKeyboardView_onDetachedFromWindow();
5684331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        }
5694331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    }
5704331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge
5714331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    @Override
5725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void cancelAllMessages() {
573f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        mKeyTimerHandler.cancelAllMessages();
5745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.cancelAllMessages();
5755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
577dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private boolean openMoreKeysKeyboardIfRequired(final Key parentKey,
578dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final PointerTracker tracker) {
5795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Check if we have a popup layout specified first.
5809d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysLayout == 0) {
5815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return false;
5825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
5835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
58463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        // Check if we are already displaying popup panel.
58520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (mMoreKeysPanel != null) {
58663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return false;
58720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
58820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (parentKey == null) {
5895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return false;
59020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
59163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        return onLongPress(parentKey, tracker);
5925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5949d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    // This default implementation returns a more keys panel.
595dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    protected MoreKeysPanel onCreateMoreKeysPanel(final Key parentKey) {
59620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (parentKey.mMoreKeys == null) {
5975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return null;
59820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
5995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6009d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        final View container = LayoutInflater.from(getContext()).inflate(mMoreKeysLayout, null);
60120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (container == null) {
6025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            throw new NullPointerException();
60320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
6045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6052affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        final MoreKeysKeyboardView moreKeysKeyboardView =
6062affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view);
6077ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final Keyboard moreKeysKeyboard = new MoreKeysKeyboard.Builder(container, parentKey, this)
6087ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka                .build();
6092affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
610b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka        container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
6115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6122affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        return moreKeysKeyboardView;
6135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
6162affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka     * Called when a key is long pressed. By default this will open more keys keyboard associated
6175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * with this key.
6185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param parentKey the key that was long pressed
6195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param tracker the pointer tracker which pressed the parent key
6205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return true if the long press is handled, false otherwise. Subclasses should call the
6215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * method on the base class if the subclass doesn't wish to handle the call.
6225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
623dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    protected boolean onLongPress(final Key parentKey, final PointerTracker tracker) {
6249bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
625c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            ResearchLogger.mainKeyboardView_onLongPress();
6269bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
6276dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        final int primaryCode = parentKey.mCode;
628e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka        if (parentKey.hasEmbeddedMoreKey()) {
629ed3bac91f242850c6d1833a5f8981b9cc208c5ddTadashi G. Takaoka            final int embeddedCode = parentKey.mMoreKeys[0].mCode;
6303708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            tracker.onLongPressed();
631e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka            invokeCodeInput(embeddedCode);
6323708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            invokeReleaseKey(primaryCode);
633a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            KeyboardSwitcher.getInstance().hapticAndAudioFeedback(primaryCode);
6343708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            return true;
6353708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        }
636240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (primaryCode == Constants.CODE_SPACE || primaryCode == Constants.CODE_LANGUAGE_SWITCH) {
6373708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            // Long pressing the space key invokes IME switcher dialog.
6383708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) {
6396dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tracker.onLongPressed();
64042e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka                invokeReleaseKey(primaryCode);
64142e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka                return true;
6426dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
6436dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
64417dc10724bf0db04d0a4bfb2b8be0739ad9e60c6Tadashi G. Takaoka        return openMoreKeysPanel(parentKey, tracker);
6456dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
6466dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
647dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private boolean invokeCustomRequest(final int code) {
6482a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        return mKeyboardActionListener.onCustomRequest(code);
64942e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    }
65042e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka
651dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private void invokeCodeInput(final int primaryCode) {
652ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka        mKeyboardActionListener.onCodeInput(
653ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka                primaryCode, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
65442e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    }
65542e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka
656dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private void invokeReleaseKey(final int primaryCode) {
6572a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        mKeyboardActionListener.onReleaseKey(primaryCode, false);
6586dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
6596dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
660dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private boolean openMoreKeysPanel(final Key parentKey, final PointerTracker tracker) {
6619d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        MoreKeysPanel moreKeysPanel = mMoreKeysPanelCache.get(parentKey);
6629d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (moreKeysPanel == null) {
6639d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            moreKeysPanel = onCreateMoreKeysPanel(parentKey);
66420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            if (moreKeysPanel == null) {
6655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                return false;
66620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            }
6679d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysPanelCache.put(parentKey, moreKeysPanel);
6685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
66963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka
670547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        final int[] lastCoords = CoordinateUtils.newInstance();
671547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        tracker.getLastCoordinates(lastCoords);
6727ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final boolean keyPreviewEnabled = isKeyPreviewPopupEnabled() && !parentKey.noKeyPreview();
6737ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually horizontally aligned with the center of the parent key.
6747ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // If showMoreKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more
6757ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // keys keyboard is placed at the touch point of the parent key.
6767ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled)
677547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                ? CoordinateUtils.x(lastCoords)
678f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka                : parentKey.mX + parentKey.mWidth / 2;
6797ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually vertically aligned with the top edge of the parent key
6807ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically
6817ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // aligned with the bottom edge of the visible part of the key preview.
68229d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@code mPreviewVisibleOffset} has been set appropriately in
68329d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@link KeyboardView#showKeyPreview(PointerTracker)}.
68429d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        final int pointY = parentKey.mY + mKeyPreviewDrawParams.mPreviewVisibleOffset;
685fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener);
686547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        final int translatedX = moreKeysPanel.translateX(CoordinateUtils.x(lastCoords));
687547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka        final int translatedY = moreKeysPanel.translateY(CoordinateUtils.y(lastCoords));
688e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaoka        tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel);
689fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        dimEntireKeyboard(true /* dimmed */);
6905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
6915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
6949d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysPanel != null) {
69563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return true;
6965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
69720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        return PointerTracker.isAnyInSlidingKeyInput();
6985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public int getPointerCount() {
7015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mOldPointerCount;
7025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
7035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
705dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    public boolean dispatchTouchEvent(MotionEvent event) {
706dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
707dc2ee7772402633817702e95c2a5b17f6dec03ebalanv            return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event);
708dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        }
709dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        return super.dispatchTouchEvent(event);
710dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    }
711dc2ee7772402633817702e95c2a5b17f6dec03ebalanv
712dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    @Override
713dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean onTouchEvent(final MotionEvent me) {
71446286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        if (getKeyboard() == null) {
71546286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka            return false;
71646286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        }
717c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        return mTouchScreenRegulator.onTouchEvent(me);
718c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    }
719c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
720c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    @Override
721dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean processMotionEvent(final MotionEvent me) {
722f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        final boolean nonDistinctMultitouch = !mHasDistinctMultitouch;
7235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int action = me.getActionMasked();
7245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int pointerCount = me.getPointerCount();
7255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int oldPointerCount = mOldPointerCount;
7265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mOldPointerCount = pointerCount;
7275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
7295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // If the device does not have distinct multi-touch support panel, ignore all multi-touch
7305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // events except a transition from/to single-touch.
731f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
7325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
7335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final long eventTime = me.getEventTime();
7365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int index = me.getActionIndex();
7375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int id = me.getPointerId(index);
73835580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final int x = (int)me.getX(index);
73935580bad6f3da3b204653825bbb6871563e70728Tom Ouyang        final int y = (int)me.getY(index);
74035580bad6f3da3b204653825bbb6871563e70728Tom Ouyang
741fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        // TODO: This might be moved to the tracker.processMotionEvent() call below.
742fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        if (ENABLE_USABILITY_STUDY_LOG && action != MotionEvent.ACTION_MOVE) {
743fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            writeUsabilityStudyLog(me, action, eventTime, index, id, x, y);
744d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge        }
745fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        // TODO: This should be moved to the tracker.processMotionEvent() call below.
746fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        // Currently the same "move" event is being logged twice.
747c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa        if (ProductionFlag.IS_EXPERIMENTAL) {
748fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            ResearchLogger.mainKeyboardView_processMotionEvent(
749fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    me, action, eventTime, index, id, x, y);
75015d4793911fa305e0a58aced925961e948582979satok        }
7515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
752f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (mKeyTimerHandler.isInKeyRepeat()) {
753e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
7545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Key repeating timer will be canceled if 2 or more keys are in action, and current
7555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // event (UP or DOWN) is non-modifier key.
7565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount > 1 && !tracker.isModifier()) {
757f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka                mKeyTimerHandler.cancelKeyRepeatTimer();
7585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
7595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Up event will pass through.
7605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
7635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Translate mutli-touch event to single-touch events on the device that has no distinct
7645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // multi-touch panel.
765f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch) {
7665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Use only main (id=0) pointer tracker.
767e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
7685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount == 1 && oldPointerCount == 2) {
7695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Multi-touch to single touch transition.
7705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send a down event for the latest pointer if the key is different from the
7715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // previous key.
772e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                final Key newKey = tracker.getKeyOn(x, y);
773e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                if (mOldKey != newKey) {
7740efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka                    tracker.onDownEvent(x, y, eventTime, this);
775fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    if (action == MotionEvent.ACTION_UP) {
776906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka                        tracker.onUpEvent(x, y, eventTime);
777fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    }
7785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                }
7795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 2 && oldPointerCount == 1) {
7805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Single-touch to multi-touch transition.
7815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send an up event for the last pointer.
782547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                final int[] lastCoords = CoordinateUtils.newInstance();
783547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                mOldKey = tracker.getKeyOn(
784547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                        CoordinateUtils.x(lastCoords), CoordinateUtils.y(lastCoords));
785547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                tracker.onUpEvent(
786547b638194c05f971003edb06c3c6c489a76da5fTadashi G. Takaoka                        CoordinateUtils.x(lastCoords), CoordinateUtils.y(lastCoords), eventTime);
7875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 1 && oldPointerCount == 1) {
7888ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka                tracker.processMotionEvent(action, x, y, eventTime, this);
7895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else {
7905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
7915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                        + " (old " + oldPointerCount + ")");
7925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
7935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
7945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (action == MotionEvent.ACTION_MOVE) {
7975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            for (int i = 0; i < pointerCount; i++) {
798d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                final int pointerId = me.getPointerId(i);
799e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka                final PointerTracker tracker = PointerTracker.getPointerTracker(
800d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                        pointerId, this);
80135580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int px = (int)me.getX(i);
80235580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                final int py = (int)me.getY(i);
80335580bad6f3da3b204653825bbb6871563e70728Tom Ouyang                tracker.onMoveEvent(px, py, eventTime, me);
804d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                if (ENABLE_USABILITY_STUDY_LOG) {
805fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    writeUsabilityStudyLog(me, action, eventTime, i, pointerId, px, py);
806d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                }
807c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa                if (ProductionFlag.IS_EXPERIMENTAL) {
808fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    ResearchLogger.mainKeyboardView_processMotionEvent(
809fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                            me, action, eventTime, i, pointerId, px, py);
81015d4793911fa305e0a58aced925961e948582979satok                }
8115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
8125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        } else {
813e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
814e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            tracker.processMotionEvent(action, x, y, eventTime, this);
8155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
8165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
8175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
8185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
8195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
820fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka    private static void writeUsabilityStudyLog(final MotionEvent me, final int action,
821fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            final long eventTime, final int index, final int id, final int x, final int y) {
822fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        final String eventTag;
823fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        switch (action) {
824fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_UP:
825fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[Up]";
826fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
827fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
828fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[Down]";
829fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
830fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
831fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[PointerUp]";
832fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
833fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
834fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[PointerDown]";
835fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
836fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_MOVE:
837fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[Move]";
838fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
839fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        default:
840fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[Action" + action + "]";
841fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
842fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        }
843fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        final float size = me.getSize(index);
844fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        final float pressure = me.getPressure(index);
845fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        UsabilityStudyLogUtils.getInstance().write(
846fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                eventTag + eventTime + "," + id + "," + x + "," + y + "," + size + "," + pressure);
847fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka    }
848fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka
8495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
8505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void closing() {
8515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.closing();
852ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        onCancelMoreKeysPanel();
8539d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanelCache.clear();
8545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
8555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
8569ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    @Override
857ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    public void onCancelMoreKeysPanel() {
858ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        super.onCancelMoreKeysPanel();
859ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang        PointerTracker.dismissAllMoreKeysPanels();
860ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    }
861ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang
862ac69ab400d1ea4f90b4ca24486d62212decf1069Tom Ouyang    @Override
863fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang    public boolean onDismissMoreKeysPanel() {
864fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        dimEntireKeyboard(false /* dimmed */);
865fa2d543785c52f639ad3157c57420f58a199c550Tom Ouyang        return super.onDismissMoreKeysPanel();
8665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
8675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
868586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette    /**
869c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka     * Receives hover events from the input framework.
870586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *
871586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @param event The motion event to be dispatched.
872586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @return {@code true} if the event was handled by the view, {@code false}
873586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *         otherwise
874586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     */
875c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka    @Override
876dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean dispatchHoverEvent(final MotionEvent event) {
8775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
878e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
879586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette            return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
8805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
8815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
882586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette        // Reflection doesn't support calling superclass methods.
8835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return false;
8845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
88522b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
886dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateShortcutKey(final boolean available) {
8877bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
88820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (keyboard == null) {
88920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
89020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
891240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        final Key shortcutKey = keyboard.getKey(Constants.CODE_SHORTCUT);
89220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (shortcutKey == null) {
89320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
89420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
8957bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        shortcutKey.setEnabled(available);
8967bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        invalidateKey(shortcutKey);
8975afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka    }
8985afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka
89973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    private void updateAltCodeKeyWhileTyping() {
90073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
90120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (keyboard == null) {
90220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
90320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
90473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        for (final Key key : keyboard.mAltCodeKeysWhileTyping) {
90573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            invalidateKey(key);
90673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
90773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    }
90873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
909dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged,
910dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final boolean needsToDisplayLanguage, final boolean hasMultipleEnabledIMEsOrSubtypes) {
9114112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mNeedsToDisplayLanguage = needsToDisplayLanguage;
912fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes;
913fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator;
914d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator == null) {
915aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            mNeedsToDisplayLanguage = false;
9164c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        } else {
917dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            if (subtypeChanged && needsToDisplayLanguage) {
9184daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE);
91931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (animator.isStarted()) {
92031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    animator.cancel();
92131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
922d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka                animator.start();
923dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            } else {
92431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (!animator.isStarted()) {
92531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    mLanguageOnSpacebarAnimAlpha = mLanguageOnSpacebarFinalAlpha;
92631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
927dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            }
9284c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        }
9294112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
9304112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
9314112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
932dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateAutoCorrectionState(final boolean isAutoCorrection) {
93320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (!mAutoCorrectionSpacebarLedEnabled) {
93420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
93520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
9364112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedOn = isAutoCorrection;
9374112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
93822b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
93922b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
94022b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    @Override
941dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
942dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final KeyDrawParams params) {
9436bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.altCodeWhileTyping() && key.isEnabled()) {
94473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
94573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
946240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (key.mCode == Constants.CODE_SPACE) {
947bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawSpacebar(key, canvas, paint);
9484112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Whether space key needs to show the "..." popup hint for special purposes
949fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) {
950bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka                drawKeyPopupHint(key, canvas, paint, params);
9514112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            }
952240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        } else if (key.mCode == Constants.CODE_LANGUAGE_SWITCH) {
95381d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
954fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            drawKeyPopupHint(key, canvas, paint, params);
955b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        } else {
956b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
95722b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka        }
95822b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
9594112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
960dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) {
961bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(1.0f);
962bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        final float textWidth = getLabelWidth(text, paint);
96320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (textWidth < width) {
96420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return true;
96520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
966bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
967bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        final float scaleX = width / textWidth;
96820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) {
96920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return false;
97020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
971bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
972bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(scaleX);
973bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        return getLabelWidth(text, paint) < width;
974bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    }
975bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
976bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // Layout language name on spacebar.
977dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private String layoutLanguageOnSpacebar(final Paint paint, final InputMethodSubtype subtype,
978bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final int width) {
979bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        // Choose appropriate language name to fit into the width.
98020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        final String fullText = getFullDisplayName(subtype, getResources());
98120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, fullText, paint)) {
98220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return fullText;
983bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        }
984bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
98520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        final String middleText = getMiddleDisplayName(subtype);
98620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, middleText, paint)) {
98720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return middleText;
9884112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9894112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
99020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        final String shortText = getShortDisplayName(subtype);
99120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, shortText, paint)) {
99220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return shortText;
9934112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9944112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
995bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        return "";
9964112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
9974112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
998dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private void drawSpacebar(final Key key, final Canvas canvas, final Paint paint) {
999bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        final int width = key.mWidth;
1000b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        final int height = key.mHeight;
10014112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
1002f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaoka        // If input language are explicitly selected.
1003bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mNeedsToDisplayLanguage) {
1004bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTextAlign(Align.CENTER);
1005bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTypeface(Typeface.DEFAULT);
1006bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTextSize(mSpacebarTextSize);
1007bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final InputMethodSubtype subtype = getKeyboard().mId.mSubtype;
1008bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final String language = layoutLanguageOnSpacebar(paint, subtype, width);
10094112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Draw language text with shadow
10104112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float descent = paint.descent();
10114112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float textHeight = -paint.ascent() + descent;
1012b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            final float baseline = height / 2 + textHeight / 2;
1013aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            paint.setColor(mSpacebarTextShadowColor);
101431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
10154112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent - 1, paint);
1016aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            paint.setColor(mSpacebarTextColor);
101731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
10184112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent, paint);
10194112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
10204112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
10214112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        // Draw the spacebar icon at the bottom
1022bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mAutoCorrectionSpacebarLedOn) {
10234112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100;
10244112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight();
10254112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
10264112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
1027bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight);
10284112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        } else if (mSpaceIcon != null) {
10294112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = mSpaceIcon.getIntrinsicWidth();
10304112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mSpaceIcon.getIntrinsicHeight();
10314112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
10324112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
1033bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mSpaceIcon, x, y, iconWidth, iconHeight);
10344112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
10354112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
103627b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
103727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // InputMethodSubtype's display name for spacebar text in its locale.
103827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //        isAdditionalSubtype (T=true, F=false)
103976d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    // locale layout  | Short  Middle      Full
104076d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    // ------ ------- - ---- --------- ----------------------
104176d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  en_US qwerty  F  En  English   English (US)           exception
104276d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  en_GB qwerty  F  En  English   English (UK)           exception
104376d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  es_US spanish F  Es  Español   Español (EE.UU.)       exception
104476d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  fr    azerty  F  Fr  Français  Français
104576d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  fr_CA qwerty  F  Fr  Français  Français (Canada)
104676d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  de    qwertz  F  De  Deutsch   Deutsch
104776d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  zz    qwerty  F      QWERTY    QWERTY
104876d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  fr    qwertz  T  Fr  Français  Français (QWERTZ)
104976d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  de    qwerty  T  De  Deutsch   Deutsch (QWERTY)
105076d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  en_US azerty  T  En  English   English (US) (AZERTY)
105176d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  zz    azerty  T      AZERTY    AZERTY
105227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
105327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's full display name in its locale.
1054dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    static String getFullDisplayName(final InputMethodSubtype subtype, final Resources res) {
105527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
105627b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
105727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
105827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
105927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        return SubtypeLocale.getSubtypeDisplayName(subtype, res);
106027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
106127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
106227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's short display name in its locale.
1063dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    static String getShortDisplayName(final InputMethodSubtype subtype) {
106427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
106527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return "";
106627b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
106727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        final Locale locale = SubtypeLocale.getSubtypeLocale(subtype);
106827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        return StringUtils.toTitleCase(locale.getLanguage(), locale);
106927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
107027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
107127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's middle display name in its locale.
1072dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    static String getMiddleDisplayName(final InputMethodSubtype subtype) {
107327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
107427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
107527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
107627b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        final Locale locale = SubtypeLocale.getSubtypeLocale(subtype);
107727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        return StringUtils.toTitleCase(locale.getDisplayLanguage(locale), locale);
107827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
10795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka}
1080