MainKeyboardView.java revision bd93eddb52816acedd5242864e467781d4adfd71
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 195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.Context; 205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.pm.PackageManager; 215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.content.res.Resources; 224112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.content.res.TypedArray; 236dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport android.graphics.Canvas; 244112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport android.graphics.Color; 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.GestureDetector; 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; 395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport android.view.accessibility.AccessibilityEvent; 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; 446dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.deprecated.VoiceProxy; 45f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; 462321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaokaimport com.android.inputmethod.keyboard.PointerTracker.TimerProxy; 476dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinIME; 4815d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.LatinImeLogger; 495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.R; 505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport com.android.inputmethod.latin.StaticInnerHandlerWrapper; 516dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaokaimport com.android.inputmethod.latin.Utils; 5215d4793911fa305e0a58aced925961e948582979satokimport com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils; 535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 544112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaokaimport java.util.Locale; 555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaokaimport java.util.WeakHashMap; 565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka/** 585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * A view that is responsible for detecting key presses and touch movements. 595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * 605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_keyHysteresisDistance 615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_verticalCorrection 625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @attr ref R.styleable#KeyboardView_popupLayout 635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */ 64c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaokapublic class LatinKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler, 65c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka SuddenJumpingTouchEventHandler.ProcessMotionEvent { 66c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka private static final String TAG = LatinKeyboardView.class.getSimpleName(); 675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka private static final boolean ENABLE_CAPSLOCK_BY_DOUBLETAP = true; 695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 70bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka // TODO: Kill process when the usability study mode was changed. 71bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy; 72bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka 73bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka /** Listener for {@link KeyboardActionListener}. */ 74bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private KeyboardActionListener mKeyboardActionListener; 75bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka 76bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka /* Space key and its icons */ 774112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka private Key mSpaceKey; 784112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka private Drawable mSpaceIcon; 79bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka // Stuff to draw language name on spacebar. 80bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private boolean mNeedsToDisplayLanguage; 81bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private Locale mSpacebarLocale; 82bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private float mSpacebarTextFadeFactor = 0.0f; 834112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka private final float mSpacebarTextRatio; 844112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka private float mSpacebarTextSize; 854112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka private final int mSpacebarTextColor; 864112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka private final int mSpacebarTextShadowColor; 874112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // Height in space key the language name will be drawn. (proportional to space key height) 88bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f; 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 985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Mini keyboard 999d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka private PopupWindow mMoreKeysWindow; 1009d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka private MoreKeysPanel mMoreKeysPanel; 1019d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka private int mMoreKeysPanelPointerTrackerId; 1029d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka private final WeakHashMap<Key, MoreKeysPanel> mMoreKeysPanelCache = 1039d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka new WeakHashMap<Key, MoreKeysPanel>(); 104bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private final boolean mConfigShowMiniKeyboardAtTouchedPoint; 1055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 106bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private final boolean mIsSpacebarTriggeringPopupByLongPress; 107bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private final SuddenJumpingTouchEventHandler mTouchScreenRegulator; 1085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 109bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka protected KeyDetector mKeyDetector; 11006b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka private boolean mHasDistinctMultitouch; 1115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka private int mOldPointerCount = 1; 112e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka private Key mOldKey; 1135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 114c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka // To detect double tap. 1155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka protected GestureDetector mGestureDetector; 1165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 117f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka private final KeyTimerHandler mKeyTimerHandler = new KeyTimerHandler(this); 1185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 119c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka private static class KeyTimerHandler extends StaticInnerHandlerWrapper<LatinKeyboardView> 1202321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka implements TimerProxy { 121f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka private static final int MSG_REPEAT_KEY = 1; 122f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka private static final int MSG_LONGPRESS_KEY = 2; 12398b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka private static final int MSG_IGNORE_DOUBLE_TAP = 3; 12493246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka private static final int MSG_KEY_TYPED = 4; 1255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 126bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private final int mKeyRepeatInterval; 1275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka private boolean mInKeyRepeat; 1285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 129c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka public KeyTimerHandler(LatinKeyboardView outerInstance) { 1305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka super(outerInstance); 131bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka // TODO: This should be the attribute of LatinKeyboardView. 132bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka final Resources res = outerInstance.getContext().getResources(); 133bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval); 1345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 1365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka @Override 1375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void handleMessage(Message msg) { 138c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka final LatinKeyboardView keyboardView = getOuterInstance(); 1395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final PointerTracker tracker = (PointerTracker) msg.obj; 1405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka switch (msg.what) { 1415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka case MSG_REPEAT_KEY: 142e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka tracker.onRepeatKey(tracker.getKey()); 143bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka startKeyRepeatTimer(mKeyRepeatInterval, tracker); 1445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka break; 1455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka case MSG_LONGPRESS_KEY: 146e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka keyboardView.openMiniKeyboardIfRequired(tracker.getKey(), tracker); 1475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka break; 1485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 1512321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka @Override 152e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka public void startKeyRepeatTimer(long delay, PointerTracker tracker) { 1535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka mInKeyRepeat = true; 154e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, tracker), delay); 1555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 1575f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void cancelKeyRepeatTimer() { 1585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka mInKeyRepeat = false; 1595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka removeMessages(MSG_REPEAT_KEY); 1605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 1625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public boolean isInKeyRepeat() { 1635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return mInKeyRepeat; 1645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 1662321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka @Override 167e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka public void startLongPressTimer(long delay, PointerTracker tracker) { 16898b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka cancelLongPressTimer(); 169e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, tracker), delay); 1705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 1722321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka @Override 17398b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka public void cancelLongPressTimer() { 1745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka removeMessages(MSG_LONGPRESS_KEY); 1755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 1772321caa1f9eb6c2d616bc36f11f5b48eebf144feTadashi G. Takaoka @Override 17893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka public void startKeyTypedTimer(long delay) { 17993246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka removeMessages(MSG_KEY_TYPED); 18093246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka sendMessageDelayed(obtainMessage(MSG_KEY_TYPED), delay); 18193246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka } 18293246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka 18393246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka @Override 184c1f7d39b4aabe71ecf7934272a848d8c0fe5a7f0Tadashi G. Takaoka public boolean isTyping() { 18593246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka return hasMessages(MSG_KEY_TYPED); 18693246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka } 18793246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka 18893246652638f423d5220449f65495dea0639c750Tadashi G. Takaoka @Override 1895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void cancelKeyTimers() { 1905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka cancelKeyRepeatTimer(); 19198b5c982b93cbfc74b221af30079ecb69dd4e0a1Tadashi G. Takaoka cancelLongPressTimer(); 1925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka removeMessages(MSG_IGNORE_DOUBLE_TAP); 1935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 1955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void startIgnoringDoubleTap() { 1965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka sendMessageDelayed(obtainMessage(MSG_IGNORE_DOUBLE_TAP), 1975f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka ViewConfiguration.getDoubleTapTimeout()); 1985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 1995f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 2005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public boolean isIgnoringDoubleTap() { 2015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return hasMessages(MSG_IGNORE_DOUBLE_TAP); 2025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 2035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 2045f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void cancelAllMessages() { 2055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka cancelKeyTimers(); 2065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 2075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 2085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 209bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka class DoubleTapListener extends GestureDetector.SimpleOnGestureListener { 210c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka private boolean mProcessingShiftDoubleTapEvent = false; 211c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka 212c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka @Override 213c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka public boolean onDoubleTap(MotionEvent firstDown) { 214c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka final Keyboard keyboard = getKeyboard(); 2153708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka if (ENABLE_CAPSLOCK_BY_DOUBLETAP && keyboard.mId.isAlphabetKeyboard()) { 216c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka final int pointerIndex = firstDown.getActionIndex(); 217c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka final int id = firstDown.getPointerId(pointerIndex); 218e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka final PointerTracker tracker = PointerTracker.getPointerTracker( 219e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka id, LatinKeyboardView.this); 220e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka final Key key = tracker.getKeyOn((int)firstDown.getX(), (int)firstDown.getY()); 221c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka // If the first down event is on shift key. 222e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka if (key != null && key.isShift()) { 223c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka mProcessingShiftDoubleTapEvent = true; 224c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka return true; 225c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka } 226c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka } 227c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka mProcessingShiftDoubleTapEvent = false; 228c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka return false; 229c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka } 230c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka 231c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka @Override 232c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka public boolean onDoubleTapEvent(MotionEvent secondTap) { 233c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka if (mProcessingShiftDoubleTapEvent 234c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka && secondTap.getAction() == MotionEvent.ACTION_DOWN) { 235c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka final MotionEvent secondDown = secondTap; 236c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka final int pointerIndex = secondDown.getActionIndex(); 237c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka final int id = secondDown.getPointerId(pointerIndex); 238e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka final PointerTracker tracker = PointerTracker.getPointerTracker( 239e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka id, LatinKeyboardView.this); 240e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka final Key key = tracker.getKeyOn((int)secondDown.getX(), (int)secondDown.getY()); 241c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka // If the second down event is also on shift key. 242e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka if (key != null && key.isShift()) { 243c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka // Detected a double tap on shift key. If we are in the ignoring double tap 244c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka // mode, it means we have already turned off caps lock in 245c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka // {@link KeyboardSwitcher#onReleaseShift} . 2462ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka onDoubleTapShiftKey(mKeyTimerHandler.isIgnoringDoubleTap()); 247c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka return true; 248c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka } 249c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka // Otherwise these events should not be handled as double tap. 250c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka mProcessingShiftDoubleTapEvent = false; 251c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka } 252c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka return mProcessingShiftDoubleTapEvent; 253c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka } 254c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka } 255c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka 256c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka public LatinKeyboardView(Context context, AttributeSet attrs) { 2575afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka this(context, attrs, R.attr.latinKeyboardViewStyle); 2585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 2595f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 260c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) { 2615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka super(context, attrs, defStyle); 2625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 263c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this); 264c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka 2655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final Resources res = getResources(); 266bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka // TODO: This should be the attribute of LatinKeyboardView. 267f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka mConfigShowMiniKeyboardAtTouchedPoint = res.getBoolean( 268f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka R.bool.config_show_mini_keyboard_at_touched_point); 269bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka // TODO: This should be the attribute of LatinKeyboardView. 270a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka final float keyHysteresisDistance = res.getDimension(R.dimen.key_hysteresis_distance); 271a19b84dcf65bd70caa0fc72089cfe043b023a898Tadashi G. Takaoka mKeyDetector = new KeyDetector(keyHysteresisDistance); 2725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 2735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final boolean ignoreMultitouch = true; 274c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka mGestureDetector = new GestureDetector( 275c71854a6614d1945739dcf40db61b0e887442b67Tadashi G. Takaoka getContext(), new DoubleTapListener(), null, ignoreMultitouch); 2765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka mGestureDetector.setIsLongpressEnabled(false); 2775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 2785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka mHasDistinctMultitouch = context.getPackageManager() 2795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT); 280906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka 2815c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka PointerTracker.init(mHasDistinctMultitouch, getContext()); 28222b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka 283bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka // TODO: This should be the attribute of LatinKeyboardView. 28422b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka final int longPressSpaceKeyTimeout = 28522b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka res.getInteger(R.integer.config_long_press_space_key_timeout); 28622b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka mIsSpacebarTriggeringPopupByLongPress = (longPressSpaceKeyTimeout > 0); 2874112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 2884112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final TypedArray a = context.obtainStyledAttributes( 2894112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka attrs, R.styleable.LatinKeyboardView, defStyle, R.style.LatinKeyboardView); 2904112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mAutoCorrectionSpacebarLedEnabled = a.getBoolean( 2914112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedEnabled, false); 2924112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mAutoCorrectionSpacebarLedIcon = a.getDrawable( 2934112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedIcon); 2944112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mSpacebarTextRatio = a.getFraction(R.styleable.LatinKeyboardView_spacebarTextRatio, 2954112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 1000, 1000, 1) / 1000.0f; 2964112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboardView_spacebarTextColor, 0); 2974112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mSpacebarTextShadowColor = a.getColor( 2984112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka R.styleable.LatinKeyboardView_spacebarTextShadowColor, 0); 2994112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka a.recycle(); 3005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 3015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 3025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void startIgnoringDoubleTap() { 3035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (ENABLE_CAPSLOCK_BY_DOUBLETAP) 304f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka mKeyTimerHandler.startIgnoringDoubleTap(); 3055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 3065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 3075a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka public void setKeyboardActionListener(KeyboardActionListener listener) { 3085f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka mKeyboardActionListener = listener; 3095c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka PointerTracker.setKeyboardActionListener(listener); 3105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 3115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 3125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka /** 3135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Returns the {@link KeyboardActionListener} object. 3145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @return the listener attached to this keyboard 3155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */ 3160efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka @Override 3170efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka public KeyboardActionListener getKeyboardActionListener() { 3185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return mKeyboardActionListener; 3195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 3205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 321bb4be5444b845655c0eb80bcfbb66f93603802eaTadashi G. Takaoka @Override 3220efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka public KeyDetector getKeyDetector() { 3230efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka return mKeyDetector; 3240efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka } 3250efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka 3260efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka @Override 327f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka public DrawingProxy getDrawingProxy() { 328f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka return this; 329f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka } 330f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka 331f426cdd5c62452224ac4bb833c3ccf7b26d1a2a8Tadashi G. Takaoka @Override 3320efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka public TimerProxy getTimerProxy() { 3330efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka return mKeyTimerHandler; 3340efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka } 3350efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka 3365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka /** 3375f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Attaches a keyboard to this view. The keyboard can be switched at any time and the 3385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * view will re-layout itself to accommodate the keyboard. 3395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @see Keyboard 3405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @see #getKeyboard() 3415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @param keyboard the keyboard to display in this view 3425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */ 3435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka @Override 3445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void setKeyboard(Keyboard keyboard) { 3455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Remove any pending messages, except dismissing preview 346f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka mKeyTimerHandler.cancelKeyTimers(); 3475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka super.setKeyboard(keyboard); 3485a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka mKeyDetector.setKeyboard( 3495a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection); 3508da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka mKeyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth); 3515c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka PointerTracker.setKeyDetector(mKeyDetector); 352c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka mTouchScreenRegulator.setKeyboard(keyboard); 3539d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysPanelCache.clear(); 3544112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 3554112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE); 35642fcb2de641c4cd5d57f34889c8752401e35dcc8Tadashi G. Takaoka mSpaceIcon = keyboard.mIconsSet.getIconByAttrId(R.styleable.Keyboard_iconSpaceKey); 3574112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; 3584112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mSpacebarTextSize = keyHeight * mSpacebarTextRatio; 3594112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mSpacebarLocale = keyboard.mId.mLocale; 3605f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 3615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 3625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka /** 3635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Returns whether the device has distinct multi-touch panel. 3645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @return true if the device has distinct multi-touch panel. 3655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */ 3665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public boolean hasDistinctMultitouch() { 3675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return mHasDistinctMultitouch; 3685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 3695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 37006b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka public void setDistinctMultitouch(boolean hasDistinctMultitouch) { 37106b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka mHasDistinctMultitouch = hasDistinctMultitouch; 37206b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka } 37306b7c256b1992f93aab0e2cdb90f57718f0631fdTadashi G. Takaoka 3745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka /** 3755f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * When enabled, calls to {@link KeyboardActionListener#onCodeInput} will include key 3765f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * codes for adjacent keys. When disabled, only the primary key code will be 3775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * reported. 3785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @param enabled whether or not the proximity correction is enabled 3795f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */ 3805f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void setProximityCorrectionEnabled(boolean enabled) { 3815f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka mKeyDetector.setProximityCorrectionEnabled(enabled); 3825f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 3835f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 3845f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka /** 3855f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Returns true if proximity correction is enabled. 3865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */ 3875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public boolean isProximityCorrectionEnabled() { 3885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return mKeyDetector.isProximityCorrectionEnabled(); 3895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 3905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 3915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka @Override 3925f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void cancelAllMessages() { 393f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka mKeyTimerHandler.cancelAllMessages(); 3945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka super.cancelAllMessages(); 3955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 3965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 397e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka private boolean openMiniKeyboardIfRequired(Key parentKey, PointerTracker tracker) { 3985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Check if we have a popup layout specified first. 3999d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysLayout == 0) { 4005f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return false; 4015f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 4025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 40363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka // Check if we are already displaying popup panel. 4049d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysPanel != null) 40563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka return false; 4065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (parentKey == null) 4075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return false; 40863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka return onLongPress(parentKey, tracker); 4095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 4105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 4112ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka private void onDoubleTapShiftKey(final boolean ignore) { 4125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // When shift key is double tapped, the first tap is correctly processed as usual tap. And 4135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // the second tap is treated as this double tap event, so that we need not mark tracker 414906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka // calling setAlreadyProcessed() nor remove the tracker from mPointerQueue. 415d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka if (ignore) { 4162a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka invokeCustomRequest(LatinIME.CODE_HAPTIC_AND_AUDIO_FEEDBACK); 417d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka } else { 4182a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka invokeCodeInput(Keyboard.CODE_CAPSLOCK); 419d999ea44805ae0a3ccac4c4f49aaf500f6c479acTadashi G. Takaoka } 4205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 4215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 4229d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka // This default implementation returns a more keys panel. 4239d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka protected MoreKeysPanel onCreateMoreKeysPanel(Key parentKey) { 4249d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (parentKey.mMoreKeys == null) 4255f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return null; 4265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 4279d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka final View container = LayoutInflater.from(getContext()).inflate(mMoreKeysLayout, null); 4285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (container == null) 4295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka throw new NullPointerException(); 4305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 4319237a72634be821c22911633ef0848130e162d58Tadashi G. Takaoka final MiniKeyboardView miniKeyboardView = 4329237a72634be821c22911633ef0848130e162d58Tadashi G. Takaoka (MiniKeyboardView)container.findViewById(R.id.mini_keyboard_view); 4335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final Keyboard parentKeyboard = getKeyboard(); 43432572948d7e3956efebcbd69d7c7d8403bb659e6Tadashi G. Takaoka final Keyboard miniKeyboard = new MiniKeyboard.Builder( 4359d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka this, parentKeyboard.mMoreKeysTemplate, parentKey, parentKeyboard).build(); 4365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka miniKeyboardView.setKeyboard(miniKeyboard); 437b8dc67466339dc14653ad634c86851025373326bTadashi G. Takaoka container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 4385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 4395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return miniKeyboardView; 4405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 4415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 4425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka /** 4435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * Called when a key is long pressed. By default this will open mini keyboard associated 4445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * with this key. 4455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @param parentKey the key that was long pressed 4465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @param tracker the pointer tracker which pressed the parent key 4475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * @return true if the long press is handled, false otherwise. Subclasses should call the 4485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka * method on the base class if the subclass doesn't wish to handle the call. 4495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka */ 4505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka protected boolean onLongPress(Key parentKey, PointerTracker tracker) { 4516dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka final int primaryCode = parentKey.mCode; 4526dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka final Keyboard keyboard = getKeyboard(); 4533708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka if (primaryCode == Keyboard.CODE_DIGIT0 && keyboard.mId.isPhoneKeyboard()) { 4543708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka tracker.onLongPressed(); 4553708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka // Long pressing on 0 in phone number keypad gives you a '+'. 4563708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka invokeCodeInput(Keyboard.CODE_PLUS); 4573708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka invokeReleaseKey(primaryCode); 4583708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka return true; 4593708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka } 4603708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka if (primaryCode == Keyboard.CODE_SHIFT && keyboard.mId.isAlphabetKeyboard()) { 4613708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka tracker.onLongPressed(); 4623708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka invokeCodeInput(Keyboard.CODE_CAPSLOCK); 4633708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka invokeReleaseKey(primaryCode); 4643708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka return true; 4653708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka } 4663708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka if (primaryCode == Keyboard.CODE_SPACE) { 4673708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka // Long pressing the space key invokes IME switcher dialog. 4683708787fe91227083d2a1874fa41493d3bc9fe10Tadashi G. Takaoka if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) { 4696dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka tracker.onLongPressed(); 47042e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka invokeReleaseKey(primaryCode); 47142e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka return true; 4726dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 4736dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 47417dc10724bf0db04d0a4bfb2b8be0739ad9e60c6Tadashi G. Takaoka return openMoreKeysPanel(parentKey, tracker); 4756dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 4766dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka 47742e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka private boolean invokeCustomRequest(int code) { 4782a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka return mKeyboardActionListener.onCustomRequest(code); 47942e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka } 48042e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka 48142e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka private void invokeCodeInput(int primaryCode) { 4822a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka mKeyboardActionListener.onCodeInput(primaryCode, null, 4836dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka KeyboardActionListener.NOT_A_TOUCH_COORDINATE, 4846dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka KeyboardActionListener.NOT_A_TOUCH_COORDINATE); 48542e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka } 48642e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka 48742e8c64a042476f555da5015558d51f96aaeb7fdTadashi G. Takaoka private void invokeReleaseKey(int primaryCode) { 4882a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka mKeyboardActionListener.onReleaseKey(primaryCode, false); 4896dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 4906dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka 4919d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka private boolean openMoreKeysPanel(Key parentKey, PointerTracker tracker) { 4929d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka MoreKeysPanel moreKeysPanel = mMoreKeysPanelCache.get(parentKey); 4939d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (moreKeysPanel == null) { 4949d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka moreKeysPanel = onCreateMoreKeysPanel(parentKey); 4959d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (moreKeysPanel == null) 4965f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return false; 4979d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysPanelCache.put(parentKey, moreKeysPanel); 4985f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 4999d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysWindow == null) { 5009d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysWindow = new PopupWindow(getContext()); 5019d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysWindow.setBackgroundDrawable(null); 5029d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysWindow.setAnimationStyle(R.style.MiniKeyboardAnimation); 5035f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 5049d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysPanel = moreKeysPanel; 5059d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysPanelPointerTrackerId = tracker.mPointerId; 50663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka 507f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka final Keyboard keyboard = getKeyboard(); 5089d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka moreKeysPanel.setShifted(keyboard.isShiftedOrShiftLocked()); 509f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka final int pointX = (mConfigShowMiniKeyboardAtTouchedPoint) ? tracker.getLastX() 510f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka : parentKey.mX + parentKey.mWidth / 2; 511f44a01b40852dde2363a061cdc7df2ef4cb59aadTadashi G. Takaoka final int pointY = parentKey.mY - keyboard.mVerticalGap; 5129d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka moreKeysPanel.showMoreKeysPanel( 5132a88440419f49d100c73e067a823390f64aba3b1Tadashi G. Takaoka this, this, pointX, pointY, mMoreKeysWindow, mKeyboardActionListener); 5149d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka final int translatedX = moreKeysPanel.translateX(tracker.getLastX()); 5159d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka final int translatedY = moreKeysPanel.translateY(tracker.getLastY()); 516e51d164482c7896892d6eccb80f1e1e6fe6d50dbTadashi G. Takaoka tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel); 5171b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka dimEntireKeyboard(true); 5185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return true; 5195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 5205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 5215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public boolean isInSlidingKeyInput() { 5229d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysPanel != null) { 52363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka return true; 52463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka } else { 5255c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka return PointerTracker.isAnyInSlidingKeyInput(); 5265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 5275f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 5285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 5295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public int getPointerCount() { 5305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return mOldPointerCount; 5315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 5325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 5335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka @Override 5345f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public boolean onTouchEvent(MotionEvent me) { 53546286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka if (getKeyboard() == null) { 53646286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka return false; 53746286874f30c4a6ef44646c4e4adf36fe55c74b9Tadashi G. Takaoka } 538c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka return mTouchScreenRegulator.onTouchEvent(me); 539c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka } 540c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka 541c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka @Override 542c403a46f6d787b79768895272d53d296100677ddTadashi G. Takaoka public boolean processMotionEvent(MotionEvent me) { 543f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka final boolean nonDistinctMultitouch = !mHasDistinctMultitouch; 5445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final int action = me.getActionMasked(); 5455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final int pointerCount = me.getPointerCount(); 5465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final int oldPointerCount = mOldPointerCount; 5475f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka mOldPointerCount = pointerCount; 5485f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 5495f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // TODO: cleanup this code into a multi-touch to single-touch event converter class? 5505f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // If the device does not have distinct multi-touch support panel, ignore all multi-touch 5515f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // events except a transition from/to single-touch. 552f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka if (nonDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) { 5535f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return true; 5545f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 5555f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 5565f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Gesture detector must be enabled only when mini-keyboard is not on the screen. 5579d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysPanel == null && mGestureDetector != null 5585f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka && mGestureDetector.onTouchEvent(me)) { 5595c73ed628b22fdfa59585803ee86e383c579a7d4Tadashi G. Takaoka PointerTracker.dismissAllKeyPreviews(); 560f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka mKeyTimerHandler.cancelKeyTimers(); 5615f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return true; 5625f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 5635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 5645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final long eventTime = me.getEventTime(); 5655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final int index = me.getActionIndex(); 5665f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final int id = me.getPointerId(index); 56763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka final int x, y; 5689d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysPanel != null && id == mMoreKeysPanelPointerTrackerId) { 5699d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka x = mMoreKeysPanel.translateX((int)me.getX(index)); 5709d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka y = mMoreKeysPanel.translateY((int)me.getY(index)); 57163c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka } else { 57263c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka x = (int)me.getX(index); 57363c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka y = (int)me.getY(index); 5745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 57515d4793911fa305e0a58aced925961e948582979satok if (ENABLE_USABILITY_STUDY_LOG) { 57615d4793911fa305e0a58aced925961e948582979satok final String eventTag; 57715d4793911fa305e0a58aced925961e948582979satok switch (action) { 57815d4793911fa305e0a58aced925961e948582979satok case MotionEvent.ACTION_UP: 57915d4793911fa305e0a58aced925961e948582979satok eventTag = "[Up]"; 58015d4793911fa305e0a58aced925961e948582979satok break; 58115d4793911fa305e0a58aced925961e948582979satok case MotionEvent.ACTION_DOWN: 58215d4793911fa305e0a58aced925961e948582979satok eventTag = "[Down]"; 58315d4793911fa305e0a58aced925961e948582979satok break; 58415d4793911fa305e0a58aced925961e948582979satok case MotionEvent.ACTION_POINTER_UP: 58515d4793911fa305e0a58aced925961e948582979satok eventTag = "[PointerUp]"; 58615d4793911fa305e0a58aced925961e948582979satok break; 58715d4793911fa305e0a58aced925961e948582979satok case MotionEvent.ACTION_POINTER_DOWN: 58815d4793911fa305e0a58aced925961e948582979satok eventTag = "[PointerDown]"; 58915d4793911fa305e0a58aced925961e948582979satok break; 5903425852be4229b8937c3fd9a82d709d9bd8c4b4eKen Wakasa case MotionEvent.ACTION_MOVE: // Skip this as being logged below 5913425852be4229b8937c3fd9a82d709d9bd8c4b4eKen Wakasa eventTag = ""; 5923425852be4229b8937c3fd9a82d709d9bd8c4b4eKen Wakasa break; 59315d4793911fa305e0a58aced925961e948582979satok default: 59415d4793911fa305e0a58aced925961e948582979satok eventTag = "[Action" + action + "]"; 59515d4793911fa305e0a58aced925961e948582979satok break; 59615d4793911fa305e0a58aced925961e948582979satok } 59715d4793911fa305e0a58aced925961e948582979satok if (!TextUtils.isEmpty(eventTag)) { 59815d4793911fa305e0a58aced925961e948582979satok UsabilityStudyLogUtils.getInstance().write( 59915d4793911fa305e0a58aced925961e948582979satok eventTag + eventTime + "," + id + "," + x + "," + y + "\t\t"); 60015d4793911fa305e0a58aced925961e948582979satok } 60115d4793911fa305e0a58aced925961e948582979satok } 6025f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 603f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka if (mKeyTimerHandler.isInKeyRepeat()) { 604e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka final PointerTracker tracker = PointerTracker.getPointerTracker(id, this); 6055f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Key repeating timer will be canceled if 2 or more keys are in action, and current 6065f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // event (UP or DOWN) is non-modifier key. 6075f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (pointerCount > 1 && !tracker.isModifier()) { 608f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka mKeyTimerHandler.cancelKeyRepeatTimer(); 6095f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6105f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Up event will pass through. 6115f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6125f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 6135f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // TODO: cleanup this code into a multi-touch to single-touch event converter class? 6145f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Translate mutli-touch event to single-touch events on the device that has no distinct 6155f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // multi-touch panel. 616f60d09ac3086f308cafcee13ebcb94c562f9e58eTadashi G. Takaoka if (nonDistinctMultitouch) { 6175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Use only main (id=0) pointer tracker. 618e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka final PointerTracker tracker = PointerTracker.getPointerTracker(0, this); 6195f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (pointerCount == 1 && oldPointerCount == 2) { 6205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Multi-touch to single touch transition. 6215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Send a down event for the latest pointer if the key is different from the 6225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // previous key. 623e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka final Key newKey = tracker.getKeyOn(x, y); 624e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka if (mOldKey != newKey) { 6250efe174ea43fe576683102effbaef5be27575706Tadashi G. Takaoka tracker.onDownEvent(x, y, eventTime, this); 6265f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (action == MotionEvent.ACTION_UP) 627906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka tracker.onUpEvent(x, y, eventTime); 6285f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6295f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } else if (pointerCount == 2 && oldPointerCount == 1) { 6305f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Single-touch to multi-touch transition. 6315f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka // Send an up event for the last pointer. 6325f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final int lastX = tracker.getLastX(); 6335f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka final int lastY = tracker.getLastY(); 634e22baaadd314c80f835e2e96fb0dfc73838ac2cdTadashi G. Takaoka mOldKey = tracker.getKeyOn(lastX, lastY); 635906f03121b6c6a795f35dbc24d2eceac0665f35fTadashi G. Takaoka tracker.onUpEvent(lastX, lastY, eventTime); 6365f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } else if (pointerCount == 1 && oldPointerCount == 1) { 6378ac6d505b7ceab020a4085b3dfbea5b47362b030Tadashi G. Takaoka tracker.processMotionEvent(action, x, y, eventTime, this); 6385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } else { 6395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount 6405f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka + " (old " + oldPointerCount + ")"); 6415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return true; 6435f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6445f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 6455f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (action == MotionEvent.ACTION_MOVE) { 6465f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka for (int i = 0; i < pointerCount; i++) { 647e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka final PointerTracker tracker = PointerTracker.getPointerTracker( 648e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka me.getPointerId(i), this); 64963c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka final int px, py; 6509d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysPanel != null 6519d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka && tracker.mPointerId == mMoreKeysPanelPointerTrackerId) { 6529d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka px = mMoreKeysPanel.translateX((int)me.getX(i)); 6539d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka py = mMoreKeysPanel.translateY((int)me.getY(i)); 65463c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka } else { 65563c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka px = (int)me.getX(i); 65663c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka py = (int)me.getY(i); 65763c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka } 65863c233ab9f50d844be6e52e382c6664475606760Tadashi G. Takaoka tracker.onMoveEvent(px, py, eventTime); 65915d4793911fa305e0a58aced925961e948582979satok if (ENABLE_USABILITY_STUDY_LOG) { 66015d4793911fa305e0a58aced925961e948582979satok UsabilityStudyLogUtils.getInstance().write("[Move]" + eventTime + "," 66115d4793911fa305e0a58aced925961e948582979satok + me.getPointerId(i) + "," + px + "," + py + "\t\t"); 66215d4793911fa305e0a58aced925961e948582979satok } 6635f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6645f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } else { 665e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka final PointerTracker tracker = PointerTracker.getPointerTracker(id, this); 666e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka tracker.processMotionEvent(action, x, y, eventTime, this); 6675f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6685f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 6695f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return true; 6705f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6715f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 6725f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka @Override 6735f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public void closing() { 6745f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka super.closing(); 6759d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka dismissMoreKeysPanel(); 6769d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysPanelCache.clear(); 6775f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6785f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 6799ec80d9d89eb599329c354451acdc482cc3de836Tadashi G. Takaoka @Override 6809d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka public boolean dismissMoreKeysPanel() { 6819d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka if (mMoreKeysWindow != null && mMoreKeysWindow.isShowing()) { 6829d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysWindow.dismiss(); 6839d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysPanel = null; 6849d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka mMoreKeysPanelPointerTrackerId = -1; 6851b087064c07975c5e2b9c17d4ca80c56e01c35c0Tadashi G. Takaoka dimEntireKeyboard(false); 6865f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return true; 6875f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6885f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return false; 6895f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6905f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 6915f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public boolean handleBack() { 6929d5601e9013c5ec9a7ac75db16f4a0a8218b02bfTadashi G. Takaoka return dismissMoreKeysPanel(); 6935f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 6945f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 6955f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka @Override 6966dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka public void draw(Canvas c) { 6976dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka Utils.GCUtils.getInstance().reset(); 6986dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka boolean tryGC = true; 6996dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { 7006dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka try { 7016dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka super.draw(c); 7026dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka tryGC = false; 7036dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } catch (OutOfMemoryError e) { 7046dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka tryGC = Utils.GCUtils.getInstance().tryGCOrWait("LatinKeyboardView", e); 7056dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 7066dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 7076dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 7086dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka 7096dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka @Override 7106dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka protected void onAttachedToWindow() { 7116dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka // Token is available from here. 7126dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka VoiceProxy.getInstance().onAttachedToWindow(); 7136dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka } 7146dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka 7156dde878d515f7bf5268d16a8fe4921d8821c5ae7Tadashi G. Takaoka @Override 7165f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 7175f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { 7185f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return AccessibleKeyboardViewProxy.getInstance().dispatchPopulateAccessibilityEvent( 7192ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka event) || super.dispatchPopulateAccessibilityEvent(event); 7205f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7215f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 7225f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return super.dispatchPopulateAccessibilityEvent(event); 7235f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7245f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 725586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette /** 726586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * Receives hover events from the input framework. This method overrides 727586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * View.dispatchHoverEvent(MotionEvent) on SDK version ICS or higher. On 728586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * lower SDK versions, this method is never called. 729586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * 730586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * @param event The motion event to be dispatched. 731586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * @return {@code true} if the event was handled by the view, {@code false} 732586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette * otherwise 733586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette */ 734586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette public boolean dispatchHoverEvent(MotionEvent event) { 7355f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { 736e88e1b22c87a075554fb3f10cee492e169570958Tadashi G. Takaoka final PointerTracker tracker = PointerTracker.getPointerTracker(0, this); 737586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker); 7385f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 7395f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka 740586a15c3f0d44590a5162e0ab4c3c52511f13f26Alan Viverette // Reflection doesn't support calling superclass methods. 7415f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka return false; 7425f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka } 74322b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka 7445afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka public void updateShortcutKey(boolean available) { 7457bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka final Keyboard keyboard = getKeyboard(); 7467bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka if (keyboard == null) return; 7477bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka final Key shortcutKey = keyboard.getKey(Keyboard.CODE_SHORTCUT); 7487bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka if (shortcutKey == null) return; 7497bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka shortcutKey.setEnabled(available); 7507bd714c086a78e2058543b0971ac92f5a30b2362Tadashi G. Takaoka invalidateKey(shortcutKey); 7515afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka } 7525afc3ae2d9df0c2c93f2c66af13b128889ac3b5dTadashi G. Takaoka 7534112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka public void updateSpacebar(float fadeFactor, boolean needsToDisplayLanguage) { 7544112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mSpacebarTextFadeFactor = fadeFactor; 7554112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mNeedsToDisplayLanguage = needsToDisplayLanguage; 7564112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka invalidateKey(mSpaceKey); 7574112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 7584112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 7594112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka public void updateAutoCorrectionState(boolean isAutoCorrection) { 7604112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka if (!mAutoCorrectionSpacebarLedEnabled) return; 7614112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka mAutoCorrectionSpacebarLedOn = isAutoCorrection; 7624112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka invalidateKey(mSpaceKey); 76322b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka } 76422b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka 76522b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka @Override 766f9521c6f378e3f2aa13d9e382ae13708e3ae6317Tadashi G. Takaoka protected void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint, KeyDrawParams params) { 76722b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka super.onDrawKeyTopVisuals(key, canvas, paint, params); 76822b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka 7694112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka if (key.mCode == Keyboard.CODE_SPACE) { 770bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka drawSpacebar(key, canvas, paint); 771bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka 7724112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // Whether space key needs to show the "..." popup hint for special purposes 7734112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka if (mIsSpacebarTriggeringPopupByLongPress 7744112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka && Utils.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) { 775bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka drawKeyPopupHint(key, canvas, paint, params); 7764112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 77722b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka } 77822b48de11ce6f31a0edf90e1308073e67a7a2adbTadashi G. Takaoka } 7794112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 7804112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka private static int getSpacebarTextColor(int color, float fadeFactor) { 7814112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final int newColor = Color.argb((int)(Color.alpha(color) * fadeFactor), 7824112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka Color.red(color), Color.green(color), Color.blue(color)); 7834112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka return newColor; 7844112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 7854112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 7864112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // Compute width of text with specified text size using paint. 787bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private int getTextWidth(Paint paint, String text, float textSize) { 7884112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka paint.setTextSize(textSize); 789bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka return (int)getLabelWidth(text, paint); 7904112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 7914112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 7924015a64a263b07178b99d3e3e864bd75da017638Tadashi G. Takaoka // Layout locale language name on spacebar. 793bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private String layoutLanguageOnSpacebar(Paint paint, Locale locale, int width, 7944112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka float origTextSize) { 795bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka paint.setTextAlign(Align.CENTER); 796bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka paint.setTypeface(Typeface.DEFAULT); 7974112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // Estimate appropriate language name text size to fit in maxTextWidth. 7984112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka String language = Utils.getFullDisplayName(locale, true); 799bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka int textWidth = getTextWidth(paint, language, origTextSize); 8004112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // Assuming text width and text size are proportional to each other. 8014112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka float textSize = origTextSize * Math.min(width / textWidth, 1.0f); 8024112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // allow variable text size 803bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka textWidth = getTextWidth(paint, language, textSize); 8044112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // If text size goes too small or text does not fit, use middle or short name 8054112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final boolean useMiddleName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME) 8064112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka || (textWidth > width); 8074112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 8084112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final boolean useShortName; 8094112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka if (useMiddleName) { 8104112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka language = Utils.getMiddleDisplayLanguage(locale); 811bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka textWidth = getTextWidth(paint, language, origTextSize); 8124112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka textSize = origTextSize * Math.min(width / textWidth, 1.0f); 8134112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka useShortName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME) 8144112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka || (textWidth > width); 8154112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } else { 8164112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka useShortName = false; 8174112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 8184112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 8194112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka if (useShortName) { 8204112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka language = Utils.getShortDisplayLanguage(locale); 821bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka textWidth = getTextWidth(paint, language, origTextSize); 8224112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka textSize = origTextSize * Math.min(width / textWidth, 1.0f); 8234112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 8244112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka paint.setTextSize(textSize); 8254112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 8264112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka return language; 8274112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 8284112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 829bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka private void drawSpacebar(Key key, Canvas canvas, Paint paint) { 830bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka final int width = key.mWidth; 831bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka final int height = mSpaceIcon != null ? mSpaceIcon.getIntrinsicHeight() : key.mHeight; 8324112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 8334112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // If application locales are explicitly selected. 834bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka if (mNeedsToDisplayLanguage) { 835bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka final String language = layoutLanguageOnSpacebar(paint, mSpacebarLocale, width, 836bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka mSpacebarTextSize); 8374112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // Draw language text with shadow 8384112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // In case there is no space icon, we will place the language text at the center of 8394112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // spacebar. 8404112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final float descent = paint.descent(); 8414112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final float textHeight = -paint.ascent() + descent; 8424112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final float baseline = (mSpaceIcon != null) ? height * SPACEBAR_LANGUAGE_BASELINE 8434112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka : height / 2 + textHeight / 2; 844bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka paint.setColor(getSpacebarTextColor(mSpacebarTextShadowColor, mSpacebarTextFadeFactor)); 8454112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka canvas.drawText(language, width / 2, baseline - descent - 1, paint); 846bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka paint.setColor(getSpacebarTextColor(mSpacebarTextColor, mSpacebarTextFadeFactor)); 8474112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka canvas.drawText(language, width / 2, baseline - descent, paint); 8484112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 8494112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka 8504112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka // Draw the spacebar icon at the bottom 851bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka if (mAutoCorrectionSpacebarLedOn) { 8524112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100; 8534112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight(); 8544112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka int x = (width - iconWidth) / 2; 8554112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka int y = height - iconHeight; 856bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight); 8574112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } else if (mSpaceIcon != null) { 8584112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final int iconWidth = mSpaceIcon.getIntrinsicWidth(); 8594112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka final int iconHeight = mSpaceIcon.getIntrinsicHeight(); 8604112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka int x = (width - iconWidth) / 2; 8614112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka int y = height - iconHeight; 862bd93eddb52816acedd5242864e467781d4adfd71Tadashi G. Takaoka drawIcon(canvas, mSpaceIcon, x, y, iconWidth, iconHeight); 8634112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 8644112dc05002d7a880e558418639cf25c4bd02a5aTadashi G. Takaoka } 8655f6816fa8bf259f0340a3d12c551d1532f647d66Tadashi G. Takaoka} 866