MainKeyboardView.java revision 76d4ffeebfd084913a8c1b7433dff48f5b2063df
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;
3213ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaokaimport android.preference.PreferenceManager;
335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.AttributeSet;
345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.Log;
355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.LayoutInflater;
365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.MotionEvent;
375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.View;
385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.ViewConfiguration;
39b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaokaimport android.view.ViewGroup;
40f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaokaimport android.view.inputmethod.InputMethodSubtype;
415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.widget.PopupWindow;
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;
5113ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaokaimport com.android.inputmethod.latin.DebugSettings;
526dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinIME;
5315d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.LatinImeLogger;
545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
559879f65651a748e4c0a45715eb7d5663652f1127Tadashi G. Takaokaimport com.android.inputmethod.latin.ResourceUtils;
565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.StaticInnerHandlerWrapper;
5727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaokaimport com.android.inputmethod.latin.StringUtils;
583bf57a5624679a20db26df912077a53b9f90ad36Tadashi G. Takaokaimport com.android.inputmethod.latin.SubtypeLocale;
5915d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils;
60c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasaimport com.android.inputmethod.latin.define.ProductionFlag;
616b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaokaimport java.util.Locale;
645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport java.util.WeakHashMap;
655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/**
675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * A view that is responsible for detecting key presses and touch movements.
685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
69dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedEnabled
70dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedIcon
71dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextRatio
72dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextColor
73dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextShadowColor
74dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha
75dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator
76dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator
77dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator
78dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyHysteresisDistance
79dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdTime
80dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdDistance
81dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_slidingKeyInputEnable
82dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyRepeatStartTimeout
83dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyRepeatInterval
84dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout
85dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout
86dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout
87dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint
883623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping
893623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold
903623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration
913623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom
923623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo
933623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom
943623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo
953623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureSamplingMinimumDistance
963623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureRecognitionMinimumTime
973623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold
983623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */
100a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
101081fedc42269845a31e962b2ac1aa017b3a2efeeTadashi G. Takaoka        TouchScreenRegulator.ProcessMotionEvent {
102c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka    private static final String TAG = MainKeyboardView.class.getSimpleName();
1035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
104d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge    // TODO: Kill process when the usability study mode was changed.
105d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge    private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
106d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge
107bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /** Listener for {@link KeyboardActionListener}. */
108bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private KeyboardActionListener mKeyboardActionListener;
109bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka
110bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /* Space key and its icons */
1114112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Key mSpaceKey;
1124112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Drawable mSpaceIcon;
113bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw language name on spacebar.
11431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private final int mLanguageOnSpacebarFinalAlpha;
11531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
116bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mNeedsToDisplayLanguage;
117fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka    private boolean mHasMultipleEnabledIMEsOrSubtypes;
1184daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
1194112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final float mSpacebarTextRatio;
1204112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private float mSpacebarTextSize;
1214112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final int mSpacebarTextColor;
1224112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final int mSpacebarTextShadowColor;
123bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // The minimum x-scale to fit the language name on spacebar.
124bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f;
125bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw auto correction LED on spacebar.
126bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mAutoCorrectionSpacebarLedOn;
127bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final boolean mAutoCorrectionSpacebarLedEnabled;
128bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final Drawable mAutoCorrectionSpacebarLedIcon;
129bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private static final int SPACE_LED_LENGTH_PERCENT = 80;
13015d4793911fa305e0a58aced925961e948582979satok
13173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    // Stuff to draw altCodeWhileTyping keys.
13231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
13331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
1344daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
13573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
1362affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    // More keys keyboard
1379d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private PopupWindow mMoreKeysWindow;
1389d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private MoreKeysPanel mMoreKeysPanel;
1399d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private int mMoreKeysPanelPointerTrackerId;
1409d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private final WeakHashMap<Key, MoreKeysPanel> mMoreKeysPanelCache =
1419d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            new WeakHashMap<Key, MoreKeysPanel>();
1422affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
1435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
144081fedc42269845a31e962b2ac1aa017b3a2efeeTadashi G. Takaoka    private final TouchScreenRegulator mTouchScreenRegulator;
1455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
146bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    protected KeyDetector mKeyDetector;
14713ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka    private final boolean mHasDistinctMultitouch;
1485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private int mOldPointerCount = 1;
149e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mOldKey;
1505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
151160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    private final KeyTimerHandler mKeyTimerHandler;
1525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
153a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    private static final class KeyTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView>
1542321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka            implements TimerProxy {
15527e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        private static final int MSG_TYPING_STATE_EXPIRED = 0;
156f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_REPEAT_KEY = 1;
157f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_LONGPRESS_KEY = 2;
1580ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        private static final int MSG_DOUBLE_TAP = 3;
1595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1605509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mKeyRepeatStartTimeout;
1615509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mKeyRepeatInterval;
1625509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mLongPressKeyTimeout;
1635509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mLongPressShiftKeyTimeout;
1645509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mIgnoreAltCodeKeyTimeout;
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);
1805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        @Override
183dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void handleMessage(final Message msg) {
184c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            final MainKeyboardView keyboardView = getOuterInstance();
1855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            final PointerTracker tracker = (PointerTracker) msg.obj;
1865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            switch (msg.what) {
18727e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            case MSG_TYPING_STATE_EXPIRED:
18827e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka                startWhileTypingFadeinAnimation(keyboardView);
18927e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka                break;
1905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_REPEAT_KEY:
1918a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                final Key currentKey = tracker.getKey();
1928a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                if (currentKey != null && currentKey.mCode == msg.arg1) {
1938a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                    tracker.onRegisterKey(currentKey);
1945509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    startKeyRepeatTimer(tracker, mKeyRepeatInterval);
1958a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                }
1965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
1975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_LONGPRESS_KEY:
198a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                if (tracker != null) {
1992affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                    keyboardView.openMoreKeysKeyboardIfRequired(tracker.getKey(), tracker);
200a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                } else {
201a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    KeyboardSwitcher.getInstance().onLongPressTimeout(msg.arg1);
202a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                }
2035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
2045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
2055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
207dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        private void startKeyRepeatTimer(final PointerTracker tracker, final long delay) {
2088a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            final Key key = tracker.getKey();
20920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            if (key == null) {
21020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka                return;
21120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            }
2128a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
21332c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka        }
21432c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka
2152321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
216dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startKeyRepeatTimer(final PointerTracker tracker) {
2175509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            startKeyRepeatTimer(tracker, mKeyRepeatStartTimeout);
2185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void cancelKeyRepeatTimer() {
2215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_REPEAT_KEY);
2225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2240d9d37cec2b3c4b4c3747baeb529bd2a70cbafb8Tadashi G. Takaoka        // TODO: Suppress layout changes in key repeat mode
2255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public boolean isInKeyRepeat() {
2260d9d37cec2b3c4b4c3747baeb529bd2a70cbafb8Tadashi G. Takaoka            return hasMessages(MSG_REPEAT_KEY);
2275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2292321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
230dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startLongPressTimer(final int code) {
23198b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka            cancelLongPressTimer();
232a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            final int delay;
233a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            switch (code) {
234240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            case Constants.CODE_SHIFT:
2355509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                delay = mLongPressShiftKeyTimeout;
236a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                break;
237a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            default:
238a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                delay = 0;
239a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                break;
240a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
241a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            if (delay > 0) {
242a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, code, 0), delay);
243a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
244a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        }
245a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka
246a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        @Override
247dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startLongPressTimer(final PointerTracker tracker) {
248a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            cancelLongPressTimer();
2497b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            if (tracker == null) {
2507b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                return;
2517b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            }
2527b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            final Key key = tracker.getKey();
2537b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            final int delay;
2547b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            switch (key.mCode) {
255240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            case Constants.CODE_SHIFT:
2565509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                delay = mLongPressShiftKeyTimeout;
2577b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                break;
2587b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            default:
2597b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                if (KeyboardSwitcher.getInstance().isInMomentarySwitchState()) {
2607b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                    // We use longer timeout for sliding finger input started from the symbols
2617b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                    // mode key.
2625509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    delay = mLongPressKeyTimeout * 3;
2637b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                } else {
2645509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    delay = mLongPressKeyTimeout;
265a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                }
2667b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                break;
2677b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            }
2687b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            if (delay > 0) {
2697b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, tracker), delay);
270a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
2715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2732321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
27498b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer() {
2755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_LONGPRESS_KEY);
2765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
278d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
27945213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka                final ObjectAnimator animatorToStart) {
28045213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            float startFraction = 0.0f;
28145213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            if (animatorToCancel.isStarted()) {
282b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka                animatorToCancel.cancel();
28345213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka                startFraction = 1.0f - animatorToCancel.getAnimatedFraction();
284b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            }
28545213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            final long startTime = (long)(animatorToStart.getDuration() * startFraction);
28645213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            animatorToStart.start();
28745213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            animatorToStart.setCurrentPlayTime(startTime);
288b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka        }
289b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka
290d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void startWhileTypingFadeinAnimation(final MainKeyboardView keyboardView) {
291d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator,
292d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                    keyboardView.mAltCodeKeyWhileTypingFadeinAnimator);
293d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        }
294d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
295d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void startWhileTypingFadeoutAnimation(final MainKeyboardView keyboardView) {
296d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator,
297d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                    keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator);
298d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        }
299d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
3002321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
301dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startTypingStateTimer(final Key typedKey) {
302d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) {
303d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                return;
304d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            }
305d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
30673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            final boolean isTyping = isTypingState();
307f1678ba8024606349bc184cfeaead2be059f7b5bTadashi G. Takaoka            removeMessages(MSG_TYPING_STATE_EXPIRED);
308d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            final MainKeyboardView keyboardView = getOuterInstance();
309d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
310d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            // When user hits the space or the enter key, just cancel the while-typing timer.
311d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            final int typedCode = typedKey.mCode;
312240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) {
313d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                startWhileTypingFadeinAnimation(keyboardView);
314d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                return;
315d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            }
316d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
31773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            sendMessageDelayed(
3185509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    obtainMessage(MSG_TYPING_STATE_EXPIRED), mIgnoreAltCodeKeyTimeout);
31973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            if (isTyping) {
32073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                return;
32173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            }
322d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            startWhileTypingFadeoutAnimation(keyboardView);
32393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
32493246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
32593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        @Override
32673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState() {
32773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            return hasMessages(MSG_TYPING_STATE_EXPIRED);
32893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
32993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
3309f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka        @Override
3310ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void startDoubleTapTimer() {
3320ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP),
3335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                    ViewConfiguration.getDoubleTapTimeout());
3345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
3355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3360ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        @Override
337beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void cancelDoubleTapTimer() {
338beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            removeMessages(MSG_DOUBLE_TAP);
339beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        }
340beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka
341beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        @Override
3420ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public boolean isInDoubleTapTimeout() {
3430ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            return hasMessages(MSG_DOUBLE_TAP);
3445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
345c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
346c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        @Override
3470ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void cancelKeyTimers() {
3480ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelKeyRepeatTimer();
3490ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelLongPressTimer();
350c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
351c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
3520ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void cancelAllMessages() {
3530ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelKeyTimers();
354c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
355c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka    }
356c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
357dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs) {
358c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka        this(context, attrs, R.attr.mainKeyboardViewStyle);
3595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
361dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
3625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super(context, attrs, defStyle);
3635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
364081fedc42269845a31e962b2ac1aa017b3a2efeeTadashi G. Takaoka        mTouchScreenRegulator = new TouchScreenRegulator(context, this);
365c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
36613ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
36713ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final boolean forceNonDistinctMultitouch = prefs.getBoolean(
36813ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka                DebugSettings.FORCE_NON_DISTINCT_MULTITOUCH_KEY, false);
36913ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        final boolean hasDistinctMultitouch = context.getPackageManager()
3705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
37113ae76d7a342581160c172cd21706b3d57d32dadTadashi G. Takaoka        mHasDistinctMultitouch = hasDistinctMultitouch && !forceNonDistinctMultitouch;
372918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka        final Resources res = getResources();
373d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka        final boolean needsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
37429d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                ResourceUtils.getDeviceOverrideValue(res,
375d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                        R.array.phantom_sudden_move_event_device_list, "false"));
37693b5c2ce63705e7ebffd9bdb7358100e8d5b5235Tadashi G. Takaoka        PointerTracker.init(needsPhantomSuddenMoveEventHack);
37722b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
3784112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final TypedArray a = context.obtainStyledAttributes(
379c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
3804112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedEnabled = a.getBoolean(
381c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false);
3824112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedIcon = a.getDrawable(
383c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon);
384ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka        mSpacebarTextRatio = a.getFraction(
385ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                R.styleable.MainKeyboardView_spacebarTextRatio, 1, 1, 1.0f);
386c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka        mSpacebarTextColor = a.getColor(R.styleable.MainKeyboardView_spacebarTextColor, 0);
3874112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextShadowColor = a.getColor(
388c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_spacebarTextShadowColor, 0);
38931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFinalAlpha = a.getInt(
390c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha,
3914daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                Constants.Color.ALPHA_OPAQUE);
392d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        final int languageOnSpacebarFadeoutAnimatorResId = a.getResourceId(
393c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
39473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeoutAnimatorResId = a.getResourceId(
395c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
39673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeinAnimatorResId = a.getResourceId(
397c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
398160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
399160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        final float keyHysteresisDistance = a.getDimension(
400c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_keyHysteresisDistance, 0);
401f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        final float keyHysteresisDistanceForSlidingModifier = a.getDimension(
402f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0);
403f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mKeyDetector = new KeyDetector(
404f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka                keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
4055509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        mKeyTimerHandler = new KeyTimerHandler(this, a);
4062affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        mConfigShowMoreKeysKeyboardAtTouchedPoint = a.getBoolean(
407c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
4085509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        PointerTracker.setParameters(a);
4094112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        a.recycle();
410160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
41131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator(
41231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                languageOnSpacebarFadeoutAnimatorResId, this);
41331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator(
41431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeoutAnimatorResId, this);
41531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator(
41631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeinAnimatorResId, this);
41731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
41831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
419dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private ObjectAnimator loadObjectAnimator(final int resId, final Object target) {
42020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (resId == 0) {
42120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return null;
42220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
42331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
42431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                getContext(), resId);
425d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator != null) {
42631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            animator.setTarget(target);
42773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
42831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return animator;
42931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
43073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
43115f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
43231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getLanguageOnSpacebarAnimAlpha() {
43331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mLanguageOnSpacebarAnimAlpha;
434d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka    }
435d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka
43615f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
437dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setLanguageOnSpacebarAnimAlpha(final int alpha) {
43831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarAnimAlpha = alpha;
43931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        invalidateKey(mSpaceKey);
44031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
44131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
44215f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
44331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getAltCodeKeyWhileTypingAnimAlpha() {
44431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mAltCodeKeyWhileTypingAnimAlpha;
44531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
44631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
44715f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    @ExternallyReferenced
448dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) {
44931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingAnimAlpha = alpha;
45031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        updateAltCodeKeyWhileTyping();
4515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
453dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboardActionListener(final KeyboardActionListener listener) {
4545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyboardActionListener = listener;
4555c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        PointerTracker.setKeyboardActionListener(listener);
4565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
4595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns the {@link KeyboardActionListener} object.
4605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return the listener attached to this keyboard
4615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
4620efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
4630efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyboardActionListener getKeyboardActionListener() {
4645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mKeyboardActionListener;
4655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
467bb4be5444b845655c0eb80bcfbb66f93603802eaTadashi G. Takaoka    @Override
4680efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyDetector getKeyDetector() {
4690efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyDetector;
4700efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
4710efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
4720efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
473f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public DrawingProxy getDrawingProxy() {
474f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        return this;
475f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
476f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
477f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    @Override
4780efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public TimerProxy getTimerProxy() {
4790efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyTimerHandler;
4800efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
4810efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
4825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
4835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Attaches a keyboard to this view. The keyboard can be switched at any time and the
4845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * view will re-layout itself to accommodate the keyboard.
4855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see Keyboard
4865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see #getKeyboard()
4875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param keyboard the keyboard to display in this view
4885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
4895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
490dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboard(final Keyboard keyboard) {
4918a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        // Remove any pending messages, except dismissing preview and key repeat.
4928a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        mKeyTimerHandler.cancelLongPressTimer();
4935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.setKeyboard(keyboard);
4945a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyDetector.setKeyboard(
4955a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka                keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
4968335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setKeyDetector(mKeyDetector);
497081fedc42269845a31e962b2ac1aa017b3a2efeeTadashi G. Takaoka        mTouchScreenRegulator.setKeyboardGeometry(keyboard.mOccupiedWidth);
4989d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanelCache.clear();
4994112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
500240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        mSpaceKey = keyboard.getKey(Constants.CODE_SPACE);
5011f2d0aa6c9b343848ee51e5bc13ccaaadf3ba4feTadashi G. Takaoka        mSpaceIcon = (mSpaceKey != null)
5024daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null;
5034112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
5044112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
50548a7681e064ae259b840f0e757da2d716043d893Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
506c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            ResearchLogger.mainKeyboardView_setKeyboard(keyboard);
50748a7681e064ae259b840f0e757da2d716043d893Kurt Partridge        }
508f147794fd41491a3383e6aca6d49007f58124068alanv
509f147794fd41491a3383e6aca6d49007f58124068alanv        // This always needs to be set since the accessibility state can
510f147794fd41491a3383e6aca6d49007f58124068alanv        // potentially change without the keyboard being set again.
511f147794fd41491a3383e6aca6d49007f58124068alanv        AccessibleKeyboardViewProxy.getInstance().setKeyboard(keyboard);
5125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5148335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
515dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
5168335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable);
5178335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
5188335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
519dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
5208335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser);
5210657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    }
5220657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka
5235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
5244331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    protected void onAttachedToWindow() {
5254331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        super.onAttachedToWindow();
5264331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        // Notify the research logger that the keyboard view has been attached.  This is needed
5274331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        // to properly show the splash screen, which requires that the window token of the
5284331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        // KeyboardView be non-null.
5294331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
530c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka            ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(this);
531c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        }
532c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    }
533c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka
534c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    @Override
535c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    protected void onDetachedFromWindow() {
536c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        super.onDetachedFromWindow();
537c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        // Notify the research logger that the keyboard view has been detached.  This is needed
538c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        // to invalidate the reference of {@link MainKeyboardView} to null.
539c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        if (ProductionFlag.IS_EXPERIMENTAL) {
540c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka            ResearchLogger.getInstance().mainKeyboardView_onDetachedFromWindow();
5414331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        }
5424331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    }
5434331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge
5444331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    @Override
5455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void cancelAllMessages() {
546f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        mKeyTimerHandler.cancelAllMessages();
5475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.cancelAllMessages();
5485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
550dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private boolean openMoreKeysKeyboardIfRequired(final Key parentKey,
551dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final PointerTracker tracker) {
5525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Check if we have a popup layout specified first.
5539d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysLayout == 0) {
5545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return false;
5555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
5565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
55763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        // Check if we are already displaying popup panel.
55820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (mMoreKeysPanel != null) {
55963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return false;
56020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
56120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (parentKey == null) {
5625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return false;
56320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
56463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        return onLongPress(parentKey, tracker);
5655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5679d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    // This default implementation returns a more keys panel.
568dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    protected MoreKeysPanel onCreateMoreKeysPanel(final Key parentKey) {
56920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (parentKey.mMoreKeys == null) {
5705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return null;
57120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
5725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5739d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        final View container = LayoutInflater.from(getContext()).inflate(mMoreKeysLayout, null);
57420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (container == null) {
5755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            throw new NullPointerException();
57620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
5775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5782affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        final MoreKeysKeyboardView moreKeysKeyboardView =
5792affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view);
5807ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final Keyboard moreKeysKeyboard = new MoreKeysKeyboard.Builder(container, parentKey, this)
5817ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka                .build();
5822affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
583b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka        container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
5845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5852affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        return moreKeysKeyboardView;
5865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
5892affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka     * Called when a key is long pressed. By default this will open more keys keyboard associated
5905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * with this key.
5915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param parentKey the key that was long pressed
5925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param tracker the pointer tracker which pressed the parent key
5935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return true if the long press is handled, false otherwise. Subclasses should call the
5945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * method on the base class if the subclass doesn't wish to handle the call.
5955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
596dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    protected boolean onLongPress(final Key parentKey, final PointerTracker tracker) {
5979bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
598c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            ResearchLogger.mainKeyboardView_onLongPress();
5999bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
6006dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        final int primaryCode = parentKey.mCode;
601e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka        if (parentKey.hasEmbeddedMoreKey()) {
602ed3bac91f242850c6d1833a5f8981b9cc208c5ddTadashi G. Takaoka            final int embeddedCode = parentKey.mMoreKeys[0].mCode;
6033708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            tracker.onLongPressed();
604e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka            invokeCodeInput(embeddedCode);
6053708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            invokeReleaseKey(primaryCode);
606a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            KeyboardSwitcher.getInstance().hapticAndAudioFeedback(primaryCode);
6073708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            return true;
6083708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        }
609240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (primaryCode == Constants.CODE_SPACE || primaryCode == Constants.CODE_LANGUAGE_SWITCH) {
6103708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            // Long pressing the space key invokes IME switcher dialog.
6113708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) {
6126dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tracker.onLongPressed();
61342e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka                invokeReleaseKey(primaryCode);
61442e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka                return true;
6156dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
6166dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
61717dc10724bf0db04d0a4bfb2b8be0739ad9e60c6Tadashi G. Takaoka        return openMoreKeysPanel(parentKey, tracker);
6186dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
6196dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
620dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private boolean invokeCustomRequest(final int code) {
6212a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        return mKeyboardActionListener.onCustomRequest(code);
62242e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    }
62342e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka
624dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private void invokeCodeInput(final int primaryCode) {
625ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka        mKeyboardActionListener.onCodeInput(
626ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka                primaryCode, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
62742e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    }
62842e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka
629dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private void invokeReleaseKey(final int primaryCode) {
6302a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        mKeyboardActionListener.onReleaseKey(primaryCode, false);
6316dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
6326dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
633dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private boolean openMoreKeysPanel(final Key parentKey, final PointerTracker tracker) {
6349d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        MoreKeysPanel moreKeysPanel = mMoreKeysPanelCache.get(parentKey);
6359d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (moreKeysPanel == null) {
6369d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            moreKeysPanel = onCreateMoreKeysPanel(parentKey);
63720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            if (moreKeysPanel == null) {
6385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                return false;
63920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            }
6409d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysPanelCache.put(parentKey, moreKeysPanel);
6415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6429d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysWindow == null) {
6439d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysWindow = new PopupWindow(getContext());
6449d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysWindow.setBackgroundDrawable(null);
6452affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka            mMoreKeysWindow.setAnimationStyle(R.style.MoreKeysKeyboardAnimation);
6465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6479d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanel = moreKeysPanel;
6489d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanelPointerTrackerId = tracker.mPointerId;
64963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka
6507ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final boolean keyPreviewEnabled = isKeyPreviewPopupEnabled() && !parentKey.noKeyPreview();
6517ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually horizontally aligned with the center of the parent key.
6527ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // If showMoreKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more
6537ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // keys keyboard is placed at the touch point of the parent key.
6547ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled)
6557ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka                ? tracker.getLastX()
656f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka                : parentKey.mX + parentKey.mWidth / 2;
6577ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually vertically aligned with the top edge of the parent key
6587ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically
6597ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // aligned with the bottom edge of the visible part of the key preview.
66029d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@code mPreviewVisibleOffset} has been set appropriately in
66129d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@link KeyboardView#showKeyPreview(PointerTracker)}.
66229d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        final int pointY = parentKey.mY + mKeyPreviewDrawParams.mPreviewVisibleOffset;
6639d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        moreKeysPanel.showMoreKeysPanel(
6642a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                this, this, pointX, pointY, mMoreKeysWindow, mKeyboardActionListener);
6659d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        final int translatedX = moreKeysPanel.translateX(tracker.getLastX());
6669d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        final int translatedY = moreKeysPanel.translateY(tracker.getLastY());
667e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaoka        tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel);
6681b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka        dimEntireKeyboard(true);
6695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
6705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
6739d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysPanel != null) {
67463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return true;
6755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
67620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        return PointerTracker.isAnyInSlidingKeyInput();
6775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public int getPointerCount() {
6805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mOldPointerCount;
6815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
684dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    public boolean dispatchTouchEvent(MotionEvent event) {
685dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
686dc2ee7772402633817702e95c2a5b17f6dec03ebalanv            return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event);
687dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        }
688dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        return super.dispatchTouchEvent(event);
689dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    }
690dc2ee7772402633817702e95c2a5b17f6dec03ebalanv
691dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    @Override
692dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean onTouchEvent(final MotionEvent me) {
69346286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        if (getKeyboard() == null) {
69446286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka            return false;
69546286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        }
696c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        return mTouchScreenRegulator.onTouchEvent(me);
697c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    }
698c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
699c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    @Override
700dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean processMotionEvent(final MotionEvent me) {
701f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        final boolean nonDistinctMultitouch = !mHasDistinctMultitouch;
7025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int action = me.getActionMasked();
7035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int pointerCount = me.getPointerCount();
7045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int oldPointerCount = mOldPointerCount;
7055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mOldPointerCount = pointerCount;
7065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
7085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // If the device does not have distinct multi-touch support panel, ignore all multi-touch
7095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // events except a transition from/to single-touch.
710f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
7115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
7125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final long eventTime = me.getEventTime();
7155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int index = me.getActionIndex();
7165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int id = me.getPointerId(index);
71763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        final int x, y;
7189d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysPanel != null && id == mMoreKeysPanelPointerTrackerId) {
7199d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            x = mMoreKeysPanel.translateX((int)me.getX(index));
7209d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            y = mMoreKeysPanel.translateY((int)me.getY(index));
72163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        } else {
72263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            x = (int)me.getX(index);
72363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            y = (int)me.getY(index);
7245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
725fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        // TODO: This might be moved to the tracker.processMotionEvent() call below.
726fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        if (ENABLE_USABILITY_STUDY_LOG && action != MotionEvent.ACTION_MOVE) {
727fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            writeUsabilityStudyLog(me, action, eventTime, index, id, x, y);
728d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge        }
729fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        // TODO: This should be moved to the tracker.processMotionEvent() call below.
730fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        // Currently the same "move" event is being logged twice.
731c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa        if (ProductionFlag.IS_EXPERIMENTAL) {
732fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            ResearchLogger.mainKeyboardView_processMotionEvent(
733fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    me, action, eventTime, index, id, x, y);
73415d4793911fa305e0a58aced925961e948582979satok        }
7355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
736f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (mKeyTimerHandler.isInKeyRepeat()) {
737e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
7385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Key repeating timer will be canceled if 2 or more keys are in action, and current
7395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // event (UP or DOWN) is non-modifier key.
7405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount > 1 && !tracker.isModifier()) {
741f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka                mKeyTimerHandler.cancelKeyRepeatTimer();
7425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
7435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Up event will pass through.
7445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
7475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Translate mutli-touch event to single-touch events on the device that has no distinct
7485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // multi-touch panel.
749f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch) {
7505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Use only main (id=0) pointer tracker.
751e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
7525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount == 1 && oldPointerCount == 2) {
7535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Multi-touch to single touch transition.
7545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send a down event for the latest pointer if the key is different from the
7555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // previous key.
756e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                final Key newKey = tracker.getKeyOn(x, y);
757e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                if (mOldKey != newKey) {
7580efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka                    tracker.onDownEvent(x, y, eventTime, this);
759fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    if (action == MotionEvent.ACTION_UP) {
760906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka                        tracker.onUpEvent(x, y, eventTime);
761fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    }
7625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                }
7635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 2 && oldPointerCount == 1) {
7645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Single-touch to multi-touch transition.
7655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send an up event for the last pointer.
7665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                final int lastX = tracker.getLastX();
7675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                final int lastY = tracker.getLastY();
768e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                mOldKey = tracker.getKeyOn(lastX, lastY);
769906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka                tracker.onUpEvent(lastX, lastY, eventTime);
7705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 1 && oldPointerCount == 1) {
7718ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka                tracker.processMotionEvent(action, x, y, eventTime, this);
7725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else {
7735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
7745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                        + " (old " + oldPointerCount + ")");
7755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
7765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
7775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (action == MotionEvent.ACTION_MOVE) {
7805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            for (int i = 0; i < pointerCount; i++) {
781d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                final int pointerId = me.getPointerId(i);
782e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka                final PointerTracker tracker = PointerTracker.getPointerTracker(
783d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                        pointerId, this);
78463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                final int px, py;
7853314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka                final MotionEvent motionEvent;
7869d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                if (mMoreKeysPanel != null
7879d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                        && tracker.mPointerId == mMoreKeysPanelPointerTrackerId) {
7889d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                    px = mMoreKeysPanel.translateX((int)me.getX(i));
7899d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                    py = mMoreKeysPanel.translateY((int)me.getY(i));
7903314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka                    motionEvent = null;
79163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                } else {
79263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                    px = (int)me.getX(i);
79363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                    py = (int)me.getY(i);
7943314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka                    motionEvent = me;
79563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                }
7963314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka                tracker.onMoveEvent(px, py, eventTime, motionEvent);
797d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                if (ENABLE_USABILITY_STUDY_LOG) {
798fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    writeUsabilityStudyLog(me, action, eventTime, i, pointerId, px, py);
799d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                }
800c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa                if (ProductionFlag.IS_EXPERIMENTAL) {
801fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                    ResearchLogger.mainKeyboardView_processMotionEvent(
802fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                            me, action, eventTime, i, pointerId, px, py);
80315d4793911fa305e0a58aced925961e948582979satok                }
8045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
8055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        } else {
806e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
807e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            tracker.processMotionEvent(action, x, y, eventTime, this);
8085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
8095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
8105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
8115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
8125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
813fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka    private static void writeUsabilityStudyLog(final MotionEvent me, final int action,
814fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            final long eventTime, final int index, final int id, final int x, final int y) {
815fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        final String eventTag;
816fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        switch (action) {
817fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_UP:
818fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[Up]";
819fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
820fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_DOWN:
821fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[Down]";
822fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
823fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_UP:
824fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[PointerUp]";
825fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
826fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_POINTER_DOWN:
827fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[PointerDown]";
828fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
829fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        case MotionEvent.ACTION_MOVE:
830fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[Move]";
831fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
832fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        default:
833fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            eventTag = "[Action" + action + "]";
834fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka            break;
835fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        }
836fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        final float size = me.getSize(index);
837fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        final float pressure = me.getPressure(index);
838fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka        UsabilityStudyLogUtils.getInstance().write(
839fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka                eventTag + eventTime + "," + id + "," + x + "," + y + "," + size + "," + pressure);
840fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka    }
841fca71cfddb7a5062e7a4fb512e1faab401c65cd3Tadashi G. Takaoka
8425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
8435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void closing() {
8445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.closing();
8459d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        dismissMoreKeysPanel();
8469d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanelCache.clear();
8475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
8485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
8499ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    @Override
8509d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public boolean dismissMoreKeysPanel() {
85120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (mMoreKeysWindow == null || !mMoreKeysWindow.isShowing()) {
85220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return false;
8535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
85420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        mMoreKeysWindow.dismiss();
85520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        mMoreKeysPanel = null;
85620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        mMoreKeysPanelPointerTrackerId = -1;
85720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        dimEntireKeyboard(false);
85820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        return true;
8595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
8605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
861586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette    /**
862c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka     * Receives hover events from the input framework.
863586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *
864586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @param event The motion event to be dispatched.
865586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @return {@code true} if the event was handled by the view, {@code false}
866586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *         otherwise
867586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     */
868c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka    @Override
869dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean dispatchHoverEvent(final MotionEvent event) {
8705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
871e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
872586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette            return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
8735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
8745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
875586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette        // Reflection doesn't support calling superclass methods.
8765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return false;
8775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
87822b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
879dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateShortcutKey(final boolean available) {
8807bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
88120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (keyboard == null) {
88220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
88320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
884240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        final Key shortcutKey = keyboard.getKey(Constants.CODE_SHORTCUT);
88520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (shortcutKey == null) {
88620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
88720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
8887bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        shortcutKey.setEnabled(available);
8897bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        invalidateKey(shortcutKey);
8905afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka    }
8915afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka
89273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    private void updateAltCodeKeyWhileTyping() {
89373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
89420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (keyboard == null) {
89520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
89620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
89773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        for (final Key key : keyboard.mAltCodeKeysWhileTyping) {
89873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            invalidateKey(key);
89973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
90073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    }
90173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
902dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged,
903dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final boolean needsToDisplayLanguage, final boolean hasMultipleEnabledIMEsOrSubtypes) {
9044112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mNeedsToDisplayLanguage = needsToDisplayLanguage;
905fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes;
906fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator;
907d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator == null) {
908aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            mNeedsToDisplayLanguage = false;
9094c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        } else {
910dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            if (subtypeChanged && needsToDisplayLanguage) {
9114daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE);
91231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (animator.isStarted()) {
91331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    animator.cancel();
91431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
915d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka                animator.start();
916dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            } else {
91731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (!animator.isStarted()) {
91831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    mLanguageOnSpacebarAnimAlpha = mLanguageOnSpacebarFinalAlpha;
91931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
920dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            }
9214c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        }
9224112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
9234112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
9244112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
925dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateAutoCorrectionState(final boolean isAutoCorrection) {
92620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (!mAutoCorrectionSpacebarLedEnabled) {
92720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return;
92820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
9294112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedOn = isAutoCorrection;
9304112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
93122b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
93222b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
93322b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    @Override
934dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
935dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final KeyDrawParams params) {
9366bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.altCodeWhileTyping() && key.isEnabled()) {
93773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
93873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
939240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        if (key.mCode == Constants.CODE_SPACE) {
940bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawSpacebar(key, canvas, paint);
9414112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Whether space key needs to show the "..." popup hint for special purposes
942fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) {
943bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka                drawKeyPopupHint(key, canvas, paint, params);
9444112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            }
945240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka        } else if (key.mCode == Constants.CODE_LANGUAGE_SWITCH) {
94681d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
947fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            drawKeyPopupHint(key, canvas, paint, params);
948b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        } else {
949b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
95022b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka        }
95122b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
9524112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
953dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) {
954bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(1.0f);
955bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        final float textWidth = getLabelWidth(text, paint);
95620dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (textWidth < width) {
95720dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return true;
95820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
959bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
960bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        final float scaleX = width / textWidth;
96120dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) {
96220dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return false;
96320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        }
964bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
965bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(scaleX);
966bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        return getLabelWidth(text, paint) < width;
967bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    }
968bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
969bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // Layout language name on spacebar.
970dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private String layoutLanguageOnSpacebar(final Paint paint, final InputMethodSubtype subtype,
971bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final int width) {
972bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        // Choose appropriate language name to fit into the width.
97320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        final String fullText = getFullDisplayName(subtype, getResources());
97420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, fullText, paint)) {
97520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return fullText;
976bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        }
977bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
97820dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        final String middleText = getMiddleDisplayName(subtype);
97920dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, middleText, paint)) {
98020dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return middleText;
9814112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9824112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
98320dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        final String shortText = getShortDisplayName(subtype);
98420dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka        if (fitsTextIntoWidth(width, shortText, paint)) {
98520dd1bc090abdd3e88855fe54b1865949aa1168dTadashi G. Takaoka            return shortText;
9864112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9874112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
988bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        return "";
9894112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
9904112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
991dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private void drawSpacebar(final Key key, final Canvas canvas, final Paint paint) {
992bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        final int width = key.mWidth;
993b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        final int height = key.mHeight;
9944112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
995f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaoka        // If input language are explicitly selected.
996bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mNeedsToDisplayLanguage) {
997bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTextAlign(Align.CENTER);
998bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTypeface(Typeface.DEFAULT);
999bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTextSize(mSpacebarTextSize);
1000bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final InputMethodSubtype subtype = getKeyboard().mId.mSubtype;
1001bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final String language = layoutLanguageOnSpacebar(paint, subtype, width);
10024112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Draw language text with shadow
10034112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float descent = paint.descent();
10044112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float textHeight = -paint.ascent() + descent;
1005b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            final float baseline = height / 2 + textHeight / 2;
1006aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            paint.setColor(mSpacebarTextShadowColor);
100731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
10084112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent - 1, paint);
1009aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            paint.setColor(mSpacebarTextColor);
101031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
10114112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent, paint);
10124112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
10134112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
10144112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        // Draw the spacebar icon at the bottom
1015bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mAutoCorrectionSpacebarLedOn) {
10164112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100;
10174112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight();
10184112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
10194112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
1020bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight);
10214112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        } else if (mSpaceIcon != null) {
10224112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = mSpaceIcon.getIntrinsicWidth();
10234112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mSpaceIcon.getIntrinsicHeight();
10244112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
10254112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
1026bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mSpaceIcon, x, y, iconWidth, iconHeight);
10274112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
10284112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
102927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
103027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // InputMethodSubtype's display name for spacebar text in its locale.
103127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //        isAdditionalSubtype (T=true, F=false)
103276d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    // locale layout  | Short  Middle      Full
103376d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    // ------ ------- - ---- --------- ----------------------
103476d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  en_US qwerty  F  En  English   English (US)           exception
103576d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  en_GB qwerty  F  En  English   English (UK)           exception
103676d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  es_US spanish F  Es  Español   Español (EE.UU.)       exception
103776d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  fr    azerty  F  Fr  Français  Français
103876d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  fr_CA qwerty  F  Fr  Français  Français (Canada)
103976d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  de    qwertz  F  De  Deutsch   Deutsch
104076d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  zz    qwerty  F      QWERTY    QWERTY
104176d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  fr    qwertz  T  Fr  Français  Français (QWERTZ)
104276d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  de    qwerty  T  De  Deutsch   Deutsch (QWERTY)
104376d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  en_US azerty  T  En  English   English (US) (AZERTY)
104476d4ffeebfd084913a8c1b7433dff48f5b2063dfTadashi G. Takaoka    //  zz    azerty  T      AZERTY    AZERTY
104527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
104627b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's full display name in its locale.
1047dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    static String getFullDisplayName(final InputMethodSubtype subtype, final Resources res) {
104827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
104927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
105027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
105127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
105227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        return SubtypeLocale.getSubtypeDisplayName(subtype, res);
105327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
105427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
105527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's short display name in its locale.
1056dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    static String getShortDisplayName(final InputMethodSubtype subtype) {
105727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
105827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return "";
105927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
106027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        final Locale locale = SubtypeLocale.getSubtypeLocale(subtype);
106127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        return StringUtils.toTitleCase(locale.getLanguage(), locale);
106227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
106327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
106427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's middle display name in its locale.
1065dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    static String getMiddleDisplayName(final InputMethodSubtype subtype) {
106627b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
106727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
106827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
106927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        final Locale locale = SubtypeLocale.getSubtypeLocale(subtype);
107027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        return StringUtils.toTitleCase(locale.getDisplayLanguage(locale), locale);
107127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
10725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka}
1073