15f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/*
25f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Copyright (C) 2011 The Android Open Source Project
35f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
45f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
55f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * you may not use this file except in compliance with the License.
65f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * You may obtain a copy of the License at
75f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
85f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
95f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * See the License for the specific language governing permissions and
145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * limitations under the License.
155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */
165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokapackage com.android.inputmethod.keyboard;
185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
19d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaokaimport android.animation.AnimatorInflater;
2031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaokaimport android.animation.ObjectAnimator;
215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.Context;
225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.pm.PackageManager;
2327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaokaimport android.content.res.Resources;
244112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.content.res.TypedArray;
256dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport android.graphics.Canvas;
2622b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaokaimport android.graphics.Paint;
274112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.Paint.Align;
28bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaokaimport android.graphics.Typeface;
294112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.drawable.Drawable;
305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.os.Message;
3115d4793911fa305e0a58aced925961e948582979satokimport android.text.TextUtils;
325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.AttributeSet;
335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.Log;
345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.LayoutInflater;
355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.MotionEvent;
365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.View;
375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.ViewConfiguration;
38b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaokaimport android.view.ViewGroup;
39f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaokaimport android.view.inputmethod.InputMethodSubtype;
405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.widget.PopupWindow;
415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils;
435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
44f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
452321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
4649d8af8a4e900f3c68c333aba7fde0a11fd368b1Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyDrawParams;
4799906b3fc2dcb447aafdd43dda0c4551513b293eTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.SuddenJumpingTouchEventHandler;
484daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyangimport com.android.inputmethod.latin.Constants;
496dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinIME;
5015d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.LatinImeLogger;
515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
529879f65651a748e4c0a45715eb7d5663652f1127Tadashi G. Takaokaimport com.android.inputmethod.latin.ResourceUtils;
535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.StaticInnerHandlerWrapper;
5427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaokaimport com.android.inputmethod.latin.StringUtils;
553bf57a5624679a20db26df912077a53b9f90ad36Tadashi G. Takaokaimport com.android.inputmethod.latin.SubtypeLocale;
5615d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils;
57c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasaimport com.android.inputmethod.latin.define.ProductionFlag;
586b966160ac8570271547bf63217efa5e228d4accKurt Partridgeimport com.android.inputmethod.research.ResearchLogger;
595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaokaimport java.util.Locale;
615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport java.util.WeakHashMap;
625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/**
645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * A view that is responsible for detecting key presses and touch movements.
655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka *
66dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedEnabled
67dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedIcon
68dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextRatio
69dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextColor
70dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_spacebarTextShadowColor
71dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha
72dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator
73dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator
74dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator
75dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyHysteresisDistance
76dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdTime
77dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdDistance
78dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_slidingKeyInputEnable
79dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyRepeatStartTimeout
80dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_keyRepeatInterval
81dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout
82dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout
83dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout
84dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint
853623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping
863623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold
873623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration
883623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom
893623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo
903623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom
913623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo
923623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureSamplingMinimumDistance
933623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureRecognitionMinimumTime
943623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold
953623b9767b3d5f122f574d4c4d14aa79ed305752Tadashi G. Takaoka * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */
97a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
98c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        SuddenJumpingTouchEventHandler.ProcessMotionEvent {
99c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka    private static final String TAG = MainKeyboardView.class.getSimpleName();
1005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
101d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge    // TODO: Kill process when the usability study mode was changed.
102d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge    private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
103d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge
104bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /** Listener for {@link KeyboardActionListener}. */
105bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private KeyboardActionListener mKeyboardActionListener;
106bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka
107bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    /* Space key and its icons */
1084112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Key mSpaceKey;
1094112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private Drawable mSpaceIcon;
110bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw language name on spacebar.
11131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private final int mLanguageOnSpacebarFinalAlpha;
11231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
113bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mNeedsToDisplayLanguage;
114fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka    private boolean mHasMultipleEnabledIMEsOrSubtypes;
1154daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
1164112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final float mSpacebarTextRatio;
1174112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private float mSpacebarTextSize;
1184112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final int mSpacebarTextColor;
1194112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    private final int mSpacebarTextShadowColor;
120bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // The minimum x-scale to fit the language name on spacebar.
121bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f;
122bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    // Stuff to draw auto correction LED on spacebar.
123bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private boolean mAutoCorrectionSpacebarLedOn;
124bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final boolean mAutoCorrectionSpacebarLedEnabled;
125bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final Drawable mAutoCorrectionSpacebarLedIcon;
126bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private static final int SPACE_LED_LENGTH_PERCENT = 80;
12715d4793911fa305e0a58aced925961e948582979satok
12873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    // Stuff to draw altCodeWhileTyping keys.
12931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
13031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
1314daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang    private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
13273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
1332affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    // More keys keyboard
1349d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private PopupWindow mMoreKeysWindow;
1359d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private MoreKeysPanel mMoreKeysPanel;
1369d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private int mMoreKeysPanelPointerTrackerId;
1379d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    private final WeakHashMap<Key, MoreKeysPanel> mMoreKeysPanelCache =
1389d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            new WeakHashMap<Key, MoreKeysPanel>();
1392affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka    private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
1405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
141bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    private final SuddenJumpingTouchEventHandler mTouchScreenRegulator;
1425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
143bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka    protected KeyDetector mKeyDetector;
14406b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka    private boolean mHasDistinctMultitouch;
1455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    private int mOldPointerCount = 1;
146e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka    private Key mOldKey;
1475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
148160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka    private final KeyTimerHandler mKeyTimerHandler;
1495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
150a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka    private static final class KeyTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView>
1512321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka            implements TimerProxy {
15227e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka        private static final int MSG_TYPING_STATE_EXPIRED = 0;
153f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_REPEAT_KEY = 1;
154f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        private static final int MSG_LONGPRESS_KEY = 2;
1550ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        private static final int MSG_DOUBLE_TAP = 3;
1565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1575509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mKeyRepeatStartTimeout;
1585509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mKeyRepeatInterval;
1595509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mLongPressKeyTimeout;
1605509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mLongPressShiftKeyTimeout;
1615509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        private final int mIgnoreAltCodeKeyTimeout;
1625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1635509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        public KeyTimerHandler(final MainKeyboardView outerInstance,
1645509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                final TypedArray mainKeyboardViewAttr) {
1655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            super(outerInstance);
1665509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka
1675509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mKeyRepeatStartTimeout = mainKeyboardViewAttr.getInt(
1685509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0);
1695509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mKeyRepeatInterval = mainKeyboardViewAttr.getInt(
1705509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_keyRepeatInterval, 0);
1715509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mLongPressKeyTimeout = mainKeyboardViewAttr.getInt(
1725509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_longPressKeyTimeout, 0);
1735509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mLongPressShiftKeyTimeout = mainKeyboardViewAttr.getInt(
1745509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_longPressShiftKeyTimeout, 0);
1755509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
1765509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
1775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
1785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
1795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        @Override
180dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void handleMessage(final Message msg) {
181c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            final MainKeyboardView keyboardView = getOuterInstance();
1825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            final PointerTracker tracker = (PointerTracker) msg.obj;
1835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            switch (msg.what) {
18427e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka            case MSG_TYPING_STATE_EXPIRED:
18527e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka                startWhileTypingFadeinAnimation(keyboardView);
18627e48447a449d2eb534dfa2dc07060727e1a8fb0Tadashi G. Takaoka                break;
1875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_REPEAT_KEY:
1888a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                final Key currentKey = tracker.getKey();
1898a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                if (currentKey != null && currentKey.mCode == msg.arg1) {
1908a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                    tracker.onRegisterKey(currentKey);
1915509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    startKeyRepeatTimer(tracker, mKeyRepeatInterval);
1928a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka                }
1935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
1945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            case MSG_LONGPRESS_KEY:
195a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                if (tracker != null) {
1962affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                    keyboardView.openMoreKeysKeyboardIfRequired(tracker.getKey(), tracker);
197a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                } else {
198a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                    KeyboardSwitcher.getInstance().onLongPressTimeout(msg.arg1);
199a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                }
2005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                break;
2015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
2025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
204dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        private void startKeyRepeatTimer(final PointerTracker tracker, final long delay) {
2058a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            final Key key = tracker.getKey();
2068a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            if (key == null) return;
2078a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
20832c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka        }
20932c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka
2102321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
211dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startKeyRepeatTimer(final PointerTracker tracker) {
2125509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka            startKeyRepeatTimer(tracker, mKeyRepeatStartTimeout);
2135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public void cancelKeyRepeatTimer() {
2165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_REPEAT_KEY);
2175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2190d9d37cec2b3c4b4c3747baeb529bd2a70cbafb8Tadashi G. Takaoka        // TODO: Suppress layout changes in key repeat mode
2205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        public boolean isInKeyRepeat() {
2210d9d37cec2b3c4b4c3747baeb529bd2a70cbafb8Tadashi G. Takaoka            return hasMessages(MSG_REPEAT_KEY);
2225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2242321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
225dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startLongPressTimer(final int code) {
22698b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka            cancelLongPressTimer();
227a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            final int delay;
228a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            switch (code) {
229a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            case Keyboard.CODE_SHIFT:
2305509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                delay = mLongPressShiftKeyTimeout;
231a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                break;
232a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            default:
233a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                delay = 0;
234a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                break;
235a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
236a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            if (delay > 0) {
237a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, code, 0), delay);
238a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
239a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        }
240a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka
241a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka        @Override
242dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startLongPressTimer(final PointerTracker tracker) {
243a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            cancelLongPressTimer();
2447b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            if (tracker == null) {
2457b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                return;
2467b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            }
2477b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            final Key key = tracker.getKey();
2487b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            final int delay;
2497b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            switch (key.mCode) {
2507b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            case Keyboard.CODE_SHIFT:
2515509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                delay = mLongPressShiftKeyTimeout;
2527b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                break;
2537b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            default:
2547b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                if (KeyboardSwitcher.getInstance().isInMomentarySwitchState()) {
2557b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                    // We use longer timeout for sliding finger input started from the symbols
2567b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                    // mode key.
2575509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    delay = mLongPressKeyTimeout * 3;
2587b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                } else {
2595509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    delay = mLongPressKeyTimeout;
260a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka                }
2617b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                break;
2627b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            }
2637b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka            if (delay > 0) {
2647b6afb1287fb6d5edfebed7403eb31ed23a8348dTadashi G. Takaoka                sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, tracker), delay);
265a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            }
2665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
2682321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
26998b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka        public void cancelLongPressTimer() {
2705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            removeMessages(MSG_LONGPRESS_KEY);
2715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
2725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
273d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
27445213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka                final ObjectAnimator animatorToStart) {
27545213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            float startFraction = 0.0f;
27645213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            if (animatorToCancel.isStarted()) {
277b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka                animatorToCancel.cancel();
27845213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka                startFraction = 1.0f - animatorToCancel.getAnimatedFraction();
279b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka            }
28045213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            final long startTime = (long)(animatorToStart.getDuration() * startFraction);
28145213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            animatorToStart.start();
28245213ed2a6e9a940ec540ff43ded7e877cb20dc9Tadashi G. Takaoka            animatorToStart.setCurrentPlayTime(startTime);
283b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka        }
284b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka
285d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void startWhileTypingFadeinAnimation(final MainKeyboardView keyboardView) {
286d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator,
287d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                    keyboardView.mAltCodeKeyWhileTypingFadeinAnimator);
288d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        }
289d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
290d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        private static void startWhileTypingFadeoutAnimation(final MainKeyboardView keyboardView) {
291d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator,
292d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                    keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator);
293d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka        }
294d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
2952321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka        @Override
296dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka        public void startTypingStateTimer(final Key typedKey) {
297d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) {
298d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                return;
299d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            }
300d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
30173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            final boolean isTyping = isTypingState();
302f1678ba8024606349bc184cfeaead2be059f7b5bTadashi G. Takaoka            removeMessages(MSG_TYPING_STATE_EXPIRED);
303d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            final MainKeyboardView keyboardView = getOuterInstance();
304d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
305d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            // When user hits the space or the enter key, just cancel the while-typing timer.
306d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            final int typedCode = typedKey.mCode;
307d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            if (typedCode == Keyboard.CODE_SPACE || typedCode == Keyboard.CODE_ENTER) {
308d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                startWhileTypingFadeinAnimation(keyboardView);
309d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka                return;
310d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            }
311d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka
31273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            sendMessageDelayed(
3135509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka                    obtainMessage(MSG_TYPING_STATE_EXPIRED), mIgnoreAltCodeKeyTimeout);
31473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            if (isTyping) {
31573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka                return;
31673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            }
317d2173b5737bf791a65f6b1e2980f26ebd94369c5Tadashi G. Takaoka            startWhileTypingFadeoutAnimation(keyboardView);
31893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
31993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
32093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        @Override
32173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        public boolean isTypingState() {
32273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            return hasMessages(MSG_TYPING_STATE_EXPIRED);
32393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka        }
32493246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka
3259f09c6fbc81ed40a41c0a23bd5913214ec7f6a9bTadashi G. Takaoka        @Override
3260ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void startDoubleTapTimer() {
3270ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP),
3285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                    ViewConfiguration.getDoubleTapTimeout());
3295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
3305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
3310ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        @Override
332beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        public void cancelDoubleTapTimer() {
333beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka            removeMessages(MSG_DOUBLE_TAP);
334beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        }
335beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka
336beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka        @Override
3370ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public boolean isInDoubleTapTimeout() {
3380ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            return hasMessages(MSG_DOUBLE_TAP);
3395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
340c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
341c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        @Override
3420ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void cancelKeyTimers() {
3430ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelKeyRepeatTimer();
3440ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelLongPressTimer();
345c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
346c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
3470ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka        public void cancelAllMessages() {
3480ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka            cancelKeyTimers();
349c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka        }
350c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka    }
351c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka
352dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs) {
353c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka        this(context, attrs, R.attr.mainKeyboardViewStyle);
3545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
3555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
356dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
3575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super(context, attrs, defStyle);
3585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
359c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this);
360c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
3615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mHasDistinctMultitouch = context.getPackageManager()
3625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
363918e420d1becc1389b9895538eceff85fe882c99Tadashi G. Takaoka        final Resources res = getResources();
364d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka        final boolean needsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
36529d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka                ResourceUtils.getDeviceOverrideValue(res,
366d438fcaca2a35ace4fee5b7a469596bfe2d1b025Tadashi G. Takaoka                        R.array.phantom_sudden_move_event_device_list, "false"));
36762b8dddb6ddb057555a1665759f9cf197e480c9fTadashi G. Takaoka        PointerTracker.init(mHasDistinctMultitouch, needsPhantomSuddenMoveEventHack);
36822b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
3694112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final TypedArray a = context.obtainStyledAttributes(
370c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
3714112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedEnabled = a.getBoolean(
372c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false);
3734112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedIcon = a.getDrawable(
374c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon);
375ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka        mSpacebarTextRatio = a.getFraction(
376ef2bfad5903fb55adca61dbea51984fbc7e4375fTadashi G. Takaoka                R.styleable.MainKeyboardView_spacebarTextRatio, 1, 1, 1.0f);
377c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka        mSpacebarTextColor = a.getColor(R.styleable.MainKeyboardView_spacebarTextColor, 0);
3784112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextShadowColor = a.getColor(
379c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_spacebarTextShadowColor, 0);
38031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFinalAlpha = a.getInt(
381c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha,
3824daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                Constants.Color.ALPHA_OPAQUE);
383d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        final int languageOnSpacebarFadeoutAnimatorResId = a.getResourceId(
384c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
38573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeoutAnimatorResId = a.getResourceId(
386c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
38773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final int altCodeKeyWhileTypingFadeinAnimatorResId = a.getResourceId(
388c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
389160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
390160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka        final float keyHysteresisDistance = a.getDimension(
391c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_keyHysteresisDistance, 0);
392f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        final float keyHysteresisDistanceForSlidingModifier = a.getDimension(
393f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka                R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0);
394f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka        mKeyDetector = new KeyDetector(
395f731eb1760a5693492a34bc11aa755053aa65c19Tadashi G. Takaoka                keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
3965509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        mKeyTimerHandler = new KeyTimerHandler(this, a);
3972affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        mConfigShowMoreKeysKeyboardAtTouchedPoint = a.getBoolean(
398c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
3995509798977a61dcb4a9dde9030f31bb138b71e3bTadashi G. Takaoka        PointerTracker.setParameters(a);
4004112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        a.recycle();
401160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka
40231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator(
40331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                languageOnSpacebarFadeoutAnimatorResId, this);
40431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator(
40531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeoutAnimatorResId, this);
40631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator(
40731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                altCodeKeyWhileTypingFadeinAnimatorResId, this);
40831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
40931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
410dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private ObjectAnimator loadObjectAnimator(final int resId, final Object target) {
41131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        if (resId == 0) return null;
41231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
41331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                getContext(), resId);
414d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator != null) {
41531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            animator.setTarget(target);
41673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
41731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return animator;
41831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
41973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
42031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    // Getter/setter methods for {@link ObjectAnimator}.
42131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getLanguageOnSpacebarAnimAlpha() {
42231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mLanguageOnSpacebarAnimAlpha;
423d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka    }
424d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka
425dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setLanguageOnSpacebarAnimAlpha(final int alpha) {
42631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mLanguageOnSpacebarAnimAlpha = alpha;
42731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        invalidateKey(mSpaceKey);
42831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
42931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
43031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    public int getAltCodeKeyWhileTypingAnimAlpha() {
43131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        return mAltCodeKeyWhileTypingAnimAlpha;
43231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka    }
43331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka
434dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) {
43531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        mAltCodeKeyWhileTypingAnimAlpha = alpha;
43631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka        updateAltCodeKeyWhileTyping();
4375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
439dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboardActionListener(final KeyboardActionListener listener) {
4405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mKeyboardActionListener = listener;
4415c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka        PointerTracker.setKeyboardActionListener(listener);
4425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
4445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
4455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns the {@link KeyboardActionListener} object.
4465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return the listener attached to this keyboard
4475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
4480efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
4490efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyboardActionListener getKeyboardActionListener() {
4505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mKeyboardActionListener;
4515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
453bb4be5444b845655c0eb80bcfbb66f93603802eaTadashi G. Takaoka    @Override
4540efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public KeyDetector getKeyDetector() {
4550efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyDetector;
4560efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
4570efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
4580efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    @Override
459f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    public DrawingProxy getDrawingProxy() {
460f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka        return this;
461f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    }
462f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka
463f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka    @Override
4640efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    public TimerProxy getTimerProxy() {
4650efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka        return mKeyTimerHandler;
4660efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka    }
4670efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka
4685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
4695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Attaches a keyboard to this view. The keyboard can be switched at any time and the
4705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * view will re-layout itself to accommodate the keyboard.
4715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see Keyboard
4725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @see #getKeyboard()
4735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param keyboard the keyboard to display in this view
4745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
4755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
476dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setKeyboard(final Keyboard keyboard) {
4778a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        // Remove any pending messages, except dismissing preview and key repeat.
4788a092b4ede02b79422deae51f0a416b034777fb3Tadashi G. Takaoka        mKeyTimerHandler.cancelLongPressTimer();
4795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.setKeyboard(keyboard);
4805a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyDetector.setKeyboard(
4815a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka                keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
4828335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setKeyDetector(mKeyDetector);
483c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        mTouchScreenRegulator.setKeyboard(keyboard);
4849d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanelCache.clear();
4854112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
4864112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE);
4871f2d0aa6c9b343848ee51e5bc13ccaaadf3ba4feTadashi G. Takaoka        mSpaceIcon = (mSpaceKey != null)
4884daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null;
4894112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
4904112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
49148a7681e064ae259b840f0e757da2d716043d893Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
492c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            ResearchLogger.mainKeyboardView_setKeyboard(keyboard);
49348a7681e064ae259b840f0e757da2d716043d893Kurt Partridge        }
494f147794fd41491a3383e6aca6d49007f58124068alanv
495f147794fd41491a3383e6aca6d49007f58124068alanv        // This always needs to be set since the accessibility state can
496f147794fd41491a3383e6aca6d49007f58124068alanv        // potentially change without the keyboard being set again.
497f147794fd41491a3383e6aca6d49007f58124068alanv        AccessibleKeyboardViewProxy.getInstance().setKeyboard(keyboard);
4985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
4995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5008335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    // Note that this method is called from a non-UI thread.
501dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
5028335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable);
5038335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka    }
5048335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka
505dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
5068335c59ea7715f3dbc6625f128a7a038f314a89fTadashi G. Takaoka        PointerTracker.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser);
5070657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka    }
5080657b9698a110f8e895448d829478982ce37b6d1Tadashi G. Takaoka
5095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
5105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * Returns whether the device has distinct multi-touch panel.
5115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return true if the device has distinct multi-touch panel.
5125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
5135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean hasDistinctMultitouch() {
5145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mHasDistinctMultitouch;
5155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
517dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void setDistinctMultitouch(final boolean hasDistinctMultitouch) {
51806b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka        mHasDistinctMultitouch = hasDistinctMultitouch;
51906b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka    }
52006b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka
5215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
5224331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    protected void onAttachedToWindow() {
5234331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        super.onAttachedToWindow();
5244331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        // Notify the research logger that the keyboard view has been attached.  This is needed
5254331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        // to properly show the splash screen, which requires that the window token of the
5264331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        // KeyboardView be non-null.
5274331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
528c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka            ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(this);
529c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        }
530c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    }
531c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka
532c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    @Override
533c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka    protected void onDetachedFromWindow() {
534c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        super.onDetachedFromWindow();
535c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        // Notify the research logger that the keyboard view has been detached.  This is needed
536c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        // to invalidate the reference of {@link MainKeyboardView} to null.
537c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka        if (ProductionFlag.IS_EXPERIMENTAL) {
538c3f78c9057a5710898feaf8027659484477e5821Tadashi G. Takaoka            ResearchLogger.getInstance().mainKeyboardView_onDetachedFromWindow();
5394331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge        }
5404331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    }
5414331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge
5424331012a9e7779ff7c8359a443dc5815ee6ea8d9Kurt Partridge    @Override
5435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void cancelAllMessages() {
544f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        mKeyTimerHandler.cancelAllMessages();
5455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.cancelAllMessages();
5465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
548dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private boolean openMoreKeysKeyboardIfRequired(final Key parentKey,
549dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final PointerTracker tracker) {
5505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Check if we have a popup layout specified first.
5519d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysLayout == 0) {
5525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return false;
5535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
5545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
55563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        // Check if we are already displaying popup panel.
5569d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysPanel != null)
55763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return false;
5585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (parentKey == null)
5595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return false;
56063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        return onLongPress(parentKey, tracker);
5615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5639d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    // This default implementation returns a more keys panel.
564dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    protected MoreKeysPanel onCreateMoreKeysPanel(final Key parentKey) {
5659d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (parentKey.mMoreKeys == null)
5665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return null;
5675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5689d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        final View container = LayoutInflater.from(getContext()).inflate(mMoreKeysLayout, null);
5695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (container == null)
5705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            throw new NullPointerException();
5715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5722affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        final MoreKeysKeyboardView moreKeysKeyboardView =
5732affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka                (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view);
5747ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final Keyboard moreKeysKeyboard = new MoreKeysKeyboard.Builder(container, parentKey, this)
5757ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka                .build();
5762affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        moreKeysKeyboardView.setKeyboard(moreKeysKeyboard);
577b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka        container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
5785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5792affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka        return moreKeysKeyboardView;
5805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
5815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
5825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    /**
5832affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka     * Called when a key is long pressed. By default this will open more keys keyboard associated
5845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * with this key.
5855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param parentKey the key that was long pressed
5865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @param tracker the pointer tracker which pressed the parent key
5875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * @return true if the long press is handled, false otherwise. Subclasses should call the
5885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     * method on the base class if the subclass doesn't wish to handle the call.
5895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka     */
590dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    protected boolean onLongPress(final Key parentKey, final PointerTracker tracker) {
5919bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        if (ProductionFlag.IS_EXPERIMENTAL) {
592c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            ResearchLogger.mainKeyboardView_onLongPress();
5939bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge        }
5946dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        final int primaryCode = parentKey.mCode;
595e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka        if (parentKey.hasEmbeddedMoreKey()) {
596ed3bac91f242850c6d1833a5f8981b9cc208c5ddTadashi G. Takaoka            final int embeddedCode = parentKey.mMoreKeys[0].mCode;
5973708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            tracker.onLongPressed();
598e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka            invokeCodeInput(embeddedCode);
5993708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            invokeReleaseKey(primaryCode);
600a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka            KeyboardSwitcher.getInstance().hapticAndAudioFeedback(primaryCode);
6013708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            return true;
6023708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka        }
60381d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        if (primaryCode == Keyboard.CODE_SPACE || primaryCode == Keyboard.CODE_LANGUAGE_SWITCH) {
6043708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            // Long pressing the space key invokes IME switcher dialog.
6053708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka            if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) {
6066dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka                tracker.onLongPressed();
60742e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka                invokeReleaseKey(primaryCode);
60842e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka                return true;
6096dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka            }
6106dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka        }
61117dc10724bf0db04d0a4bfb2b8be0739ad9e60c6Tadashi G. Takaoka        return openMoreKeysPanel(parentKey, tracker);
6126dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
6136dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
614dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private boolean invokeCustomRequest(final int code) {
6152a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        return mKeyboardActionListener.onCustomRequest(code);
61642e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    }
61742e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka
618dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private void invokeCodeInput(final int primaryCode) {
619ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka        mKeyboardActionListener.onCodeInput(
620ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka                primaryCode, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
62142e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka    }
62242e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka
623dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private void invokeReleaseKey(final int primaryCode) {
6242a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka        mKeyboardActionListener.onReleaseKey(primaryCode, false);
6256dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka    }
6266dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka
627dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private boolean openMoreKeysPanel(final Key parentKey, final PointerTracker tracker) {
6289d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        MoreKeysPanel moreKeysPanel = mMoreKeysPanelCache.get(parentKey);
6299d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (moreKeysPanel == null) {
6309d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            moreKeysPanel = onCreateMoreKeysPanel(parentKey);
6319d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            if (moreKeysPanel == null)
6325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                return false;
6339d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysPanelCache.put(parentKey, moreKeysPanel);
6345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6359d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysWindow == null) {
6369d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysWindow = new PopupWindow(getContext());
6379d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysWindow.setBackgroundDrawable(null);
6382affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka            mMoreKeysWindow.setAnimationStyle(R.style.MoreKeysKeyboardAnimation);
6395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6409d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanel = moreKeysPanel;
6419d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanelPointerTrackerId = tracker.mPointerId;
64263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka
6437ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final boolean keyPreviewEnabled = isKeyPreviewPopupEnabled() && !parentKey.noKeyPreview();
6447ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually horizontally aligned with the center of the parent key.
6457ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // If showMoreKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more
6467ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // keys keyboard is placed at the touch point of the parent key.
6477ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled)
6487ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka                ? tracker.getLastX()
649f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka                : parentKey.mX + parentKey.mWidth / 2;
6507ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // The more keys keyboard is usually vertically aligned with the top edge of the parent key
6517ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically
6527ecc1081ab9b4e41e4b2aec7877aaaf8df29e611Tadashi G. Takaoka        // aligned with the bottom edge of the visible part of the key preview.
65329d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@code mPreviewVisibleOffset} has been set appropriately in
65429d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        // {@link KeyboardView#showKeyPreview(PointerTracker)}.
65529d5973fd35438a83acf7f44b5d55d5620278ee3Tadashi G. Takaoka        final int pointY = parentKey.mY + mKeyPreviewDrawParams.mPreviewVisibleOffset;
6569d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        moreKeysPanel.showMoreKeysPanel(
6572a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka                this, this, pointX, pointY, mMoreKeysWindow, mKeyboardActionListener);
6589d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        final int translatedX = moreKeysPanel.translateX(tracker.getLastX());
6599d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        final int translatedY = moreKeysPanel.translateY(tracker.getLastY());
660e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaoka        tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel);
6611b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka        dimEntireKeyboard(true);
6625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
6635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public boolean isInSlidingKeyInput() {
6669d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysPanel != null) {
66763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            return true;
66863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        } else {
6695c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka            return PointerTracker.isAnyInSlidingKeyInput();
6705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
6715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public int getPointerCount() {
6745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return mOldPointerCount;
6755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
6765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
6775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
678dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    public boolean dispatchTouchEvent(MotionEvent event) {
679dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
680dc2ee7772402633817702e95c2a5b17f6dec03ebalanv            return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event);
681dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        }
682dc2ee7772402633817702e95c2a5b17f6dec03ebalanv        return super.dispatchTouchEvent(event);
683dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    }
684dc2ee7772402633817702e95c2a5b17f6dec03ebalanv
685dc2ee7772402633817702e95c2a5b17f6dec03ebalanv    @Override
686dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean onTouchEvent(final MotionEvent me) {
68746286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        if (getKeyboard() == null) {
68846286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka            return false;
68946286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka        }
690c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka        return mTouchScreenRegulator.onTouchEvent(me);
691c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    }
692c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka
693c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka    @Override
694dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean processMotionEvent(final MotionEvent me) {
695f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        final boolean nonDistinctMultitouch = !mHasDistinctMultitouch;
6965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int action = me.getActionMasked();
6975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int pointerCount = me.getPointerCount();
6985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int oldPointerCount = mOldPointerCount;
6995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        mOldPointerCount = pointerCount;
7005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
7025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // If the device does not have distinct multi-touch support panel, ignore all multi-touch
7035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // events except a transition from/to single-touch.
704f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
7055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
7065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final long eventTime = me.getEventTime();
7095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int index = me.getActionIndex();
7105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        final int id = me.getPointerId(index);
71163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        final int x, y;
7129d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysPanel != null && id == mMoreKeysPanelPointerTrackerId) {
7139d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            x = mMoreKeysPanel.translateX((int)me.getX(index));
7149d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            y = mMoreKeysPanel.translateY((int)me.getY(index));
71563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka        } else {
71663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            x = (int)me.getX(index);
71763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka            y = (int)me.getY(index);
7185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
719d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge        if (ENABLE_USABILITY_STUDY_LOG) {
720d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge            final String eventTag;
721d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge            switch (action) {
722d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                case MotionEvent.ACTION_UP:
723d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    eventTag = "[Up]";
724d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    break;
725d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                case MotionEvent.ACTION_DOWN:
726d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    eventTag = "[Down]";
727d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    break;
728d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                case MotionEvent.ACTION_POINTER_UP:
729d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    eventTag = "[PointerUp]";
730d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    break;
731d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                case MotionEvent.ACTION_POINTER_DOWN:
732d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    eventTag = "[PointerDown]";
733d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    break;
734d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                case MotionEvent.ACTION_MOVE: // Skip this as being logged below
735d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    eventTag = "";
736d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    break;
737d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                default:
738d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    eventTag = "[Action" + action + "]";
739d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    break;
740d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge            }
741d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge            if (!TextUtils.isEmpty(eventTag)) {
742d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                final float size = me.getSize(index);
743d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                final float pressure = me.getPressure(index);
744d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                UsabilityStudyLogUtils.getInstance().write(
745d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                        eventTag + eventTime + "," + id + "," + x + "," + y + ","
746d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                        + size + "," + pressure);
747d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge            }
748d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge        }
749c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa        if (ProductionFlag.IS_EXPERIMENTAL) {
750c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka            ResearchLogger.mainKeyboardView_processMotionEvent(me, action, eventTime, index, id,
7519bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                    x, y);
75215d4793911fa305e0a58aced925961e948582979satok        }
7535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
754f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (mKeyTimerHandler.isInKeyRepeat()) {
755e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
7565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Key repeating timer will be canceled if 2 or more keys are in action, and current
7575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // event (UP or DOWN) is non-modifier key.
7585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount > 1 && !tracker.isModifier()) {
759f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka                mKeyTimerHandler.cancelKeyRepeatTimer();
7605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
7615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Up event will pass through.
7625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // TODO: cleanup this code into a multi-touch to single-touch event converter class?
7655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // Translate mutli-touch event to single-touch events on the device that has no distinct
7665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        // multi-touch panel.
767f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka        if (nonDistinctMultitouch) {
7685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            // Use only main (id=0) pointer tracker.
769e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
7705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            if (pointerCount == 1 && oldPointerCount == 2) {
7715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Multi-touch to single touch transition.
7725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send a down event for the latest pointer if the key is different from the
7735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // previous key.
774e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                final Key newKey = tracker.getKeyOn(x, y);
775e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                if (mOldKey != newKey) {
7760efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka                    tracker.onDownEvent(x, y, eventTime, this);
7775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                    if (action == MotionEvent.ACTION_UP)
778906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka                        tracker.onUpEvent(x, y, eventTime);
7795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                }
7805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 2 && oldPointerCount == 1) {
7815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Single-touch to multi-touch transition.
7825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                // Send an up event for the last pointer.
7835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                final int lastX = tracker.getLastX();
7845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                final int lastY = tracker.getLastY();
785e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka                mOldKey = tracker.getKeyOn(lastX, lastY);
786906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka                tracker.onUpEvent(lastX, lastY, eventTime);
7875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else if (pointerCount == 1 && oldPointerCount == 1) {
7888ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka                tracker.processMotionEvent(action, x, y, eventTime, this);
7895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            } else {
7905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
7915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka                        + " (old " + oldPointerCount + ")");
7925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
7935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
7945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
7955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
7965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (action == MotionEvent.ACTION_MOVE) {
7975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            for (int i = 0; i < pointerCount; i++) {
798d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                final int pointerId = me.getPointerId(i);
799e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka                final PointerTracker tracker = PointerTracker.getPointerTracker(
800d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                        pointerId, this);
80163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                final int px, py;
8023314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka                final MotionEvent motionEvent;
8039d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                if (mMoreKeysPanel != null
8049d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                        && tracker.mPointerId == mMoreKeysPanelPointerTrackerId) {
8059d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                    px = mMoreKeysPanel.translateX((int)me.getX(i));
8069d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka                    py = mMoreKeysPanel.translateY((int)me.getY(i));
8073314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka                    motionEvent = null;
80863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                } else {
80963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                    px = (int)me.getX(i);
81063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                    py = (int)me.getY(i);
8113314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka                    motionEvent = me;
81263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka                }
8133314d38dafc0b9670e695a194c74950c4ebf2b3dTadashi G. Takaoka                tracker.onMoveEvent(px, py, eventTime, motionEvent);
814d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                if (ENABLE_USABILITY_STUDY_LOG) {
815d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    final float pointerSize = me.getSize(i);
816d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    final float pointerPressure = me.getPressure(i);
817d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                    UsabilityStudyLogUtils.getInstance().write("[Move]"  + eventTime + ","
818d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                            + pointerId + "," + px + "," + py + ","
819d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                            + pointerSize + "," + pointerPressure);
820d05afa3f4c59641c8fabed034e457cb25f0c57f0Kurt Partridge                }
821c166697e3f5ec600089987dbbff0be7f3e308565Ken Wakasa                if (ProductionFlag.IS_EXPERIMENTAL) {
822c8e45ddb032554f4e9d4411d8ef47d98db62d77bTadashi G. Takaoka                    ResearchLogger.mainKeyboardView_processMotionEvent(me, action, eventTime,
8239bc29d78a6ce83f77869aa63748176241e29d43cKurt Partridge                            i, pointerId, px, py);
82415d4793911fa305e0a58aced925961e948582979satok                }
8255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            }
8265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        } else {
827e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
828e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            tracker.processMotionEvent(action, x, y, eventTime, this);
8295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
8305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
8315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return true;
8325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
8335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
8345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    @Override
8355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    public void closing() {
8365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        super.closing();
8379d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        dismissMoreKeysPanel();
8389d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        mMoreKeysPanelCache.clear();
8395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
8405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
8419ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka    @Override
8429d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka    public boolean dismissMoreKeysPanel() {
8439d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka        if (mMoreKeysWindow != null && mMoreKeysWindow.isShowing()) {
8449d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysWindow.dismiss();
8459d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysPanel = null;
8469d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka            mMoreKeysPanelPointerTrackerId = -1;
8471b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka            dimEntireKeyboard(false);
8485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka            return true;
8495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
8505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return false;
8515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
8525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
853586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette    /**
854c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka     * Receives hover events from the input framework.
855586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *
856586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @param event The motion event to be dispatched.
857586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     * @return {@code true} if the event was handled by the view, {@code false}
858586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     *         otherwise
859586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette     */
860c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka    @Override
861dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public boolean dispatchHoverEvent(final MotionEvent event) {
8625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
863e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka            final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
864586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette            return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
8655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        }
8665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka
867586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette        // Reflection doesn't support calling superclass methods.
8685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka        return false;
8695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka    }
87022b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
871dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateShortcutKey(final boolean available) {
8727bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
8737bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        if (keyboard == null) return;
8747bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        final Key shortcutKey = keyboard.getKey(Keyboard.CODE_SHORTCUT);
8757bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        if (shortcutKey == null) return;
8767bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        shortcutKey.setEnabled(available);
8777bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka        invalidateKey(shortcutKey);
8785afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka    }
8795afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka
88073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    private void updateAltCodeKeyWhileTyping() {
88173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        final Keyboard keyboard = getKeyboard();
88273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        if (keyboard == null) return;
88373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        for (final Key key : keyboard.mAltCodeKeysWhileTyping) {
88473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            invalidateKey(key);
88573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
88673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka    }
88773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka
888dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged,
889dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final boolean needsToDisplayLanguage, final boolean hasMultipleEnabledIMEsOrSubtypes) {
8904112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mNeedsToDisplayLanguage = needsToDisplayLanguage;
891fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes;
892fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka        final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator;
893d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka        if (animator == null) {
894aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            mNeedsToDisplayLanguage = false;
8954c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        } else {
896dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            if (subtypeChanged && needsToDisplayLanguage) {
8974daf32b6c0358f0273a99b622a259ecdf6b44fa4Tom Ouyang                setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE);
89831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (animator.isStarted()) {
89931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    animator.cancel();
90031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
901d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka                animator.start();
902dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            } else {
90331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                if (!animator.isStarted()) {
90431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                    mLanguageOnSpacebarAnimAlpha = mLanguageOnSpacebarFinalAlpha;
90531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka                }
906dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka            }
9074c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka        }
9084112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
9094112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
9104112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
911dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    public void updateAutoCorrectionState(final boolean isAutoCorrection) {
9124112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        if (!mAutoCorrectionSpacebarLedEnabled) return;
9134112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        mAutoCorrectionSpacebarLedOn = isAutoCorrection;
9144112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        invalidateKey(mSpaceKey);
91522b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
91622b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka
91722b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    @Override
918dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
919dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka            final KeyDrawParams params) {
9206bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka        if (key.altCodeWhileTyping() && key.isEnabled()) {
92173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka            params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
92273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka        }
9234112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        if (key.mCode == Keyboard.CODE_SPACE) {
924bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawSpacebar(key, canvas, paint);
9254112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Whether space key needs to show the "..." popup hint for special purposes
926fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) {
927bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka                drawKeyPopupHint(key, canvas, paint, params);
9284112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            }
92981d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka        } else if (key.mCode == Keyboard.CODE_LANGUAGE_SWITCH) {
93081d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
931fd60b2f97035382b14dce207b3613711982a613eTadashi G. Takaoka            drawKeyPopupHint(key, canvas, paint, params);
932b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        } else {
933b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            super.onDrawKeyTopVisuals(key, canvas, paint, params);
93422b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka        }
93522b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka    }
9364112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
937dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) {
938bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(1.0f);
939bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        final float textWidth = getLabelWidth(text, paint);
940bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        if (textWidth < width) return true;
941bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
942bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        final float scaleX = width / textWidth;
943bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) return false;
944bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
945bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        paint.setTextScaleX(scaleX);
946bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        return getLabelWidth(text, paint) < width;
947bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    }
948bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
949bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka    // Layout language name on spacebar.
950dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private String layoutLanguageOnSpacebar(final Paint paint, final InputMethodSubtype subtype,
951bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final int width) {
952bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        // Choose appropriate language name to fit into the width.
953bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        String text = getFullDisplayName(subtype, getResources());
954bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        if (fitsTextIntoWidth(width, text, paint)) {
955bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            return text;
956bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        }
957bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka
958bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        text = getMiddleDisplayName(subtype);
959bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        if (fitsTextIntoWidth(width, text, paint)) {
960bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            return text;
9614112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9624112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
963bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        text = getShortDisplayName(subtype);
964bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        if (fitsTextIntoWidth(width, text, paint)) {
965bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            return text;
9664112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9674112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
968bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka        return "";
9694112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
9704112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
971dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    private void drawSpacebar(final Key key, final Canvas canvas, final Paint paint) {
972bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        final int width = key.mWidth;
973b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka        final int height = key.mHeight;
9744112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
975f6972561fcb45310f18230ce217f0c6bb57e7eeeTadashi G. Takaoka        // If input language are explicitly selected.
976bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mNeedsToDisplayLanguage) {
977bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTextAlign(Align.CENTER);
978bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTypeface(Typeface.DEFAULT);
979bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            paint.setTextSize(mSpacebarTextSize);
980bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final InputMethodSubtype subtype = getKeyboard().mId.mSubtype;
981bd2ca9c0214ea80fa860f4a9d118f866e16b03caTadashi G. Takaoka            final String language = layoutLanguageOnSpacebar(paint, subtype, width);
9824112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            // Draw language text with shadow
9834112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float descent = paint.descent();
9844112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final float textHeight = -paint.ascent() + descent;
985b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka            final float baseline = height / 2 + textHeight / 2;
986aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            paint.setColor(mSpacebarTextShadowColor);
98731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
9884112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent - 1, paint);
989aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka            paint.setColor(mSpacebarTextColor);
99031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka            paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
9914112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            canvas.drawText(language, width / 2, baseline - descent, paint);
9924112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
9934112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka
9944112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        // Draw the spacebar icon at the bottom
995bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka        if (mAutoCorrectionSpacebarLedOn) {
9964112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100;
9974112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight();
9984112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
9994112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
1000bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight);
10014112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        } else if (mSpaceIcon != null) {
10024112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconWidth = mSpaceIcon.getIntrinsicWidth();
10034112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            final int iconHeight = mSpaceIcon.getIntrinsicHeight();
10044112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int x = (width - iconWidth) / 2;
10054112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka            int y = height - iconHeight;
1006bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka            drawIcon(canvas, mSpaceIcon, x, y, iconWidth, iconHeight);
10074112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka        }
10084112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka    }
100927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
101027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // InputMethodSubtype's display name for spacebar text in its locale.
101127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //        isAdditionalSubtype (T=true, F=false)
101227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // locale layout | Short  Middle      Full
101327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // ------ ------ - ---- --------- ----------------------
101427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  en_US qwerty F  En  English   English (US)           exception
101527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  en_GB qwerty F  En  English   English (UK)           exception
101627b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  fr    azerty F  Fr  Français  Français
101727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  fr_CA qwerty F  Fr  Français  Français (Canada)
101827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  de    qwertz F  De  Deutsch   Deutsch
101927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  zz    qwerty F      QWERTY    QWERTY
102027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  fr    qwertz T  Fr  Français  Français (QWERTZ)
102127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  de    qwerty T  De  Deutsch   Deutsch (QWERTY)
102227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  en_US azerty T  En  English   English (US) (AZERTY)
102327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    //  zz    azerty T      AZERTY    AZERTY
102427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
102527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's full display name in its locale.
1026dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    static String getFullDisplayName(final InputMethodSubtype subtype, final Resources res) {
102727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
102827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
102927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
103027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
103127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        return SubtypeLocale.getSubtypeDisplayName(subtype, res);
103227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
103327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
103427b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's short display name in its locale.
1035dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    static String getShortDisplayName(final InputMethodSubtype subtype) {
103627b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
103727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return "";
103827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
103927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        final Locale locale = SubtypeLocale.getSubtypeLocale(subtype);
104027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        return StringUtils.toTitleCase(locale.getLanguage(), locale);
104127b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
104227b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka
104327b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    // Get InputMethodSubtype's middle display name in its locale.
1044dc34da218a22489d92d1015e9e5dac8d951b89f4Tadashi G. Takaoka    static String getMiddleDisplayName(final InputMethodSubtype subtype) {
104527b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        if (SubtypeLocale.isNoLanguage(subtype)) {
104627b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka            return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
104727b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        }
104827b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        final Locale locale = SubtypeLocale.getSubtypeLocale(subtype);
104927b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka        return StringUtils.toTitleCase(locale.getDisplayLanguage(locale), locale);
105027b42ced86e1c85de3d59d91a9e5c577fa552569Tadashi G. Takaoka    }
10515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka}
1052