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