MainKeyboardView.java revision 31c94cea82f1788e3a04f2a1e012945f35497f0a
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; 234112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.content.res.TypedArray; 246dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport android.graphics.Canvas; 2522b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaokaimport android.graphics.Paint; 264112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.Paint.Align; 27bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaokaimport android.graphics.Typeface; 284112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.drawable.Drawable; 295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.os.Message; 3015d4793911fa305e0a58aced925961e948582979satokimport android.text.TextUtils; 315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.AttributeSet; 325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.util.Log; 335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.LayoutInflater; 345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.MotionEvent; 355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.View; 365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.ViewConfiguration; 37b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaokaimport android.view.ViewGroup; 385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.widget.PopupWindow; 395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibilityUtils; 415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; 426dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.deprecated.VoiceProxy; 43f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; 442321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.TimerProxy; 45e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeySpecParser; 466dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinIME; 4715d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.LatinImeLogger; 485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.R; 495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.StaticInnerHandlerWrapper; 50cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaokaimport com.android.inputmethod.latin.StringUtils; 51cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaokaimport com.android.inputmethod.latin.SubtypeUtils; 526dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.latin.Utils; 5315d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils; 545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 554112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport java.util.Locale; 565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport java.util.WeakHashMap; 575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/** 595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * A view that is responsible for detecting key presses and touch movements. 605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * 615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_keyHysteresisDistance 625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_verticalCorrection 635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_popupLayout 645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */ 65c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaokapublic class LatinKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler, 66c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka SuddenJumpingTouchEventHandler.ProcessMotionEvent { 67c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka private static final String TAG = LatinKeyboardView.class.getSimpleName(); 685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 69bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka // TODO: Kill process when the usability study mode was changed. 70bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy; 71bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka 72bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka /** Listener for {@link KeyboardActionListener}. */ 73bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private KeyboardActionListener mKeyboardActionListener; 74bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka 75bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka /* Space key and its icons */ 764112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka private Key mSpaceKey; 774112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka private Drawable mSpaceIcon; 78bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka // Stuff to draw language name on spacebar. 7931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka private final int mLanguageOnSpacebarFinalAlpha; 8031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator; 81aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka private static final int ALPHA_OPAQUE = 255; 82bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private boolean mNeedsToDisplayLanguage; 83bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private Locale mSpacebarLocale; 8431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka private int mLanguageOnSpacebarAnimAlpha = ALPHA_OPAQUE; 854112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka private final float mSpacebarTextRatio; 864112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka private float mSpacebarTextSize; 874112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka private final int mSpacebarTextColor; 884112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka private final int mSpacebarTextShadowColor; 894112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // If the full language name needs to be smaller than this value to be drawn on space key, 904112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // its short language name will be used instead. 914112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka private static final float MINIMUM_SCALE_OF_LANGUAGE_NAME = 0.8f; 92bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka // Stuff to draw auto correction LED on spacebar. 93bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private boolean mAutoCorrectionSpacebarLedOn; 94bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private final boolean mAutoCorrectionSpacebarLedEnabled; 95bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private final Drawable mAutoCorrectionSpacebarLedIcon; 96bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private static final int SPACE_LED_LENGTH_PERCENT = 80; 9715d4793911fa305e0a58aced925961e948582979satok 9873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka // Stuff to draw altCodeWhileTyping keys. 9931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator; 10031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator; 101b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka private int mAltCodeKeyWhileTypingAnimAlpha = ALPHA_OPAQUE; 10273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka 1032affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka // More keys keyboard 1049d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka private PopupWindow mMoreKeysWindow; 1059d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka private MoreKeysPanel mMoreKeysPanel; 1069d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka private int mMoreKeysPanelPointerTrackerId; 1079d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka private final WeakHashMap<Key, MoreKeysPanel> mMoreKeysPanelCache = 1089d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka new WeakHashMap<Key, MoreKeysPanel>(); 1092affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint; 1105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 111160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka private final PointerTrackerParams mPointerTrackerParams; 112bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private final boolean mIsSpacebarTriggeringPopupByLongPress; 113bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private final SuddenJumpingTouchEventHandler mTouchScreenRegulator; 1145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 115bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka protected KeyDetector mKeyDetector; 11606b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka private boolean mHasDistinctMultitouch; 1175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka private int mOldPointerCount = 1; 118e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka private Key mOldKey; 1195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 120160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka private final KeyTimerHandler mKeyTimerHandler; 1215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 122c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka private static class KeyTimerHandler extends StaticInnerHandlerWrapper<LatinKeyboardView> 1232321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka implements TimerProxy { 124f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka private static final int MSG_REPEAT_KEY = 1; 125f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka private static final int MSG_LONGPRESS_KEY = 2; 1260ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka private static final int MSG_DOUBLE_TAP = 3; 12773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka private static final int MSG_TYPING_STATE_EXPIRED = 4; 1285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 129a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka private final KeyTimerParams mParams; 1305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka private boolean mInKeyRepeat; 1315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 132a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka public KeyTimerHandler(LatinKeyboardView outerInstance, KeyTimerParams params) { 1335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka super(outerInstance); 134a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka mParams = params; 1355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 1375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka @Override 1385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void handleMessage(Message msg) { 139c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka final LatinKeyboardView keyboardView = getOuterInstance(); 1405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final PointerTracker tracker = (PointerTracker) msg.obj; 1415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka switch (msg.what) { 1425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka case MSG_REPEAT_KEY: 143e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka tracker.onRepeatKey(tracker.getKey()); 14432c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka startKeyRepeatTimer(tracker, mParams.mKeyRepeatInterval); 1455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka break; 1465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka case MSG_LONGPRESS_KEY: 147a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka if (tracker != null) { 1482affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka keyboardView.openMoreKeysKeyboardIfRequired(tracker.getKey(), tracker); 149a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka } else { 150a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka KeyboardSwitcher.getInstance().onLongPressTimeout(msg.arg1); 151a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka } 1525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka break; 15373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka case MSG_TYPING_STATE_EXPIRED: 154b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator, 155b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka keyboardView.mAltCodeKeyWhileTypingFadeinAnimator); 15673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka break; 1575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 16032c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka private void startKeyRepeatTimer(PointerTracker tracker, long delay) { 16132c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, tracker), delay); 16232c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka } 16332c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka 1642321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka @Override 165a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka public void startKeyRepeatTimer(PointerTracker tracker) { 1665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka mInKeyRepeat = true; 16732c54c4dbed0b27b7177f796d90a2ebb9566c9c9Tadashi G. Takaoka startKeyRepeatTimer(tracker, mParams.mKeyRepeatStartTimeout); 1685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 1705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void cancelKeyRepeatTimer() { 1715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka mInKeyRepeat = false; 1725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka removeMessages(MSG_REPEAT_KEY); 1735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 1755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public boolean isInKeyRepeat() { 1765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return mInKeyRepeat; 1775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 1792321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka @Override 180a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka public void startLongPressTimer(int code) { 18198b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka cancelLongPressTimer(); 182a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka final int delay; 183a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka switch (code) { 184a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka case Keyboard.CODE_SHIFT: 18540e9012276b1df9be8c1a9069eaeb16027549a85Tadashi G. Takaoka delay = mParams.mLongPressShiftKeyTimeout; 186a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka break; 187a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka default: 188a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka delay = 0; 189a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka break; 190a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka } 191a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka if (delay > 0) { 192a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, code, 0), delay); 193a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka } 194a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka } 195a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka 196a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka @Override 197a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka public void startLongPressTimer(PointerTracker tracker) { 198a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka cancelLongPressTimer(); 199a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka if (tracker != null) { 200a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka final Key key = tracker.getKey(); 201a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka final int delay; 202a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka switch (key.mCode) { 203a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka case Keyboard.CODE_SHIFT: 204a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka delay = mParams.mLongPressShiftKeyTimeout; 205a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka break; 206a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka case Keyboard.CODE_SPACE: 207a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka delay = mParams.mLongPressSpaceKeyTimeout; 208a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka break; 209a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka default: 210a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka if (KeyboardSwitcher.getInstance().isInMomentarySwitchState()) { 211a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka // We use longer timeout for sliding finger input started from the symbols 212a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka // mode key. 213a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka delay = mParams.mLongPressKeyTimeout * 3; 214a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka } else { 215a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka delay = mParams.mLongPressKeyTimeout; 216a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka } 217a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka break; 218a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka } 219a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka if (delay > 0) { 220a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, tracker), delay); 221a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka } 222a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka } 2235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 2245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 2252321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka @Override 22698b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka public void cancelLongPressTimer() { 2275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka removeMessages(MSG_LONGPRESS_KEY); 2285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 2295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 23031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka public static void cancelAndStartAnimators(ObjectAnimator animatorToCancel, 23131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka ObjectAnimator animatorToStart) { 232b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka if (animatorToCancel != null && animatorToCancel.isStarted()) { 233b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka animatorToCancel.cancel(); 234b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka } 235b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka // TODO: Start the animation with an initial value that is the same as the final value 236b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka // of the above animation when it gets cancelled. 237b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka if (animatorToStart != null && !animatorToStart.isStarted()) { 238b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka animatorToStart.start(); 239b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka } 240b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka } 241b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka 242b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka private void cancelTypingStateTimer() { 243b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka removeMessages(MSG_TYPING_STATE_EXPIRED); 244b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka } 245b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka 2462321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka @Override 24773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka public void startTypingStateTimer() { 24873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka final boolean isTyping = isTypingState(); 249b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka cancelTypingStateTimer(); 25073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka sendMessageDelayed( 25173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka obtainMessage(MSG_TYPING_STATE_EXPIRED), mParams.mIgnoreAltCodeKeyTimeout); 25273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka if (isTyping) { 25373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka return; 25473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka } 255b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka final LatinKeyboardView keyboardView = getOuterInstance(); 256b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator, 257b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator); 25893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka } 25993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka 26093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka @Override 26173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka public boolean isTypingState() { 26273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka return hasMessages(MSG_TYPING_STATE_EXPIRED); 26393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka } 26493246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka 26593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka @Override 2660ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka public void startDoubleTapTimer() { 2670ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP), 2685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka ViewConfiguration.getDoubleTapTimeout()); 2695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 2705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 2710ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka @Override 272beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka public void cancelDoubleTapTimer() { 273beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka removeMessages(MSG_DOUBLE_TAP); 274beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka } 275beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka 276beb08b398fa73a26f2d42d6feec87e34a96ca2d9Tadashi G. Takaoka @Override 2770ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka public boolean isInDoubleTapTimeout() { 2780ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka return hasMessages(MSG_DOUBLE_TAP); 2795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 280c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka 281c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka @Override 2820ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka public void cancelKeyTimers() { 2830ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka cancelKeyRepeatTimer(); 2840ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka cancelLongPressTimer(); 285c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka } 286c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka 2870ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka public void cancelAllMessages() { 2880ed2d3a4491cb0f6142975a15b653be6079b6a4eTadashi G. Takaoka cancelKeyTimers(); 289b9720a55b47684589e3176434cd2b1a08942d112Tadashi G. Takaoka cancelTypingStateTimer(); 290c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka } 291c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka } 292c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka 293160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka public static class PointerTrackerParams { 294160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka public final boolean mSlidingKeyInputEnabled; 295160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka public final int mTouchNoiseThresholdTime; 296160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka public final float mTouchNoiseThresholdDistance; 297160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka 298160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka public static final PointerTrackerParams DEFAULT = new PointerTrackerParams(); 299160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka 300160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka private PointerTrackerParams() { 301160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka mSlidingKeyInputEnabled = false; 302160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka mTouchNoiseThresholdTime =0; 303160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka mTouchNoiseThresholdDistance = 0; 304160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka } 305160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka 306160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka public PointerTrackerParams(TypedArray latinKeyboardViewAttr) { 307160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka mSlidingKeyInputEnabled = latinKeyboardViewAttr.getBoolean( 308160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka R.styleable.LatinKeyboardView_slidingKeyInputEnable, false); 309a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka mTouchNoiseThresholdTime = latinKeyboardViewAttr.getInt( 310a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka R.styleable.LatinKeyboardView_touchNoiseThresholdTime, 0); 311a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka mTouchNoiseThresholdDistance = latinKeyboardViewAttr.getDimension( 312a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka R.styleable.LatinKeyboardView_touchNoiseThresholdDistance, 0); 313a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka } 314a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka } 315a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka 316a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka static class KeyTimerParams { 317a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka public final int mKeyRepeatStartTimeout; 318a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka public final int mKeyRepeatInterval; 319a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka public final int mLongPressKeyTimeout; 320a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka public final int mLongPressShiftKeyTimeout; 321a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka public final int mLongPressSpaceKeyTimeout; 32273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka public final int mIgnoreAltCodeKeyTimeout; 323a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka 324a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka public KeyTimerParams(TypedArray latinKeyboardViewAttr) { 325160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka mKeyRepeatStartTimeout = latinKeyboardViewAttr.getInt( 326160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka R.styleable.LatinKeyboardView_keyRepeatStartTimeout, 0); 327a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka mKeyRepeatInterval = latinKeyboardViewAttr.getInt( 328a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka R.styleable.LatinKeyboardView_keyRepeatInterval, 0); 329160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka mLongPressKeyTimeout = latinKeyboardViewAttr.getInt( 330160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka R.styleable.LatinKeyboardView_longPressKeyTimeout, 0); 331160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka mLongPressShiftKeyTimeout = latinKeyboardViewAttr.getInt( 332160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka R.styleable.LatinKeyboardView_longPressShiftKeyTimeout, 0); 333160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka mLongPressSpaceKeyTimeout = latinKeyboardViewAttr.getInt( 334160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka R.styleable.LatinKeyboardView_longPressSpaceKeyTimeout, 0); 33573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka mIgnoreAltCodeKeyTimeout = latinKeyboardViewAttr.getInt( 33673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka R.styleable.LatinKeyboardView_ignoreAltCodeKeyTimeout, 0); 337160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka } 338160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka } 339160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka 340c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka public LatinKeyboardView(Context context, AttributeSet attrs) { 3415afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka this(context, attrs, R.attr.latinKeyboardViewStyle); 3425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 3435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 344c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) { 3455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka super(context, attrs, defStyle); 3465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 347c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this); 348c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka 3495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka mHasDistinctMultitouch = context.getPackageManager() 3505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT); 351906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka 352a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka PointerTracker.init(mHasDistinctMultitouch); 35322b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka 3544112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final TypedArray a = context.obtainStyledAttributes( 3554112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka attrs, R.styleable.LatinKeyboardView, defStyle, R.style.LatinKeyboardView); 3564112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mAutoCorrectionSpacebarLedEnabled = a.getBoolean( 3574112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedEnabled, false); 3584112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mAutoCorrectionSpacebarLedIcon = a.getDrawable( 3594112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedIcon); 3604112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mSpacebarTextRatio = a.getFraction(R.styleable.LatinKeyboardView_spacebarTextRatio, 3614112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 1000, 1000, 1) / 1000.0f; 3624112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboardView_spacebarTextColor, 0); 3634112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mSpacebarTextShadowColor = a.getColor( 3644112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka R.styleable.LatinKeyboardView_spacebarTextShadowColor, 0); 36531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka mLanguageOnSpacebarFinalAlpha = a.getInt( 36631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka R.styleable.LatinKeyboardView_languageOnSpacebarFinalAlpha, ALPHA_OPAQUE); 367d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka final int languageOnSpacebarFadeoutAnimatorResId = a.getResourceId( 368d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka R.styleable.LatinKeyboardView_languageOnSpacebarFadeoutAnimator, 0); 36973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka final int altCodeKeyWhileTypingFadeoutAnimatorResId = a.getResourceId( 37073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka R.styleable.LatinKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0); 37173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka final int altCodeKeyWhileTypingFadeinAnimatorResId = a.getResourceId( 37273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka R.styleable.LatinKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0); 373160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka 374a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka final KeyTimerParams keyTimerParams = new KeyTimerParams(a); 375160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka mPointerTrackerParams = new PointerTrackerParams(a); 376a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka mIsSpacebarTriggeringPopupByLongPress = (keyTimerParams.mLongPressSpaceKeyTimeout > 0); 377160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka 378160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka final float keyHysteresisDistance = a.getDimension( 379160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka R.styleable.LatinKeyboardView_keyHysteresisDistance, 0); 380160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka mKeyDetector = new KeyDetector(keyHysteresisDistance); 381a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka mKeyTimerHandler = new KeyTimerHandler(this, keyTimerParams); 3822affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka mConfigShowMoreKeysKeyboardAtTouchedPoint = a.getBoolean( 3832affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka R.styleable.LatinKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false); 3844112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka a.recycle(); 385160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka 386160f01211d169d64102205e80e9ac8d46c7d674bTadashi G. Takaoka PointerTracker.setParameters(mPointerTrackerParams); 3874c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka 38831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator( 38931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka languageOnSpacebarFadeoutAnimatorResId, this); 39031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator( 39131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka altCodeKeyWhileTypingFadeoutAnimatorResId, this); 39231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator( 39331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka altCodeKeyWhileTypingFadeinAnimatorResId, this); 39431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka } 39531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka 39631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka private ObjectAnimator loadObjectAnimator(int resId, Object target) { 39731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka if (resId == 0) return null; 39831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator( 39931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka getContext(), resId); 400d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka if (animator != null) { 40131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka animator.setTarget(target); 40273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka } 40331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka return animator; 40431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka } 40573a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka 40631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka // Getter/setter methods for {@link ObjectAnimator}. 40731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka public int getLanguageOnSpacebarAnimAlpha() { 40831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka return mLanguageOnSpacebarAnimAlpha; 409d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka } 410d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka 41131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka public void setLanguageOnSpacebarAnimAlpha(int alpha) { 41231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka mLanguageOnSpacebarAnimAlpha = alpha; 41331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka invalidateKey(mSpaceKey); 41431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka } 41531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka 41631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka public int getAltCodeKeyWhileTypingAnimAlpha() { 41731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka return mAltCodeKeyWhileTypingAnimAlpha; 41831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka } 41931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka 42031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka public void setAltCodeKeyWhileTypingAnimAlpha(int alpha) { 42131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka mAltCodeKeyWhileTypingAnimAlpha = alpha; 42231c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka updateAltCodeKeyWhileTyping(); 4235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 4245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 4255a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka public void setKeyboardActionListener(KeyboardActionListener listener) { 4265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka mKeyboardActionListener = listener; 4275c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka PointerTracker.setKeyboardActionListener(listener); 4285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 4295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 4305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka /** 4315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Returns the {@link KeyboardActionListener} object. 4325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @return the listener attached to this keyboard 4335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */ 4340efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka @Override 4350efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka public KeyboardActionListener getKeyboardActionListener() { 4365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return mKeyboardActionListener; 4375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 4385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 439bb4be5444b845655c0eb80bcfbb66f93603802eaTadashi G. Takaoka @Override 4400efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka public KeyDetector getKeyDetector() { 4410efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka return mKeyDetector; 4420efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka } 4430efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka 4440efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka @Override 445f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka public DrawingProxy getDrawingProxy() { 446f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka return this; 447f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka } 448f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka 449f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka @Override 4500efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka public TimerProxy getTimerProxy() { 4510efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka return mKeyTimerHandler; 4520efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka } 4530efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka 4545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka /** 4555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Attaches a keyboard to this view. The keyboard can be switched at any time and the 4565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * view will re-layout itself to accommodate the keyboard. 4575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @see Keyboard 4585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @see #getKeyboard() 4595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @param keyboard the keyboard to display in this view 4605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */ 4615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka @Override 4625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void setKeyboard(Keyboard keyboard) { 4635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Remove any pending messages, except dismissing preview 464f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka mKeyTimerHandler.cancelKeyTimers(); 4655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka super.setKeyboard(keyboard); 4665a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka mKeyDetector.setKeyboard( 4675a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection); 4685c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka PointerTracker.setKeyDetector(mKeyDetector); 469c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka mTouchScreenRegulator.setKeyboard(keyboard); 4709d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysPanelCache.clear(); 4714112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 4724112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE); 473b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka mSpaceIcon = (mSpaceKey != null) ? mSpaceKey.getIcon(keyboard.mIconsSet) : null; 4744112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; 4754112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mSpacebarTextSize = keyHeight * mSpacebarTextRatio; 4764112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mSpacebarLocale = keyboard.mId.mLocale; 4775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 4785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 4795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka /** 4805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Returns whether the device has distinct multi-touch panel. 4815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @return true if the device has distinct multi-touch panel. 4825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */ 4835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public boolean hasDistinctMultitouch() { 4845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return mHasDistinctMultitouch; 4855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 4865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 48706b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka public void setDistinctMultitouch(boolean hasDistinctMultitouch) { 48806b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka mHasDistinctMultitouch = hasDistinctMultitouch; 48906b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka } 49006b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka 4915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka /** 4925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * When enabled, calls to {@link KeyboardActionListener#onCodeInput} will include key 4935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * codes for adjacent keys. When disabled, only the primary key code will be 4945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * reported. 4955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @param enabled whether or not the proximity correction is enabled 4965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */ 4975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void setProximityCorrectionEnabled(boolean enabled) { 4985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka mKeyDetector.setProximityCorrectionEnabled(enabled); 4995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 5005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 5015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka /** 5025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Returns true if proximity correction is enabled. 5035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */ 5045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public boolean isProximityCorrectionEnabled() { 5055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return mKeyDetector.isProximityCorrectionEnabled(); 5065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 5075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 5085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka @Override 5095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void cancelAllMessages() { 510f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka mKeyTimerHandler.cancelAllMessages(); 5115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka super.cancelAllMessages(); 5125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 5135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 5142affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka private boolean openMoreKeysKeyboardIfRequired(Key parentKey, PointerTracker tracker) { 5155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Check if we have a popup layout specified first. 5169d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysLayout == 0) { 5175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return false; 5185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 5195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 52063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka // Check if we are already displaying popup panel. 5219d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysPanel != null) 52263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka return false; 5235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (parentKey == null) 5245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return false; 52563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka return onLongPress(parentKey, tracker); 5265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 5275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 5289d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka // This default implementation returns a more keys panel. 5299d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka protected MoreKeysPanel onCreateMoreKeysPanel(Key parentKey) { 5309d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (parentKey.mMoreKeys == null) 5315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return null; 5325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 5339d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka final View container = LayoutInflater.from(getContext()).inflate(mMoreKeysLayout, null); 5345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (container == null) 5355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka throw new NullPointerException(); 5365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 5372affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka final MoreKeysKeyboardView moreKeysKeyboardView = 5382affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view); 5395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final Keyboard parentKeyboard = getKeyboard(); 5402affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka final Keyboard moreKeysKeyboard = new MoreKeysKeyboard.Builder( 5419d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka this, parentKeyboard.mMoreKeysTemplate, parentKey, parentKeyboard).build(); 5422affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka moreKeysKeyboardView.setKeyboard(moreKeysKeyboard); 543b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 5445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 5452affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka return moreKeysKeyboardView; 5465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 5475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 5485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka /** 5492affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka * Called when a key is long pressed. By default this will open more keys keyboard associated 5505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * with this key. 5515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @param parentKey the key that was long pressed 5525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @param tracker the pointer tracker which pressed the parent key 5535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @return true if the long press is handled, false otherwise. Subclasses should call the 5545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * method on the base class if the subclass doesn't wish to handle the call. 5555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */ 5565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka protected boolean onLongPress(Key parentKey, PointerTracker tracker) { 5576dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka final int primaryCode = parentKey.mCode; 558e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka if (parentKey.hasEmbeddedMoreKey()) { 559e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka final int embeddedCode = KeySpecParser.getCode(getResources(), parentKey.mMoreKeys[0]); 5603708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka tracker.onLongPressed(); 561e491be6e8690ffb8359887838fa12d5873346be5Tadashi G. Takaoka invokeCodeInput(embeddedCode); 5623708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka invokeReleaseKey(primaryCode); 563a5c96f376ad57e78a88942bb618e067054ed818aTadashi G. Takaoka KeyboardSwitcher.getInstance().hapticAndAudioFeedback(primaryCode); 5643708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka return true; 5653708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka } 56681d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka if (primaryCode == Keyboard.CODE_SPACE || primaryCode == Keyboard.CODE_LANGUAGE_SWITCH) { 5673708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka // Long pressing the space key invokes IME switcher dialog. 5683708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) { 5696dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka tracker.onLongPressed(); 57042e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka invokeReleaseKey(primaryCode); 57142e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka return true; 5726dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 5736dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 57417dc10724bf0db04d0a4bfb2b8be0739ad9e60c6Tadashi G. Takaoka return openMoreKeysPanel(parentKey, tracker); 5756dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 5766dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka 57742e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka private boolean invokeCustomRequest(int code) { 5782a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka return mKeyboardActionListener.onCustomRequest(code); 57942e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka } 58042e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka 58142e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka private void invokeCodeInput(int primaryCode) { 582ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok mKeyboardActionListener.onCodeInput(primaryCode, 5836dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka KeyboardActionListener.NOT_A_TOUCH_COORDINATE, 5846dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka KeyboardActionListener.NOT_A_TOUCH_COORDINATE); 58542e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka } 58642e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka 58742e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka private void invokeReleaseKey(int primaryCode) { 5882a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka mKeyboardActionListener.onReleaseKey(primaryCode, false); 5896dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 5906dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka 5919d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka private boolean openMoreKeysPanel(Key parentKey, PointerTracker tracker) { 5929d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka MoreKeysPanel moreKeysPanel = mMoreKeysPanelCache.get(parentKey); 5939d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (moreKeysPanel == null) { 5949d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka moreKeysPanel = onCreateMoreKeysPanel(parentKey); 5959d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (moreKeysPanel == null) 5965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return false; 5979d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysPanelCache.put(parentKey, moreKeysPanel); 5985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 5999d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysWindow == null) { 6009d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysWindow = new PopupWindow(getContext()); 6019d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysWindow.setBackgroundDrawable(null); 6022affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka mMoreKeysWindow.setAnimationStyle(R.style.MoreKeysKeyboardAnimation); 6035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6049d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysPanel = moreKeysPanel; 6059d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysPanelPointerTrackerId = tracker.mPointerId; 60663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka 607f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka final Keyboard keyboard = getKeyboard(); 6082affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint) ? tracker.getLastX() 609f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka : parentKey.mX + parentKey.mWidth / 2; 610f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka final int pointY = parentKey.mY - keyboard.mVerticalGap; 6119d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka moreKeysPanel.showMoreKeysPanel( 6122a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka this, this, pointX, pointY, mMoreKeysWindow, mKeyboardActionListener); 6139d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka final int translatedX = moreKeysPanel.translateX(tracker.getLastX()); 6149d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka final int translatedY = moreKeysPanel.translateY(tracker.getLastY()); 615e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaoka tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel); 6161b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka dimEntireKeyboard(true); 6175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return true; 6185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 6205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public boolean isInSlidingKeyInput() { 6219d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysPanel != null) { 62263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka return true; 62363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka } else { 6245c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka return PointerTracker.isAnyInSlidingKeyInput(); 6255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 6285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public int getPointerCount() { 6295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return mOldPointerCount; 6305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 6325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka @Override 6335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public boolean onTouchEvent(MotionEvent me) { 63446286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka if (getKeyboard() == null) { 63546286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka return false; 63646286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka } 637c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka return mTouchScreenRegulator.onTouchEvent(me); 638c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka } 639c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka 640c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka @Override 641c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka public boolean processMotionEvent(MotionEvent me) { 642f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka final boolean nonDistinctMultitouch = !mHasDistinctMultitouch; 6435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final int action = me.getActionMasked(); 6445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final int pointerCount = me.getPointerCount(); 6455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final int oldPointerCount = mOldPointerCount; 6465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka mOldPointerCount = pointerCount; 6475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 6485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // TODO: cleanup this code into a multi-touch to single-touch event converter class? 6495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // If the device does not have distinct multi-touch support panel, ignore all multi-touch 6505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // events except a transition from/to single-touch. 651f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka if (nonDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) { 6525f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return true; 6535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 6555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final long eventTime = me.getEventTime(); 6565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final int index = me.getActionIndex(); 6575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final int id = me.getPointerId(index); 65863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka final int x, y; 6599d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysPanel != null && id == mMoreKeysPanelPointerTrackerId) { 6609d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka x = mMoreKeysPanel.translateX((int)me.getX(index)); 6619d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka y = mMoreKeysPanel.translateY((int)me.getY(index)); 66263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka } else { 66363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka x = (int)me.getX(index); 66463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka y = (int)me.getY(index); 6655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 66615d4793911fa305e0a58aced925961e948582979satok if (ENABLE_USABILITY_STUDY_LOG) { 66715d4793911fa305e0a58aced925961e948582979satok final String eventTag; 66815d4793911fa305e0a58aced925961e948582979satok switch (action) { 66915d4793911fa305e0a58aced925961e948582979satok case MotionEvent.ACTION_UP: 67015d4793911fa305e0a58aced925961e948582979satok eventTag = "[Up]"; 67115d4793911fa305e0a58aced925961e948582979satok break; 67215d4793911fa305e0a58aced925961e948582979satok case MotionEvent.ACTION_DOWN: 67315d4793911fa305e0a58aced925961e948582979satok eventTag = "[Down]"; 67415d4793911fa305e0a58aced925961e948582979satok break; 67515d4793911fa305e0a58aced925961e948582979satok case MotionEvent.ACTION_POINTER_UP: 67615d4793911fa305e0a58aced925961e948582979satok eventTag = "[PointerUp]"; 67715d4793911fa305e0a58aced925961e948582979satok break; 67815d4793911fa305e0a58aced925961e948582979satok case MotionEvent.ACTION_POINTER_DOWN: 67915d4793911fa305e0a58aced925961e948582979satok eventTag = "[PointerDown]"; 68015d4793911fa305e0a58aced925961e948582979satok break; 6813425852be4229b8937c3fd9a82d709d9bd8c4b4eKen Wakasa case MotionEvent.ACTION_MOVE: // Skip this as being logged below 6823425852be4229b8937c3fd9a82d709d9bd8c4b4eKen Wakasa eventTag = ""; 6833425852be4229b8937c3fd9a82d709d9bd8c4b4eKen Wakasa break; 68415d4793911fa305e0a58aced925961e948582979satok default: 68515d4793911fa305e0a58aced925961e948582979satok eventTag = "[Action" + action + "]"; 68615d4793911fa305e0a58aced925961e948582979satok break; 68715d4793911fa305e0a58aced925961e948582979satok } 68815d4793911fa305e0a58aced925961e948582979satok if (!TextUtils.isEmpty(eventTag)) { 68915d4793911fa305e0a58aced925961e948582979satok UsabilityStudyLogUtils.getInstance().write( 6906029b43a3df1fa9a9baa339e2dbb984c6669d214Ken Wakasa eventTag + eventTime + "," + id + "," + x + "," + y + "," 6916029b43a3df1fa9a9baa339e2dbb984c6669d214Ken Wakasa + me.getSize(index) + "," + me.getPressure(index)); 69215d4793911fa305e0a58aced925961e948582979satok } 69315d4793911fa305e0a58aced925961e948582979satok } 6945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 695f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka if (mKeyTimerHandler.isInKeyRepeat()) { 696e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka final PointerTracker tracker = PointerTracker.getPointerTracker(id, this); 6975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Key repeating timer will be canceled if 2 or more keys are in action, and current 6985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // event (UP or DOWN) is non-modifier key. 6995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (pointerCount > 1 && !tracker.isModifier()) { 700f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka mKeyTimerHandler.cancelKeyRepeatTimer(); 7015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Up event will pass through. 7035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 7055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // TODO: cleanup this code into a multi-touch to single-touch event converter class? 7065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Translate mutli-touch event to single-touch events on the device that has no distinct 7075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // multi-touch panel. 708f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka if (nonDistinctMultitouch) { 7095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Use only main (id=0) pointer tracker. 710e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka final PointerTracker tracker = PointerTracker.getPointerTracker(0, this); 7115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (pointerCount == 1 && oldPointerCount == 2) { 7125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Multi-touch to single touch transition. 7135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Send a down event for the latest pointer if the key is different from the 7145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // previous key. 715e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka final Key newKey = tracker.getKeyOn(x, y); 716e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka if (mOldKey != newKey) { 7170efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka tracker.onDownEvent(x, y, eventTime, this); 7185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (action == MotionEvent.ACTION_UP) 719906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka tracker.onUpEvent(x, y, eventTime); 7205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } else if (pointerCount == 2 && oldPointerCount == 1) { 7225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Single-touch to multi-touch transition. 7235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Send an up event for the last pointer. 7245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final int lastX = tracker.getLastX(); 7255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final int lastY = tracker.getLastY(); 726e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka mOldKey = tracker.getKeyOn(lastX, lastY); 727906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka tracker.onUpEvent(lastX, lastY, eventTime); 7285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } else if (pointerCount == 1 && oldPointerCount == 1) { 7298ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka tracker.processMotionEvent(action, x, y, eventTime, this); 7305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } else { 7315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount 7325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka + " (old " + oldPointerCount + ")"); 7335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return true; 7355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 7375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (action == MotionEvent.ACTION_MOVE) { 7385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka for (int i = 0; i < pointerCount; i++) { 739e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka final PointerTracker tracker = PointerTracker.getPointerTracker( 740e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka me.getPointerId(i), this); 74163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka final int px, py; 7429d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysPanel != null 7439d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka && tracker.mPointerId == mMoreKeysPanelPointerTrackerId) { 7449d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka px = mMoreKeysPanel.translateX((int)me.getX(i)); 7459d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka py = mMoreKeysPanel.translateY((int)me.getY(i)); 74663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka } else { 74763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka px = (int)me.getX(i); 74863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka py = (int)me.getY(i); 74963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka } 75063c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka tracker.onMoveEvent(px, py, eventTime); 75115d4793911fa305e0a58aced925961e948582979satok if (ENABLE_USABILITY_STUDY_LOG) { 75215d4793911fa305e0a58aced925961e948582979satok UsabilityStudyLogUtils.getInstance().write("[Move]" + eventTime + "," 7536029b43a3df1fa9a9baa339e2dbb984c6669d214Ken Wakasa + me.getPointerId(i) + "," + px + "," + py + "," 7546029b43a3df1fa9a9baa339e2dbb984c6669d214Ken Wakasa + me.getSize(i) + "," + me.getPressure(i)); 75515d4793911fa305e0a58aced925961e948582979satok } 7565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } else { 758e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka final PointerTracker tracker = PointerTracker.getPointerTracker(id, this); 759e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka tracker.processMotionEvent(action, x, y, eventTime, this); 7605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 7625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return true; 7635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 7655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka @Override 7665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void closing() { 7675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka super.closing(); 7689d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka dismissMoreKeysPanel(); 7699d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysPanelCache.clear(); 7705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 7729ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka @Override 7739d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka public boolean dismissMoreKeysPanel() { 7749d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysWindow != null && mMoreKeysWindow.isShowing()) { 7759d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysWindow.dismiss(); 7769d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysPanel = null; 7779d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysPanelPointerTrackerId = -1; 7781b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka dimEntireKeyboard(false); 7795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return true; 7805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return false; 7825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 7845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public boolean handleBack() { 7859d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka return dismissMoreKeysPanel(); 7865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 7885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka @Override 7896dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka public void draw(Canvas c) { 7906dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka Utils.GCUtils.getInstance().reset(); 7916dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka boolean tryGC = true; 7926dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { 7936dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka try { 7946dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka super.draw(c); 7956dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka tryGC = false; 7966dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } catch (OutOfMemoryError e) { 797dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka tryGC = Utils.GCUtils.getInstance().tryGCOrWait(TAG, e); 7986dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 7996dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 8006dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 8016dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka 8026dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka @Override 8036dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka protected void onAttachedToWindow() { 8046dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka // Token is available from here. 8056dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka VoiceProxy.getInstance().onAttachedToWindow(); 8066dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 8076dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka 808586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette /** 809586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * Receives hover events from the input framework. This method overrides 810586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * View.dispatchHoverEvent(MotionEvent) on SDK version ICS or higher. On 811586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * lower SDK versions, this method is never called. 812586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * 813586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * @param event The motion event to be dispatched. 814586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * @return {@code true} if the event was handled by the view, {@code false} 815586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * otherwise 816586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette */ 817ca7ec2097ca6af1505c1e6aa8b81b6068ba46daesatok //Should not annotate @override 818586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette public boolean dispatchHoverEvent(MotionEvent event) { 8195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { 820e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka final PointerTracker tracker = PointerTracker.getPointerTracker(0, this); 821586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker); 8225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 8235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 824586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette // Reflection doesn't support calling superclass methods. 8255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return false; 8265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 82722b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka 8285afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka public void updateShortcutKey(boolean available) { 8297bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka final Keyboard keyboard = getKeyboard(); 8307bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka if (keyboard == null) return; 8317bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka final Key shortcutKey = keyboard.getKey(Keyboard.CODE_SHORTCUT); 8327bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka if (shortcutKey == null) return; 8337bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka shortcutKey.setEnabled(available); 8347bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka invalidateKey(shortcutKey); 8355afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka } 8365afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka 83773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka private void updateAltCodeKeyWhileTyping() { 83873a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka final Keyboard keyboard = getKeyboard(); 83973a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka if (keyboard == null) return; 84073a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka for (final Key key : keyboard.mAltCodeKeysWhileTyping) { 84173a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka invalidateKey(key); 84273a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka } 84373a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka } 84473a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka 845dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka public void startDisplayLanguageOnSpacebar(boolean subtypeChanged, 8464c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka boolean needsToDisplayLanguage) { 84731c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator; 8484112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mNeedsToDisplayLanguage = needsToDisplayLanguage; 849d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka if (animator == null) { 850aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka mNeedsToDisplayLanguage = false; 8514c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka } else { 852dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka if (subtypeChanged && needsToDisplayLanguage) { 85331c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka setLanguageOnSpacebarAnimAlpha(ALPHA_OPAQUE); 85431c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka if (animator.isStarted()) { 85531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka animator.cancel(); 85631c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka } 857d7c4ba170982ddce5ac12ea92c3c3d8b53d524baTadashi G. Takaoka animator.start(); 858dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka } else { 85931c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka if (!animator.isStarted()) { 86031c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka mLanguageOnSpacebarAnimAlpha = mLanguageOnSpacebarFinalAlpha; 86131c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka } 862dabf96896ef4c304c6dad36b307a2a458a58209dTadashi G. Takaoka } 8634c0c638a189c1073b1fb6e43fe5fddb6f9932038Tadashi G. Takaoka } 8644112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka invalidateKey(mSpaceKey); 8654112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 8664112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 8674112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka public void updateAutoCorrectionState(boolean isAutoCorrection) { 8684112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka if (!mAutoCorrectionSpacebarLedEnabled) return; 8694112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mAutoCorrectionSpacebarLedOn = isAutoCorrection; 8704112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka invalidateKey(mSpaceKey); 87122b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka } 87222b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka 87322b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka @Override 874f9521c6f378e3f2aa13d9e382ae13708e3ae6317Tadashi G. Takaoka protected void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint, KeyDrawParams params) { 8756bc9186457219daeb3734531a01271b0e4fa37fbTadashi G. Takaoka if (key.altCodeWhileTyping() && key.isEnabled()) { 87673a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha; 87773a46bfeb7a109b49be196e5d679e44c9e66a2e8Tadashi G. Takaoka } 8784112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka if (key.mCode == Keyboard.CODE_SPACE) { 879bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka drawSpacebar(key, canvas, paint); 880bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka 8814112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // Whether space key needs to show the "..." popup hint for special purposes 8824112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka if (mIsSpacebarTriggeringPopupByLongPress 883cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka && SubtypeUtils.hasMultipleEnabledIMEsOrSubtypes( 884cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka true /* include aux subtypes */)) { 885bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka drawKeyPopupHint(key, canvas, paint, params); 8864112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 88781d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka } else if (key.mCode == Keyboard.CODE_LANGUAGE_SWITCH) { 88881d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka super.onDrawKeyTopVisuals(key, canvas, paint, params); 889cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka if (SubtypeUtils.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) { 89081d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka drawKeyPopupHint(key, canvas, paint, params); 89181d4e3cd66a9388c47c7dba55240ddf849b31934Tadashi G. Takaoka } 892b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka } else { 893b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka super.onDrawKeyTopVisuals(key, canvas, paint, params); 89422b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka } 89522b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka } 8964112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 8974112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // Compute width of text with specified text size using paint. 898bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private int getTextWidth(Paint paint, String text, float textSize) { 8994112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka paint.setTextSize(textSize); 900bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka return (int)getLabelWidth(text, paint); 9014112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 9024112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 9034015a64a263b07178b99d3e3e864bd75da017638Tadashi G. Takaoka // Layout locale language name on spacebar. 904bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private String layoutLanguageOnSpacebar(Paint paint, Locale locale, int width, 9054112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka float origTextSize) { 906bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka paint.setTextAlign(Align.CENTER); 907bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka paint.setTypeface(Typeface.DEFAULT); 9084112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // Estimate appropriate language name text size to fit in maxTextWidth. 909cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka String language = StringUtils.getFullDisplayName(locale, true); 910bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka int textWidth = getTextWidth(paint, language, origTextSize); 9114112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // Assuming text width and text size are proportional to each other. 9124112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka float textSize = origTextSize * Math.min(width / textWidth, 1.0f); 9134112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // allow variable text size 914bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka textWidth = getTextWidth(paint, language, textSize); 9154112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // If text size goes too small or text does not fit, use middle or short name 9164112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final boolean useMiddleName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME) 9174112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka || (textWidth > width); 9184112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 9194112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final boolean useShortName; 9204112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka if (useMiddleName) { 921cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka language = StringUtils.getMiddleDisplayLanguage(locale); 922bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka textWidth = getTextWidth(paint, language, origTextSize); 9234112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka textSize = origTextSize * Math.min(width / textWidth, 1.0f); 9244112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka useShortName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME) 9254112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka || (textWidth > width); 9264112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } else { 9274112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka useShortName = false; 9284112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 9294112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 9304112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka if (useShortName) { 931cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka language = StringUtils.getShortDisplayLanguage(locale); 932bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka textWidth = getTextWidth(paint, language, origTextSize); 9334112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka textSize = origTextSize * Math.min(width / textWidth, 1.0f); 9344112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 9354112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka paint.setTextSize(textSize); 9364112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 9374112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka return language; 9384112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 9394112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 940bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private void drawSpacebar(Key key, Canvas canvas, Paint paint) { 941bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka final int width = key.mWidth; 942b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka final int height = key.mHeight; 9434112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 944aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka // If input subtypes are explicitly selected. 945bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka if (mNeedsToDisplayLanguage) { 946bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka final String language = layoutLanguageOnSpacebar(paint, mSpacebarLocale, width, 947bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka mSpacebarTextSize); 9484112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // Draw language text with shadow 9494112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // In case there is no space icon, we will place the language text at the center of 9504112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // spacebar. 9514112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final float descent = paint.descent(); 9524112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final float textHeight = -paint.ascent() + descent; 953b19a6b9fc55910bd241bee3b312169a818cb721dTadashi G. Takaoka final float baseline = height / 2 + textHeight / 2; 954aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka paint.setColor(mSpacebarTextShadowColor); 95531c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka paint.setAlpha(mLanguageOnSpacebarAnimAlpha); 9564112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka canvas.drawText(language, width / 2, baseline - descent - 1, paint); 957aee5f03d6ebf9cb03b52cbea003556f38745b4feTadashi G. Takaoka paint.setColor(mSpacebarTextColor); 95831c94cea82f1788e3a04f2a1e012945f35497f0aTadashi G. Takaoka paint.setAlpha(mLanguageOnSpacebarAnimAlpha); 9594112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka canvas.drawText(language, width / 2, baseline - descent, paint); 9604112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 9614112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 9624112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // Draw the spacebar icon at the bottom 963bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka if (mAutoCorrectionSpacebarLedOn) { 9644112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100; 9654112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight(); 9664112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka int x = (width - iconWidth) / 2; 9674112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka int y = height - iconHeight; 968bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight); 9694112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } else if (mSpaceIcon != null) { 9704112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final int iconWidth = mSpaceIcon.getIntrinsicWidth(); 9714112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final int iconHeight = mSpaceIcon.getIntrinsicHeight(); 9724112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka int x = (width - iconWidth) / 2; 9734112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka int y = height - iconHeight; 974bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka drawIcon(canvas, mSpaceIcon, x, y, iconWidth, iconHeight); 9754112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 9764112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 9775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka} 978