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