1b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer/* 2b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Copyright (C) 2016 The Android Open Source Project 3b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * 4b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Licensed under the Apache License, Version 2.0 (the "License"); 5b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * you may not use this file except in compliance with the License. 6b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * You may obtain a copy of the License at 7b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * 8b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * http://www.apache.org/licenses/LICENSE-2.0 9b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * 10b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Unless required by applicable law or agreed to in writing, software 11b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * distributed under the License is distributed on an "AS IS" BASIS, 12b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * See the License for the specific language governing permissions and 14b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * limitations under the License. 15b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 16b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 17b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerpackage com.android.inputmethod.latin.car; 18b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 19b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.animation.Animator; 20b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.animation.ValueAnimator; 21b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.annotation.SuppressLint; 22b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.content.Context; 23b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.content.res.TypedArray; 24b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.graphics.Bitmap; 25b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.graphics.Canvas; 26b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.graphics.Color; 27b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.graphics.Paint; 28b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.graphics.Paint.Align; 29b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.graphics.PorterDuff; 30b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.graphics.Rect; 31b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.graphics.Region.Op; 32b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.graphics.Typeface; 33b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.graphics.drawable.Drawable; 34b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.inputmethodservice.Keyboard; 35b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.inputmethodservice.Keyboard.Key; 36b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.os.Handler; 37b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.os.Message; 38b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.util.AttributeSet; 39b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.util.Log; 40b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.util.TypedValue; 41b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.view.GestureDetector; 42b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.view.Gravity; 43b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.view.LayoutInflater; 44b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.view.MotionEvent; 45b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.view.View; 46b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.view.ViewConfiguration; 47b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.view.ViewGroup.LayoutParams; 48b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.view.accessibility.AccessibilityManager; 49b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.view.animation.AccelerateDecelerateInterpolator; 50b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.view.animation.LinearInterpolator; 51b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.widget.PopupWindow; 52b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport android.widget.TextView; 53b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 54b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport com.android.inputmethod.latin.R; 55b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 56b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport java.lang.ref.WeakReference; 57b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport java.util.Arrays; 58b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport java.util.HashMap; 59b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport java.util.List; 60b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport java.util.Locale; 61b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport java.util.Map; 62b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerimport java.util.regex.Pattern; 63b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 64b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer/** 65b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * A view that renders a virtual {@link android.inputmethodservice.Keyboard}. It handles rendering of keys and 66b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * detecting key presses and touch movements. 67b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * 68b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @attr ref android.R.styleable#KeyboardView_keyBackground 69b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @attr ref android.R.styleable#KeyboardView_keyPreviewLayout 70b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @attr ref android.R.styleable#KeyboardView_keyPreviewOffset 71b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @attr ref android.R.styleable#KeyboardView_labelTextSize 72b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @attr ref android.R.styleable#KeyboardView_keyTextSize 73b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @attr ref android.R.styleable#KeyboardView_keyTextColor 74b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @attr ref android.R.styleable#KeyboardView_verticalCorrection 75b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @attr ref android.R.styleable#KeyboardView_popupLayout 76b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 77b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyerpublic class KeyboardView extends View implements View.OnClickListener { 78b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 79b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 80b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Listener for virtual keyboard events. 81b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 82b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public interface OnKeyboardActionListener { 83b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 84b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 85b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Called when the user presses a key. This is sent before the {@link #onKey} is called. 86b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * For keys that repeat, this is only called once. 87b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @param primaryCode the unicode of the key being pressed. If the touch is not on a valid 88b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * key, the value will be zero. 89b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 90b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer void onPress(int primaryCode); 91b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 92b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 93b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Called when the user releases a key. This is sent after the {@link #onKey} is called. 94b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * For keys that repeat, this is only called once. 95b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @param primaryCode the code of the key that was released 96b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 97b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer void onRelease(int primaryCode); 98b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 99b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 100b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Send a key press to the listener. 101b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @param primaryCode this is the key that was pressed 102b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @param keyCodes the codes for all the possible alternative keys 103b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * with the primary code being the first. If the primary key code is 104b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * a single character such as an alphabet or number or symbol, the alternatives 105b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * will include other characters that may be on the same key or adjacent keys. 106b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * These codes are useful to correct for accidental presses of a key adjacent to 107b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * the intended key. 108b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 109b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer void onKey(int primaryCode, int[] keyCodes); 110b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 111b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 112b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Sends a sequence of characters to the listener. 113b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @param text the sequence of characters to be displayed. 114b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 115b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer void onText(CharSequence text); 116b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 117b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 118b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Called when the user quickly moves the finger from right to left. 119b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 120b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer void swipeLeft(); 121b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 122b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 123b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Called when the user quickly moves the finger from left to right. 124b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 125b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer void swipeRight(); 126b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 127b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 128b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Called when the user quickly moves the finger from up to down. 129b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 130b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer void swipeDown(); 131b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 132b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 133b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Called when the user quickly moves the finger from down to up. 134b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 135b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer void swipeUp(); 136b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 137b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 138b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Called when we want to stop keyboard input. 139b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 140b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer void stopInput(); 141b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 142b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 143b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final boolean DEBUG = false; 144b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int NOT_A_KEY = -1; 145b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int[] KEY_DELETE = { Keyboard.KEYCODE_DELETE }; 146b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int SCRIM_ALPHA = 242; // 95% opacity. 147b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int MAX_ALPHA = 255; 148b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final float MIN_ANIMATION_VALUE = 0.0f; 149b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final float MAX_ANIMATION_VALUE = 1.0f; 150b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final Pattern PUNCTUATION_PATTERN = Pattern.compile("_|-|,|\\."); 151b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer //private static final int[] LONG_PRESSABLE_STATE_SET = { R.attr.state_long_pressable }; 152b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final Context mContext; 153b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 154b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private Keyboard mKeyboard; 155b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mCurrentKeyIndex = NOT_A_KEY; 156b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mLabelTextSize; 157b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mKeyTextSize; 158b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mKeyPunctuationSize; 159b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mKeyTextColorPrimary; 160b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mKeyTextColorSecondary; 161b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private String mFontFamily; 162b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mTextStyle = Typeface.BOLD; 163b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 164b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private TextView mPreviewText; 165b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final PopupWindow mPreviewPopup; 166b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mPreviewTextSizeLarge; 167b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mPreviewOffset; 168b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mPreviewHeight; 169b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Working variable 170b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final int[] mCoordinates = new int[2]; 171b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 172b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private KeyboardView mPopupKeyboardView; 173b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private boolean mPopupKeyboardOnScreen; 174b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private View mPopupParent; 175b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mMiniKeyboardOffsetX; 176b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mMiniKeyboardOffsetY; 177b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final Map<Key,View> mMiniKeyboardCache; 178b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private Key[] mKeys; 179b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 180b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** Listener for {@link OnKeyboardActionListener}. */ 181b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private OnKeyboardActionListener mKeyboardActionListener; 182b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 183b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int MSG_SHOW_PREVIEW = 1; 184b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int MSG_REMOVE_PREVIEW = 2; 185b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int MSG_REPEAT = 3; 186b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int MSG_LONGPRESS = 4; 187b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 188b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int DELAY_BEFORE_PREVIEW = 0; 189b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int DELAY_AFTER_PREVIEW = 70; 190b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int DEBOUNCE_TIME = 70; 191b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 192b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int ANIMATE_IN_OUT_DURATION_MS = 300; 193b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int ANIMATE_SCRIM_DURATION_MS = 150; 194b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int ANIMATE_SCRIM_DELAY_MS = 225; 195b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 196b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mVerticalCorrection; 197b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mProximityThreshold; 198b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 199b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final boolean mPreviewCentered = false; 200b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private boolean mShowPreview = true; 201b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final boolean mShowTouchPoints = true; 202b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mPopupPreviewX; 203b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mPopupPreviewY; 204b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mPopupScrimColor; 205b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mBackgroundColor; 206b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 207b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mLastX; 208b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mLastY; 209b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mStartX; 210b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mStartY; 211b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 212b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private boolean mProximityCorrectOn; 213b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 214b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final Paint mPaint; 215b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final Rect mPadding; 216b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 217b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private long mDownTime; 218b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private long mLastMoveTime; 219b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mLastKey; 220b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mLastCodeX; 221b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mLastCodeY; 222b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mCurrentKey = NOT_A_KEY; 223b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mDownKey = NOT_A_KEY; 224b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private long mLastKeyTime; 225b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private long mCurrentKeyTime; 226b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final int[] mKeyIndices = new int[12]; 227b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private GestureDetector mGestureDetector; 228b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mRepeatKeyIndex = NOT_A_KEY; 229b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private boolean mAbortKey; 230b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private Key mInvalidatedKey; 231b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final Rect mClipRegion = new Rect(0, 0, 0, 0); 232b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private boolean mPossiblePoly; 233b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final SwipeTracker mSwipeTracker = new SwipeTracker(); 234b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final int mSwipeThreshold; 235b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final boolean mDisambiguateSwipe; 236b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 237b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Variables for dealing with multiple pointers 238b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mOldPointerCount = 1; 239b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private float mOldPointerX; 240b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private float mOldPointerY; 241b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 242b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private Drawable mKeyBackground; 243b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 244b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int REPEAT_INTERVAL = 50; // ~20 keys per second 245b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int REPEAT_START_DELAY = 400; 246b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout(); 247b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 248b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static int MAX_NEARBY_KEYS = 12; 249b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final int[] mDistances = new int[MAX_NEARBY_KEYS]; 250b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 251b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // For multi-tap 252b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mLastSentIndex; 253b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mTapCount; 254b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private long mLastTapTime; 255b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private boolean mInMultiTap; 256b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static final int MULTITAP_INTERVAL = 800; // milliseconds 257b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final StringBuilder mPreviewLabel = new StringBuilder(1); 258b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 259b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/ 260b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private boolean mDrawPending; 261b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** The dirty region in the keyboard bitmap */ 262b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final Rect mDirtyRect = new Rect(); 263b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** The keyboard bitmap for faster updates */ 264b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private Bitmap mBuffer; 265b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */ 266b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private boolean mKeyboardChanged; 267b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** The canvas for the above mutable keyboard bitmap */ 268b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private Canvas mCanvas; 269b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** The accessibility manager for accessibility support */ 270b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private AccessibilityManager mAccessibilityManager; 271b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 272b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private boolean mUseSecondaryColor = true; 273b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private Locale mLocale; 274b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 275b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Handler mHandler = new KeyboardHander(this); 276b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 277b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private float mAnimateInValue; 278b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private ValueAnimator mAnimateInAnimator; 279b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private float mAnimateOutValue; 280b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private ValueAnimator mAnimateOutAnimator; 281b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mScrimAlpha; 282b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private ValueAnimator mScrimAlphaAnimator; 283b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int mAnimateCenter; 284b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 285b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public KeyboardView(Context context, AttributeSet attrs) { 286b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer this(context, attrs, 0); 287b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 288b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 289b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public KeyboardView(Context context, AttributeSet attrs, int defStyle) { 290b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer super(context, attrs, defStyle); 291b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mContext = context; 292b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 293b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer TypedArray a = 294b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer context.obtainStyledAttributes( 295b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer attrs, R.styleable.KeyboardView, defStyle, 0); 296b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 297b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer LayoutInflater inflate = 298b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer (LayoutInflater) context 299b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 300b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 301b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int previewLayout = 0; 302b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 303b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int n = a.getIndexCount(); 304b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 305b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer for (int i = 0; i < n; i++) { 306b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int attr = a.getIndex(i); 307b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 308b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer switch (attr) { 309b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case R.styleable.KeyboardView_keyBackground: 310b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyBackground = a.getDrawable(attr); 311b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 312b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer/* case com.android.internal.R.styleable.KeyboardView_verticalCorrection: 313b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mVerticalCorrection = a.getDimensionPixelOffset(attr, 0); 314b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 315b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case com.android.internal.R.styleable.KeyboardView_keyPreviewLayout: 316b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer previewLayout = a.getResourceId(attr, 0); 317b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 318b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case com.android.internal.R.styleable.KeyboardView_keyPreviewOffset: 319b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewOffset = a.getDimensionPixelOffset(attr, 0); 320b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 321b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case com.android.internal.R.styleable.KeyboardView_keyPreviewHeight: 322b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewHeight = a.getDimensionPixelSize(attr, 80); 323b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; */ 324b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case R.styleable.KeyboardView_keyTextSize: 325b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyTextSize = a.getDimensionPixelSize(attr, 18); 326b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 327b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case R.styleable.KeyboardView_keyTextColorPrimary: 328b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyTextColorPrimary = a.getColor(attr, 0xFF000000); 329b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 330b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case R.styleable.KeyboardView_keyTextColorSecondary: 331b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyTextColorSecondary = a.getColor(attr, 0x99000000); 332b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 333b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case R.styleable.KeyboardView_labelTextSize: 334b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLabelTextSize = a.getDimensionPixelSize(attr, 14); 335b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 336b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case R.styleable.KeyboardView_fontFamily: 337b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mFontFamily = a.getString(attr); 338b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 339b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case R.styleable.KeyboardView_textStyle: 340b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mTextStyle = a.getInt(attr, 0); 341b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 342b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 343b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 344b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 345b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer a.recycle(); 346b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer a = mContext.obtainStyledAttributes(R.styleable.KeyboardView); 347b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer a.recycle(); 348b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 349b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewPopup = new PopupWindow(context); 350b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (previewLayout != 0) { 351b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewText = (TextView) inflate.inflate(previewLayout, null); 352b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewTextSizeLarge = (int) mPreviewText.getTextSize(); 353b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewPopup.setContentView(mPreviewText); 354b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewPopup.setBackgroundDrawable(null); 355b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 356b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mShowPreview = false; 357b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 358b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 359b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewPopup.setTouchable(false); 360b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 361b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupParent = this; 362b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer //mPredicting = true; 363b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 364b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPaint = new Paint(); 365b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPaint.setAntiAlias(true); 366b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPaint.setTextSize(mKeyTextSize); 367b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPaint.setTextAlign(Align.CENTER); 368b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPaint.setAlpha(MAX_ALPHA); 369b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 370b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mFontFamily != null) { 371b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Typeface typeface = Typeface.create(mFontFamily, mTextStyle); 372b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPaint.setTypeface(typeface); 373b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 374b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer else { 375b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPaint.setTypeface(Typeface.create(Typeface.DEFAULT, mTextStyle)); 376b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 377b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 378b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPadding = new Rect(0, 0, 0, 0); 379b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mMiniKeyboardCache = new HashMap<Key,View>(); 380b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyBackground.getPadding(mPadding); 381b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 382b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mSwipeThreshold = (int) (500 * getResources().getDisplayMetrics().density); 383b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mDisambiguateSwipe = true; 384b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 385d37a2ccc556bf38860c18558db470516f1a146e5Anthony Chen int color = getResources().getColor(R.color.car_dark_blue_grey_700); 386b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupScrimColor = Color.argb( 387b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer SCRIM_ALPHA, Color.red(color), Color.green(color), Color.blue(color)); 388b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mBackgroundColor = Color.TRANSPARENT; 389b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 390b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyPunctuationSize = 391b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer (int) getResources().getDimension(R.dimen.keyboard_key_punctuation_height); 392b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 393b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer resetMultiTap(); 394b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer initGestureDetector(); 395b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 396b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAnimateInValue = MAX_ANIMATION_VALUE; 397b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAnimateInAnimator = ValueAnimator.ofFloat(MIN_ANIMATION_VALUE, MAX_ANIMATION_VALUE); 398b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer AccelerateDecelerateInterpolator accInterpolator = new AccelerateDecelerateInterpolator(); 399b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAnimateInAnimator.setInterpolator(accInterpolator); 400b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAnimateInAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 401b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 402b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void onAnimationUpdate(ValueAnimator valueAnimator) { 403b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAnimateInValue = (float) valueAnimator.getAnimatedValue(); 404b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidateAllKeys(); 405b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 406b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer }); 407b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAnimateInAnimator.setDuration(ANIMATE_IN_OUT_DURATION_MS); 408b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 409b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mScrimAlpha = 0; 410b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mScrimAlphaAnimator = ValueAnimator.ofInt(0, SCRIM_ALPHA); 411b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer LinearInterpolator linearInterpolator = new LinearInterpolator(); 412b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mScrimAlphaAnimator.setInterpolator(linearInterpolator); 413b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mScrimAlphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 414b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 415b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void onAnimationUpdate(ValueAnimator valueAnimator) { 416b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mScrimAlpha = (int) valueAnimator.getAnimatedValue(); 417b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidateAllKeys(); 418b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 419b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer }); 420b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mScrimAlphaAnimator.setDuration(ANIMATE_SCRIM_DURATION_MS); 421b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 422b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAnimateOutValue = MIN_ANIMATION_VALUE; 423b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAnimateOutAnimator = ValueAnimator.ofFloat(MIN_ANIMATION_VALUE, MAX_ANIMATION_VALUE); 424b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAnimateOutAnimator.setInterpolator(accInterpolator); 425b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAnimateOutAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 426b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 427b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void onAnimationUpdate(ValueAnimator valueAnimator) { 428b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAnimateOutValue = (float) valueAnimator.getAnimatedValue(); 429b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidateAllKeys(); 430b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 431b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer }); 432b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAnimateOutAnimator.setDuration(ANIMATE_IN_OUT_DURATION_MS); 433b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAnimateOutAnimator.addListener(new Animator.AnimatorListener() { 434b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 435b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void onAnimationEnd(Animator animation) { 436b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer setVisibility(View.GONE); 437b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAnimateOutValue = MIN_ANIMATION_VALUE; 438b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 439b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 440b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 441b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void onAnimationCancel(Animator animator) { 442b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 443b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 444b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 445b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void onAnimationRepeat(Animator animator) { 446b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 447b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 448b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 449b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void onAnimationStart(Animator animator) { 450b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 451b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer }); 452b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 453b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 454b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 455b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private void initGestureDetector() { 456b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() { 457b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 458b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public boolean onFling(MotionEvent me1, MotionEvent me2, 459b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer float velocityX, float velocityY) { 460b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mPossiblePoly) return false; 461b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final float absX = Math.abs(velocityX); 462b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final float absY = Math.abs(velocityY); 463b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer float deltaX = me2.getX() - me1.getX(); 464b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer float deltaY = me2.getY() - me1.getY(); 465b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int travelX = getWidth() / 2; // Half the keyboard width 466b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int travelY = getHeight() / 2; // Half the keyboard height 467b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mSwipeTracker.computeCurrentVelocity(1000); 468b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final float endingVelocityX = mSwipeTracker.getXVelocity(); 469b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final float endingVelocityY = mSwipeTracker.getYVelocity(); 470b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer boolean sendDownKey = false; 471b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (velocityX > mSwipeThreshold && absY < absX && deltaX > travelX) { 472b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mDisambiguateSwipe && endingVelocityX < velocityX / 4) { 473b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer sendDownKey = true; 474b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 475b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer swipeRight(); 476b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return true; 477b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 478b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else if (velocityX < -mSwipeThreshold && absY < absX && deltaX < -travelX) { 479b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mDisambiguateSwipe && endingVelocityX > velocityX / 4) { 480b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer sendDownKey = true; 481b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 482b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer swipeLeft(); 483b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return true; 484b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 485b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else if (velocityY < -mSwipeThreshold && absX < absY && deltaY < -travelY) { 486b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mDisambiguateSwipe && endingVelocityY > velocityY / 4) { 487b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer sendDownKey = true; 488b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 489b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer swipeUp(); 490b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return true; 491b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 492b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) { 493b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mDisambiguateSwipe && endingVelocityY < velocityY / 4) { 494b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer sendDownKey = true; 495b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 496b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer swipeDown(); 497b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return true; 498b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 499b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 500b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 501b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (sendDownKey) { 502b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer detectAndSendKey(mDownKey, mStartX, mStartY, me1.getEventTime()); 503b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 504b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return false; 505b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 506b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer }); 507b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 508b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mGestureDetector.setIsLongpressEnabled(false); 509b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 510b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 511b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void setOnKeyboardActionListener(OnKeyboardActionListener listener) { 512b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardActionListener = listener; 513b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 514b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 515b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 516b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Returns the {@link OnKeyboardActionListener} object. 517b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @return the listener attached to this keyboard 518b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 519b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer protected OnKeyboardActionListener getOnKeyboardActionListener() { 520b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return mKeyboardActionListener; 521b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 522b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 523b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 524b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Attaches a keyboard to this view. The keyboard can be switched at any time and the 525b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * view will re-layout itself to accommodate the keyboard. 526b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @see android.inputmethodservice.Keyboard 527b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @see #getKeyboard() 528b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @param keyboard the keyboard to display in this view 529b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 530b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void setKeyboard(Keyboard keyboard, Locale locale) { 531b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mKeyboard != null) { 532b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer showPreview(NOT_A_KEY); 533b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 534b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Remove any pending messages 535b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer removeMessages(); 536b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboard = keyboard; 537b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer List<Key> keys = mKeyboard.getKeys(); 538b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeys = keys.toArray(new Key[keys.size()]); 539b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer requestLayout(); 540b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Hint to reallocate the buffer if the size changed 541b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardChanged = true; 542b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidateAllKeys(); 543b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer computeProximityThreshold(keyboard); 544b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mMiniKeyboardCache.clear(); // Not really necessary to do every time, but will free up views 545b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Switching to a different keyboard should abort any pending keys so that the key up 546b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // doesn't get delivered to the old or new keyboard 547b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAbortKey = true; // Until the next ACTION_DOWN 548b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLocale = locale; 549b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 550b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 551b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 552b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Returns the current keyboard being displayed by this view. 553b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @return the currently attached keyboard 554b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @see #setKeyboard(android.inputmethodservice.Keyboard, java.util.Locale) 555b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 556b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public Keyboard getKeyboard() { 557b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return mKeyboard; 558b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 559b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 560b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 561b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Sets the state of the shift key of the keyboard, if any. 562b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @param shifted whether or not to enable the state of the shift key 563b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @return true if the shift key state changed, false if there was no change 564b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @see KeyboardView#isShifted() 565b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 566b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public boolean setShifted(boolean shifted) { 567b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mKeyboard != null) { 568b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mKeyboard.setShifted(shifted)) { 569b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // The whole keyboard probably needs to be redrawn 570b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidateAllKeys(); 571b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return true; 572b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 573b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 574b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return false; 575b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 576b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 577b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 578b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Returns the state of the shift key of the keyboard, if any. 579b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @return true if the shift is in a pressed state, false otherwise. If there is 580b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * no shift key on the keyboard or there is no keyboard attached, it returns false. 581b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @see KeyboardView#setShifted(boolean) 582b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 583b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public boolean isShifted() { 584b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mKeyboard != null) { 585b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return mKeyboard.isShifted(); 586b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 587b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return false; 588b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 589b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 590b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 591b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Enables or disables the key feedback popup. This is a popup that shows a magnified 592b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * version of the depressed key. By default the preview is enabled. 593b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @param previewEnabled whether or not to enable the key feedback popup 594b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @see #isPreviewEnabled() 595b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 596b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void setPreviewEnabled(boolean previewEnabled) { 597b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mShowPreview = previewEnabled; 598b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 599b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 600b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 601b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Returns the enabled state of the key feedback popup. 602b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @return whether or not the key feedback popup is enabled 603b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @see #setPreviewEnabled(boolean) 604b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 605b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public boolean isPreviewEnabled() { 606b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return mShowPreview; 607b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 608b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 609b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void setPopupParent(View v) { 610b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupParent = v; 611b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 612b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 613b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void setPopupOffset(int x, int y) { 614b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mMiniKeyboardOffsetX = x; 615b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mMiniKeyboardOffsetY = y; 616b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mPreviewPopup.isShowing()) { 617b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewPopup.dismiss(); 618b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 619b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 620b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 621b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 622b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * When enabled, calls to {@link OnKeyboardActionListener#onKey} will include key 623b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * codes for adjacent keys. When disabled, only the primary key code will be 624b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * reported. 625b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @param enabled whether or not the proximity correction is enabled 626b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 627b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void setProximityCorrectionEnabled(boolean enabled) { 628b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mProximityCorrectOn = enabled; 629b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 630b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 631b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 632b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Returns true if proximity correction is enabled. 633b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 634b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public boolean isProximityCorrectionEnabled() { 635b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return mProximityCorrectOn; 636b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 637b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 638b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public boolean getUseSecondaryColor() { 639b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return mUseSecondaryColor; 640b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 641b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 642b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void setUseSecondaryColor(boolean useSecondaryColor) { 643b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mUseSecondaryColor = useSecondaryColor; 644b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 645b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 646b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 647b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Popup keyboard close button clicked. 648b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @hide 649b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 650b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 651b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void onClick(View v) { 652b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer dismissPopupKeyboard(); 653b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 654b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 655b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private CharSequence adjustCase(CharSequence label) { 656b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mKeyboard.isShifted() && label != null && label.length() < 3 657b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer && Character.isLowerCase(label.charAt(0))) { 658b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer label = label.toString().toUpperCase(mLocale); 659b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 660b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return label; 661b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 662b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 663b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 664b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 665b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Round up a little 666b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mKeyboard == null) { 667b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer setMeasuredDimension(getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom()); 668b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 669b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int width = mKeyboard.getMinWidth() + getPaddingLeft() + getPaddingRight(); 670b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (MeasureSpec.getSize(widthMeasureSpec) < width + 10) { 671b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer width = MeasureSpec.getSize(widthMeasureSpec); 672b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 673b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer setMeasuredDimension(width, mKeyboard.getHeight() + getPaddingTop() + getPaddingBottom()); 674b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 675b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 676b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 677b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 678b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Compute the average distance between adjacent keys (horizontally and vertically) 679b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * and square it to get the proximity threshold. We use a square here and in computing 680b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * the touch distance from a key's center to avoid taking a square root. 681b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @param keyboard 682b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 683b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private void computeProximityThreshold(Keyboard keyboard) { 684b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (keyboard == null) return; 685b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Key[] keys = mKeys; 686b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (keys == null) return; 687b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int length = keys.length; 688b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int dimensionSum = 0; 689b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer for (int i = 0; i < length; i++) { 690b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Key key = keys[i]; 691b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer dimensionSum += Math.min(key.width, key.height) + key.gap; 692b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 693b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (dimensionSum < 0 || length == 0) return; 694b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mProximityThreshold = (int) (dimensionSum * 1.4f / length); 695b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mProximityThreshold *= mProximityThreshold; // Square it 696b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 697b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 698b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 699b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void onSizeChanged(int w, int h, int oldw, int oldh) { 700b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer super.onSizeChanged(w, h, oldw, oldh); 701b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mKeyboard != null) { 702b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer //TODO(ftamp): mKeyboard.resize(w, h); 703b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 704b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Release the buffer, if any and it will be reallocated on the next draw 705b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mBuffer = null; 706b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 707b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 708b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 709b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void onDraw(Canvas canvas) { 710b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer super.onDraw(canvas); 711b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mDrawPending || mBuffer == null || mKeyboardChanged) { 712b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer onBufferDraw(); 713b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 714b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.drawBitmap(mBuffer, 0, 0, null); 715b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 716b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 717b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @SuppressWarnings("unused") 718b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private void onBufferDraw() { 719b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mBuffer == null || mKeyboardChanged) { 720b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mBuffer == null || mKeyboardChanged && 721b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) { 722b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Make sure our bitmap is at least 1x1 723b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int width = Math.max(1, getWidth()); 724b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int height = Math.max(1, getHeight()); 725b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 726b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCanvas = new Canvas(mBuffer); 727b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 728b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidateAllKeys(); 729b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardChanged = false; 730b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 731b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Canvas canvas = mCanvas; 732b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.clipRect(mDirtyRect, Op.REPLACE); 733b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 734b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mKeyboard == null) return; 735b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 736b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Paint paint = mPaint; 737b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Drawable keyBackground = mKeyBackground; 738b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Rect clipRegion = mClipRegion; 739b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Rect padding = mPadding; 740b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int kbdPaddingLeft = getPaddingLeft(); 741b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int kbdPaddingTop = getPaddingTop(); 742b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Key[] keys = mKeys; 743b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Key invalidKey = mInvalidatedKey; 744b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 745b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setColor(mKeyTextColorPrimary); 746b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer boolean drawSingleKey = false; 747b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (invalidKey != null && canvas.getClipBounds(clipRegion)) { 748b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Is clipRegion completely contained within the invalidated key? 749b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (invalidKey.x + kbdPaddingLeft - 1 <= clipRegion.left && 750b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidKey.y + kbdPaddingTop - 1 <= clipRegion.top && 751b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidKey.x + invalidKey.width + kbdPaddingLeft + 1 >= clipRegion.right && 752b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidKey.y + invalidKey.height + kbdPaddingTop + 1 >= clipRegion.bottom) { 753b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer drawSingleKey = true; 754b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 755b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 756b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); 757b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 758b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Animate in translation. No overall translation for animating out. 759b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer float offset = getWidth()/2 * (1 - mAnimateInValue); 760b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.translate(offset, 0); 761b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 762b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Draw background. 763b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer float backgroundWidth = getWidth() * (mAnimateInValue - mAnimateOutValue); 764b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setColor(mBackgroundColor); 765b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int center; 766b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mLastSentIndex != NOT_A_KEY && mLastSentIndex < keys.length) { 767b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer center = keys[mLastSentIndex].x + keys[mLastSentIndex].width / 2; 768b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 769b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer center = getWidth()/2; 770b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 771b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.drawRect(mAnimateOutValue * center, 0, mAnimateOutValue * center + backgroundWidth, 772b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer getHeight(), paint); 773b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int keyCount = keys.length; 774b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer for (int i = 0; i < keyCount; i++) { 775b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Key key = keys[i]; 776b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (drawSingleKey && invalidKey != key) { 777b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer continue; 778b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 779b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int[] drawableState = key.getCurrentDrawableState(); 780b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer keyBackground.setState(drawableState); 781b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (key.icon != null) { 782b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer key.icon.setState(drawableState); 783b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 784b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 785b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 786b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Switch the character to uppercase if shift is pressed 787b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer String label = key.label == null? null : adjustCase(key.label).toString(); 788b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 789b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Rect bounds = keyBackground.getBounds(); 790b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (key.width != bounds.right || 791b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer key.height != bounds.bottom) { 792b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer keyBackground.setBounds(0, 0, key.width, key.height); 793b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 794b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer float animateOutOffset = 0; 795b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mAnimateOutValue != MIN_ANIMATION_VALUE) { 796b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer animateOutOffset = (center - key.x - key.width/2) * mAnimateOutValue; 797b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 798b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.translate(key.x + kbdPaddingLeft + animateOutOffset, key.y + kbdPaddingTop); 799b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer keyBackground.draw(canvas); 800b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 801b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (label != null && label.length() > 0) { 802b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Use primary color for letters and digits, secondary color for everything else 803b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (Character.isLetterOrDigit(label.charAt(0))) { 804b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setColor(mKeyTextColorPrimary); 805b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else if (mUseSecondaryColor) { 806b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setColor(mKeyTextColorSecondary); 807b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 808b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // For characters, use large font. For labels like "Done", use small font. 809b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (label.length() > 1 && key.codes.length < 2) { 810b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setTextSize(mLabelTextSize); 811b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else if (isPunctuation(label)) { 812b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setTextSize(mKeyPunctuationSize); 813b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 814b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setTextSize(mKeyTextSize); 815b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 816b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mAnimateInValue != MAX_ANIMATION_VALUE) { 817b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int alpha = 818b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer (int) ((backgroundWidth - key.x - key.width) / key.width * MAX_ALPHA); 819b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (alpha > MAX_ALPHA) { 820b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer alpha = MAX_ALPHA; 821b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else if (alpha < 0) { 822b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer alpha = 0; 823b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 824b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setAlpha(alpha); 825b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else if (mAnimateOutValue != MIN_ANIMATION_VALUE) { 826b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int alpha; 827b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (i == mLastSentIndex) { 828b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Fade out selected character from 1/2 to 3/4 of animate out. 829b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer alpha = (int) ((3 - mAnimateOutValue * 4) * MAX_ALPHA); 830b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 831b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Fade out the rest from start to 1/2 of animate out. 832b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer alpha = (int) ((1 - mAnimateOutValue * 2) * MAX_ALPHA); 833b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 834b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (alpha > MAX_ALPHA) { 835b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer alpha = MAX_ALPHA; 836b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else if (alpha < 0) { 837b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer alpha = 0; 838b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 839b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setAlpha(alpha); 840b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 841b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Draw the text 842b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.drawText(label, 843b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer (key.width - padding.left - padding.right) / 2 844b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer + padding.left, 845b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer (key.height - padding.top - padding.bottom) / 2 846b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer + (paint.getTextSize() - paint.descent()) / 2 + padding.top, 847b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint); 848b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Turn off drop shadow 849b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setShadowLayer(0, 0, 0, 0); 850b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else if (key.icon != null) { 851b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int drawableX = (key.width - padding.left - padding.right 852b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer - key.icon.getIntrinsicWidth()) / 2 + padding.left; 853b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int drawableY = (key.height - padding.top - padding.bottom 854b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer - key.icon.getIntrinsicHeight()) / 2 + padding.top; 855b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.translate(drawableX, drawableY); 856b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer key.icon.setBounds(0, 0, 857b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer key.icon.getIntrinsicWidth(), key.icon.getIntrinsicHeight()); 858b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer key.icon.draw(canvas); 859b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.translate(-drawableX, -drawableY); 860b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 861b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.translate(-key.x - kbdPaddingLeft - animateOutOffset, -key.y - kbdPaddingTop); 862b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 863b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mInvalidatedKey = null; 864b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Overlay a dark rectangle to dim the keyboard 865b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setColor(mPopupScrimColor); 866b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setAlpha(mScrimAlpha); 867b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.drawRect(0, 0, getWidth(), getHeight(), paint); 868b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.translate(-offset, 0); 869b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 870b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (DEBUG && mShowTouchPoints) { 871b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setAlpha(128); 872b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setColor(0xFFFF0000); 873b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.drawCircle(mStartX, mStartY, 3, paint); 874b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.drawLine(mStartX, mStartY, mLastX, mLastY, paint); 875b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setColor(0xFF0000FF); 876b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.drawCircle(mLastX, mLastY, 3, paint); 877b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer paint.setColor(0xFF00FF00); 878b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer canvas.drawCircle((mStartX + mLastX) / 2, (mStartY + mLastY) / 2, 2, paint); 879b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 880b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 881b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mDrawPending = false; 882b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mDirtyRect.setEmpty(); 883b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 884b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 885b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private boolean isPunctuation(String label) { 886b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return PUNCTUATION_PATTERN.matcher(label).matches(); 887b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 888b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 889b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private int getKeyIndices(int x, int y, int[] allKeys) { 890b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Key[] keys = mKeys; 891b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int primaryIndex = NOT_A_KEY; 892b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int closestKey = NOT_A_KEY; 893b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int closestKeyDist = mProximityThreshold + 1; 894b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Arrays.fill(mDistances, Integer.MAX_VALUE); 895b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int [] nearestKeyIndices = mKeyboard.getNearestKeys(x, y); 896b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int keyCount = nearestKeyIndices.length; 897b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer for (int i = 0; i < keyCount; i++) { 898b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Key key = keys[nearestKeyIndices[i]]; 899b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int dist = 0; 900b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer boolean isInside = key.isInside(x,y); 901b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (isInside) { 902b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer primaryIndex = nearestKeyIndices[i]; 903b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 904b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 905b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (((mProximityCorrectOn 906b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer && (dist = key.squaredDistanceFrom(x, y)) < mProximityThreshold) 907b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer || isInside) 908b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer && key.codes[0] > 32) { 909b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Find insertion point 910b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int nCodes = key.codes.length; 911b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (dist < closestKeyDist) { 912b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer closestKeyDist = dist; 913b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer closestKey = nearestKeyIndices[i]; 914b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 915b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 916b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (allKeys == null) continue; 917b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 918b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer for (int j = 0; j < mDistances.length; j++) { 919b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mDistances[j] > dist) { 920b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Make space for nCodes codes 921b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer System.arraycopy(mDistances, j, mDistances, j + nCodes, 922b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mDistances.length - j - nCodes); 923b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer System.arraycopy(allKeys, j, allKeys, j + nCodes, 924b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer allKeys.length - j - nCodes); 925b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer for (int c = 0; c < nCodes; c++) { 926b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer allKeys[j + c] = key.codes[c]; 927b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mDistances[j + c] = dist; 928b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 929b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 930b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 931b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 932b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 933b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 934b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (primaryIndex == NOT_A_KEY) { 935b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer primaryIndex = closestKey; 936b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 937b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return primaryIndex; 938b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 939b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 940b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private void detectAndSendKey(int index, int x, int y, long eventTime) { 941b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (index != NOT_A_KEY && index < mKeys.length) { 942b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Key key = mKeys[index]; 943b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (key.text != null) { 944b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardActionListener.onText(key.text); 945b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardActionListener.onRelease(NOT_A_KEY); 946b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 947b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int code = key.codes[0]; 948b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer //TextEntryState.keyPressedAt(key, x, y); 949b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int[] codes = new int[MAX_NEARBY_KEYS]; 950b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Arrays.fill(codes, NOT_A_KEY); 951b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer getKeyIndices(x, y, codes); 952b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Multi-tap 953b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mInMultiTap) { 954b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mTapCount != -1) { 955b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardActionListener.onKey(Keyboard.KEYCODE_DELETE, KEY_DELETE); 956b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 957b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mTapCount = 0; 958b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 959b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer code = key.codes[mTapCount]; 960b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 961b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardActionListener.onKey(code, codes); 962b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardActionListener.onRelease(code); 963b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 964b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastSentIndex = index; 965b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastTapTime = eventTime; 966b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 967b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 968b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 969b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 970b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Handle multi-tap keys by producing the key label for the current multi-tap state. 971b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 972b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private CharSequence getPreviewText(Key key) { 973b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mInMultiTap) { 974b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Multi-tap 975b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewLabel.setLength(0); 976b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewLabel.append((char) key.codes[mTapCount < 0 ? 0 : mTapCount]); 977b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return adjustCase(mPreviewLabel); 978b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 979b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return adjustCase(key.label); 980b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 981b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 982b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 983b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private void showPreview(int keyIndex) { 984b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int oldKeyIndex = mCurrentKeyIndex; 985b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final PopupWindow previewPopup = mPreviewPopup; 986b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 987b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCurrentKeyIndex = keyIndex; 988b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Release the old key and press the new key 989b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Key[] keys = mKeys; 990b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (oldKeyIndex != mCurrentKeyIndex) { 991b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (oldKeyIndex != NOT_A_KEY && keys.length > oldKeyIndex) { 992b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Key oldKey = keys[oldKeyIndex]; 993b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Keyboard.Key.onReleased(boolean) should be called here, but due to b/21446448, 994b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // onReleased doesn't check the input param and it always toggles the on state. 995b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Therefore we need to just set the pressed state to false here. 996b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer oldKey.pressed = false; 997b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidateKey(oldKeyIndex); 998b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 999b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mCurrentKeyIndex != NOT_A_KEY && keys.length > mCurrentKeyIndex) { 1000b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Key newKey = keys[mCurrentKeyIndex]; 1001b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer newKey.onPressed(); 1002b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidateKey(mCurrentKeyIndex); 1003b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1004b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1005b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // If key changed and preview is on ... 1006b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (oldKeyIndex != mCurrentKeyIndex && mShowPreview) { 1007b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mHandler.removeMessages(MSG_SHOW_PREVIEW); 1008b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (previewPopup.isShowing()) { 1009b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (keyIndex == NOT_A_KEY) { 1010b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mHandler.sendMessageDelayed(mHandler 1011b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer .obtainMessage(MSG_REMOVE_PREVIEW), 1012b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer DELAY_AFTER_PREVIEW); 1013b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1014b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1015b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (keyIndex != NOT_A_KEY) { 1016b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (previewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) { 1017b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Show right away, if it's already visible and finger is moving around 1018b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer showKey(keyIndex); 1019b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 1020b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mHandler.sendMessageDelayed( 1021b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mHandler.obtainMessage(MSG_SHOW_PREVIEW, keyIndex, 0), 1022b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer DELAY_BEFORE_PREVIEW); 1023b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1024b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1025b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1026b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1027b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1028b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private void showKey(final int keyIndex) { 1029b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final PopupWindow previewPopup = mPreviewPopup; 1030b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Key[] keys = mKeys; 1031b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (keyIndex < 0 || keyIndex >= mKeys.length) return; 1032b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Key key = keys[keyIndex]; 1033b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (key.icon != null) { 1034b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewText.setCompoundDrawables(null, null, null, 1035b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer key.iconPreview != null ? key.iconPreview : key.icon); 1036b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewText.setText(null); 1037b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 1038b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewText.setCompoundDrawables(null, null, null, null); 1039b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewText.setText(getPreviewText(key)); 1040b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (key.label.length() > 1 && key.codes.length < 2) { 1041b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyTextSize); 1042b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewText.setTypeface(Typeface.DEFAULT_BOLD); 1043b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 1044b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSizeLarge); 1045b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewText.setTypeface(Typeface.DEFAULT); 1046b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1047b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1048b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 1049b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 1050b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), key.width 1051b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight()); 1052b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int popupHeight = mPreviewHeight; 1053b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer LayoutParams lp = mPreviewText.getLayoutParams(); 1054b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (lp != null) { 1055b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer lp.width = popupWidth; 1056b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer lp.height = popupHeight; 1057b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1058b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (!mPreviewCentered) { 1059b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupPreviewX = key.x - mPreviewText.getPaddingLeft() + getPaddingLeft(); 1060b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupPreviewY = key.y - popupHeight + mPreviewOffset; 1061b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 1062b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // TODO: Fix this if centering is brought back 1063b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupPreviewX = 160 - mPreviewText.getMeasuredWidth() / 2; 1064b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupPreviewY = - mPreviewText.getMeasuredHeight(); 1065b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1066b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mHandler.removeMessages(MSG_REMOVE_PREVIEW); 1067b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer getLocationInWindow(mCoordinates); 1068b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCoordinates[0] += mMiniKeyboardOffsetX; // Offset may be zero 1069b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCoordinates[1] += mMiniKeyboardOffsetY; // Offset may be zero 1070b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1071b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Set the preview background state 1072b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /* 1073b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewText.getBackground().setState( 1074b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET); 1075b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 1076b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupPreviewX += mCoordinates[0]; 1077b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupPreviewY += mCoordinates[1]; 1078b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1079b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // If the popup cannot be shown above the key, put it on the side 1080b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer getLocationOnScreen(mCoordinates); 1081b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mPopupPreviewY + mCoordinates[1] < 0) { 1082b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // If the key you're pressing is on the left side of the keyboard, show the popup on 1083b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // the right, offset by enough to see at least one key to the left/right. 1084b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (key.x + key.width <= getWidth() / 2) { 1085b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupPreviewX += (int) (key.width * 2.5); 1086b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 1087b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupPreviewX -= (int) (key.width * 2.5); 1088b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1089b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupPreviewY += popupHeight; 1090b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1091b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1092b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (previewPopup.isShowing()) { 1093b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer previewPopup.update(mPopupPreviewX, mPopupPreviewY, 1094b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer popupWidth, popupHeight); 1095b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 1096b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer previewPopup.setWidth(popupWidth); 1097b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer previewPopup.setHeight(popupHeight); 1098b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY, 1099b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupPreviewX, mPopupPreviewY); 1100b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1101b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewText.setVisibility(VISIBLE); 1102b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1103b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1104b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 1105b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient 1106b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * because the keyboard renders the keys to an off-screen buffer and an invalidate() only 1107b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * draws the cached buffer. 1108b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @see #invalidateKey(int) 1109b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 1110b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void invalidateAllKeys() { 1111b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mDirtyRect.union(0, 0, getWidth(), getHeight()); 1112b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mDrawPending = true; 1113b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidate(); 1114b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1115b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1116b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 1117b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only 1118b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * one key is changing it's content. Any changes that affect the position or size of the key 1119b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * may not be honored. 1120b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @param keyIndex the index of the key in the attached {@link android.inputmethodservice.Keyboard}. 1121b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @see #invalidateAllKeys 1122b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 1123b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void invalidateKey(int keyIndex) { 1124b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mKeys == null) return; 1125b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (keyIndex < 0 || keyIndex >= mKeys.length) { 1126b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return; 1127b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1128b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final Key key = mKeys[keyIndex]; 1129b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mInvalidatedKey = key; 1130b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mDirtyRect.union(key.x + getPaddingLeft(), key.y + getPaddingTop(), 1131b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop()); 1132b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer onBufferDraw(); 1133b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidate(key.x + getPaddingLeft(), key.y + getPaddingTop(), 1134b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop()); 1135b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1136b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1137b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private void openPopupIfRequired(MotionEvent unused) { 1138b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Check if we have a popup keyboard first. 1139b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mPopupKeyboardView == null || mCurrentKey < 0 || mCurrentKey >= mKeys.length) { 1140b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return; 1141b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1142b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1143b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Key popupKey = mKeys[mCurrentKey]; 1144b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer boolean result = onLongPress(popupKey); 1145b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (result) { 1146b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAbortKey = true; 1147b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer showPreview(NOT_A_KEY); 1148b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1149b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1150b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1151b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer /** 1152b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * Called when a key is long pressed. By default this will open any popup keyboard associated 1153b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * with this key through the attributes popupLayout and popupCharacters. 1154b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * 1155b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @param popupKey the key that was long pressed 1156b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * @return true if the long press is handled, false otherwise. Subclasses should call the 1157b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer * method on the base class if the subclass doesn't wish to handle the call. 1158b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer */ 1159b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer protected boolean onLongPress(Key popupKey) { 1160b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int popupKeyboardId = popupKey.popupResId; 1161b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1162b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (popupKeyboardId != 0) { 1163b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Keyboard keyboard; 1164b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (popupKey.popupCharacters != null) { 1165b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer keyboard = new Keyboard(getContext(), popupKeyboardId, 1166b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer popupKey.popupCharacters, -1, getPaddingLeft() + getPaddingRight()); 1167b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 1168b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer keyboard = new Keyboard(getContext(), popupKeyboardId); 1169b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1170b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupKeyboardView.setKeyboard(keyboard, mLocale); 1171b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupKeyboardView.setVisibility(VISIBLE); 1172b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupKeyboardView.setShifted(isShifted()); 1173b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupKeyboardView.mAnimateInAnimator.start(); 1174b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupKeyboardView.mLastSentIndex = NOT_A_KEY; 1175b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mScrimAlphaAnimator.setStartDelay(0); 1176b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mScrimAlphaAnimator.start(); 1177b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupKeyboardView.invalidate(); 1178b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupKeyboardOnScreen = true; 1179b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidateAllKeys(); 1180b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return true; 1181b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1182b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return false; 1183b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1184b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1185b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 1186b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public boolean onHoverEvent(MotionEvent event) { 1187b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mAccessibilityManager.isTouchExplorationEnabled() && event.getPointerCount() == 1) { 1188b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int action = event.getAction(); 1189b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer switch (action) { 1190b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case MotionEvent.ACTION_HOVER_ENTER: { 1191b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer event.setAction(MotionEvent.ACTION_DOWN); 1192b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } break; 1193b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case MotionEvent.ACTION_HOVER_MOVE: { 1194b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer event.setAction(MotionEvent.ACTION_MOVE); 1195b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } break; 1196b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case MotionEvent.ACTION_HOVER_EXIT: { 1197b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer event.setAction(MotionEvent.ACTION_UP); 1198b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } break; 1199b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1200b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return onTouchEvent(event); 1201b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1202b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return true; 1203b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1204b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1205b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @SuppressLint("ClickableViewAccessibility") 1206b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 1207b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public boolean onTouchEvent(MotionEvent me) { 1208b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Don't process touches while animating. 1209b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mAnimateInValue != MAX_ANIMATION_VALUE || mAnimateOutValue != MIN_ANIMATION_VALUE) { 1210b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return false; 1211b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1212b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1213b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Convert multi-pointer up/down events to single up/down events to 1214b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // deal with the typical multi-pointer behavior of two-thumb typing 1215b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int pointerCount = me.getPointerCount(); 1216b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int action = me.getAction(); 1217b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer boolean result = false; 1218b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final long now = me.getEventTime(); 1219b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1220b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (pointerCount != mOldPointerCount) { 1221b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (pointerCount == 1) { 1222b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Send a down event for the latest pointer 1223b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer MotionEvent down = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1224b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer me.getX(), me.getY(), me.getMetaState()); 1225b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer result = onModifiedTouchEvent(down, false); 1226b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer down.recycle(); 1227b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // If it's an up action, then deliver the up as well. 1228b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (action == MotionEvent.ACTION_UP) { 1229b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer result = onModifiedTouchEvent(me, true); 1230b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1231b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 1232b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Send an up event for the last pointer 1233b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer MotionEvent up = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 1234b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mOldPointerX, mOldPointerY, me.getMetaState()); 1235b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer result = onModifiedTouchEvent(up, true); 1236b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer up.recycle(); 1237b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1238b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 1239b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (pointerCount == 1) { 1240b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer result = onModifiedTouchEvent(me, false); 1241b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mOldPointerX = me.getX(); 1242b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mOldPointerY = me.getY(); 1243b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 1244b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Don't do anything when 2 pointers are down and moving. 1245b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer result = true; 1246b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1247b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1248b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mOldPointerCount = pointerCount; 1249b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1250b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return result; 1251b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1252b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1253b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private boolean onModifiedTouchEvent(MotionEvent me, boolean possiblePoly) { 1254b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int touchX = (int) me.getX() - getPaddingLeft(); 1255b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int touchY = (int) me.getY() - getPaddingTop(); 1256b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (touchY >= -mVerticalCorrection) 1257b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer touchY += mVerticalCorrection; 1258b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int action = me.getAction(); 1259b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final long eventTime = me.getEventTime(); 1260b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int keyIndex = getKeyIndices(touchX, touchY, null); 1261b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPossiblePoly = possiblePoly; 1262b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1263b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Track the last few movements to look for spurious swipes. 1264b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (action == MotionEvent.ACTION_DOWN) mSwipeTracker.clear(); 1265b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mSwipeTracker.addMovement(me); 1266b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1267b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Ignore all motion events until a DOWN. 1268b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mAbortKey 1269b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer && action != MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_CANCEL) { 1270b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return true; 1271b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1272b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1273b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mGestureDetector.onTouchEvent(me)) { 1274b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer showPreview(NOT_A_KEY); 1275b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mHandler.removeMessages(MSG_REPEAT); 1276b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mHandler.removeMessages(MSG_LONGPRESS); 1277b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return true; 1278b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1279b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1280b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mPopupKeyboardOnScreen) { 1281b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer dismissPopupKeyboard(); 1282b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return true; 1283b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1284b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1285b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer switch (action) { 1286b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case MotionEvent.ACTION_DOWN: 1287b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAbortKey = false; 1288b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mStartX = touchX; 1289b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mStartY = touchY; 1290b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastCodeX = touchX; 1291b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastCodeY = touchY; 1292b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastKeyTime = 0; 1293b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCurrentKeyTime = 0; 1294b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastKey = NOT_A_KEY; 1295b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCurrentKey = keyIndex; 1296b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mDownKey = keyIndex; 1297b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mDownTime = me.getEventTime(); 1298b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastMoveTime = mDownTime; 1299b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer checkMultiTap(eventTime, keyIndex); 1300b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (keyIndex != NOT_A_KEY) { 1301b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardActionListener.onPress(mKeys[keyIndex].codes[0]); 1302b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1303b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mCurrentKey >= 0 && mKeys[mCurrentKey].repeatable) { 1304b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mRepeatKeyIndex = mCurrentKey; 1305b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Message msg = mHandler.obtainMessage(MSG_REPEAT); 1306b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mHandler.sendMessageDelayed(msg, REPEAT_START_DELAY); 1307b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer repeatKey(); 1308b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Delivering the key could have caused an abort 1309b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mAbortKey) { 1310b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mRepeatKeyIndex = NOT_A_KEY; 1311b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 1312b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1313b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1314b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mCurrentKey != NOT_A_KEY) { 1315b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Message msg = mHandler.obtainMessage(MSG_LONGPRESS, me); 1316b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT); 1317b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1318b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer showPreview(keyIndex); 1319b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 1320b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1321b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case MotionEvent.ACTION_MOVE: 1322b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer boolean continueLongPress = false; 1323b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (keyIndex != NOT_A_KEY) { 1324b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mCurrentKey == NOT_A_KEY) { 1325b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCurrentKey = keyIndex; 1326b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCurrentKeyTime = eventTime - mDownTime; 1327b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 1328b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (keyIndex == mCurrentKey) { 1329b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCurrentKeyTime += eventTime - mLastMoveTime; 1330b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer continueLongPress = true; 1331b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else if (mRepeatKeyIndex == NOT_A_KEY) { 1332b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer resetMultiTap(); 1333b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastKey = mCurrentKey; 1334b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastCodeX = mLastX; 1335b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastCodeY = mLastY; 1336b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastKeyTime = 1337b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCurrentKeyTime + eventTime - mLastMoveTime; 1338b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCurrentKey = keyIndex; 1339b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCurrentKeyTime = 0; 1340b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1341b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1342b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1343b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (!continueLongPress) { 1344b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Cancel old longpress 1345b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mHandler.removeMessages(MSG_LONGPRESS); 1346b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Start new longpress if key has changed 1347b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (keyIndex != NOT_A_KEY) { 1348b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Message msg = mHandler.obtainMessage(MSG_LONGPRESS, me); 1349b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT); 1350b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1351b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1352b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer showPreview(mCurrentKey); 1353b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastMoveTime = eventTime; 1354b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 1355b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1356b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case MotionEvent.ACTION_UP: 1357b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer removeMessages(); 1358b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (keyIndex == mCurrentKey) { 1359b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCurrentKeyTime += eventTime - mLastMoveTime; 1360b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 1361b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer resetMultiTap(); 1362b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastKey = mCurrentKey; 1363b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastKeyTime = mCurrentKeyTime + eventTime - mLastMoveTime; 1364b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCurrentKey = keyIndex; 1365b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCurrentKeyTime = 0; 1366b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1367b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mCurrentKeyTime < mLastKeyTime && mCurrentKeyTime < DEBOUNCE_TIME 1368b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer && mLastKey != NOT_A_KEY) { 1369b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCurrentKey = mLastKey; 1370b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer touchX = mLastCodeX; 1371b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer touchY = mLastCodeY; 1372b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1373b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer showPreview(NOT_A_KEY); 1374b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Arrays.fill(mKeyIndices, NOT_A_KEY); 1375b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // If we're not on a repeating key (which sends on a DOWN event) 1376b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mRepeatKeyIndex == NOT_A_KEY && !mPopupKeyboardOnScreen && !mAbortKey) { 1377b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer detectAndSendKey(mCurrentKey, touchX, touchY, eventTime); 1378b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1379b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidateKey(keyIndex); 1380b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mRepeatKeyIndex = NOT_A_KEY; 1381b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 1382b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case MotionEvent.ACTION_CANCEL: 1383b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer removeMessages(); 1384b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer dismissPopupKeyboard(); 1385b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mAbortKey = true; 1386b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer showPreview(NOT_A_KEY); 1387b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidateKey(mCurrentKey); 1388b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 1389b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1390b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastX = touchX; 1391b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastY = touchY; 1392b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return true; 1393b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1394b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1395b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private boolean repeatKey() { 1396b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Key key = mKeys[mRepeatKeyIndex]; 1397b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer detectAndSendKey(mCurrentKey, key.x, key.y, mLastTapTime); 1398b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return true; 1399b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1400b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1401b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer protected void swipeRight() { 1402b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardActionListener.swipeRight(); 1403b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1404b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1405b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer protected void swipeLeft() { 1406b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardActionListener.swipeLeft(); 1407b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1408b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1409b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer protected void swipeUp() { 1410b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardActionListener.swipeUp(); 1411b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1412b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1413b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer protected void swipeDown() { 1414b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardActionListener.swipeDown(); 1415b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1416b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1417b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void closing() { 1418b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mPreviewPopup.isShowing()) { 1419b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPreviewPopup.dismiss(); 1420b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1421b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer removeMessages(); 1422b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1423b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer dismissPopupKeyboard(); 1424b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mBuffer = null; 1425b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mCanvas = null; 1426b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mMiniKeyboardCache.clear(); 1427b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1428b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1429b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private void removeMessages() { 1430b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mHandler.removeMessages(MSG_REPEAT); 1431b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mHandler.removeMessages(MSG_LONGPRESS); 1432b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mHandler.removeMessages(MSG_SHOW_PREVIEW); 1433b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1434b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1435b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 1436b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void onDetachedFromWindow() { 1437b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer super.onDetachedFromWindow(); 1438b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer closing(); 1439b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1440b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1441b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void dismissPopupKeyboard() { 1442b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupKeyboardOnScreen = false; 1443b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mPopupKeyboardView != null && mPopupKeyboardView.getVisibility() == View.VISIBLE) { 1444b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (mPopupKeyboardView.mAnimateInValue == MAX_ANIMATION_VALUE) { 1445b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupKeyboardView.mAnimateOutAnimator.start(); 1446b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mScrimAlphaAnimator.setStartDelay(ANIMATE_SCRIM_DELAY_MS); 1447b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mScrimAlphaAnimator.reverse(); 1448b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 1449b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupKeyboardView.mAnimateInAnimator.reverse(); 1450b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mScrimAlphaAnimator.setStartDelay(0); 1451b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mScrimAlphaAnimator.reverse(); 1452b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1453b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1454b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1455b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer invalidateAllKeys(); 1456b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1457b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1458b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void setPopupKeyboardView(KeyboardView popupKeyboardView) { 1459b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupKeyboardView = popupKeyboardView; 1460b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPopupKeyboardView.mBackgroundColor = getResources().getColor(R.color.car_teal_700); 1461b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1462b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1463b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private void resetMultiTap() { 1464b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastSentIndex = NOT_A_KEY; 1465b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mTapCount = 0; 1466b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mLastTapTime = -1; 1467b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mInMultiTap = false; 1468b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1469b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1470b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private void checkMultiTap(long eventTime, int keyIndex) { 1471b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (keyIndex == NOT_A_KEY) return; 1472b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Key key = mKeys[keyIndex]; 1473b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (key.codes.length > 1) { 1474b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mInMultiTap = true; 1475b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (eventTime < mLastTapTime + MULTITAP_INTERVAL 1476b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer && keyIndex == mLastSentIndex) { 1477b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mTapCount = (mTapCount + 1) % key.codes.length; 1478b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return; 1479b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else { 1480b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mTapCount = -1; 1481b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return; 1482b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1483b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1484b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (eventTime > mLastTapTime + MULTITAP_INTERVAL || keyIndex != mLastSentIndex) { 1485b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer resetMultiTap(); 1486b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1487b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1488b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1489b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 1490b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public boolean onGenericMotionEvent(MotionEvent event) { 1491b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer // Close the touch keyboard when the user scrolls. 1492b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) { 1493b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardActionListener.stopInput(); 1494b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return true; 1495b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1496b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return false; 1497b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1498b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1499b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static class SwipeTracker { 1500b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1501b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer static final int NUM_PAST = 4; 1502b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer static final int LONGEST_PAST_TIME = 200; 1503b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1504b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final float mPastX[] = new float[NUM_PAST]; 1505b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final float mPastY[] = new float[NUM_PAST]; 1506b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final long mPastTime[] = new long[NUM_PAST]; 1507b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1508b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer float mYVelocity; 1509b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer float mXVelocity; 1510b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1511b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void clear() { 1512b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mPastTime[0] = 0; 1513b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1514b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1515b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void addMovement(MotionEvent ev) { 1516b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer long time = ev.getEventTime(); 1517b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int N = ev.getHistorySize(); 1518b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer for (int i=0; i<N; i++) { 1519b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i), 1520b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer ev.getHistoricalEventTime(i)); 1521b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1522b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer addPoint(ev.getX(), ev.getY(), time); 1523b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1524b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1525b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private void addPoint(float x, float y, long time) { 1526b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int drop = -1; 1527b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int i; 1528b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final long[] pastTime = mPastTime; 1529b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer for (i=0; i<NUM_PAST; i++) { 1530b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (pastTime[i] == 0) { 1531b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 1532b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } else if (pastTime[i] < time-LONGEST_PAST_TIME) { 1533b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer drop = i; 1534b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1535b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1536b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (i == NUM_PAST && drop < 0) { 1537b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer drop = 0; 1538b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1539b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (drop == i) drop--; 1540b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final float[] pastX = mPastX; 1541b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final float[] pastY = mPastY; 1542b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (drop >= 0) { 1543b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int start = drop+1; 1544b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int count = NUM_PAST-drop-1; 1545b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer System.arraycopy(pastX, start, pastX, 0, count); 1546b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer System.arraycopy(pastY, start, pastY, 0, count); 1547b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer System.arraycopy(pastTime, start, pastTime, 0, count); 1548b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer i -= (drop+1); 1549b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1550b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer pastX[i] = x; 1551b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer pastY[i] = y; 1552b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer pastTime[i] = time; 1553b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer i++; 1554b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (i < NUM_PAST) { 1555b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer pastTime[i] = 0; 1556b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1557b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1558b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1559b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void computeCurrentVelocity(int units) { 1560b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer computeCurrentVelocity(units, Float.MAX_VALUE); 1561b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1562b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1563b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void computeCurrentVelocity(int units, float maxVelocity) { 1564b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final float[] pastX = mPastX; 1565b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final float[] pastY = mPastY; 1566b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final long[] pastTime = mPastTime; 1567b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1568b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final float oldestX = pastX[0]; 1569b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final float oldestY = pastY[0]; 1570b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final long oldestTime = pastTime[0]; 1571b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer float accumX = 0; 1572b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer float accumY = 0; 1573b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer int N=0; 1574b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer while (N < NUM_PAST) { 1575b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (pastTime[N] == 0) { 1576b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 1577b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1578b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer N++; 1579b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1580b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1581b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer for (int i=1; i < N; i++) { 1582b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer final int dur = (int)(pastTime[i] - oldestTime); 1583b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (dur == 0) continue; 1584b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer float dist = pastX[i] - oldestX; 1585b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer float vel = (dist/dur) * units; // pixels/frame. 1586b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (accumX == 0) accumX = vel; 1587b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer else accumX = (accumX + vel) * .5f; 1588b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1589b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer dist = pastY[i] - oldestY; 1590b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer vel = (dist/dur) * units; // pixels/frame. 1591b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (accumY == 0) accumY = vel; 1592b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer else accumY = (accumY + vel) * .5f; 1593b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1594b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity) 1595b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer : Math.min(accumX, maxVelocity); 1596b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity) 1597b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer : Math.min(accumY, maxVelocity); 1598b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1599b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1600b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public float getXVelocity() { 1601b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return mXVelocity; 1602b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1603b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1604b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public float getYVelocity() { 1605b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer return mYVelocity; 1606b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1607b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1608b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1609b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private static class KeyboardHander extends Handler { 1610b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer private final WeakReference<KeyboardView> mKeyboardView; 1611b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1612b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public KeyboardHander(KeyboardView keyboardView) { 1613b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer mKeyboardView = new WeakReference<KeyboardView>(keyboardView); 1614b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1615b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer 1616b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer @Override 1617b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer public void handleMessage(Message msg) { 1618b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer KeyboardView keyboardView = mKeyboardView.get(); 1619b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (keyboardView != null) { 1620b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer switch (msg.what) { 1621b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case MSG_SHOW_PREVIEW: 1622b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer keyboardView.showKey(msg.arg1); 1623b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 1624b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case MSG_REMOVE_PREVIEW: 1625b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer keyboardView.mPreviewText.setVisibility(INVISIBLE); 1626b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 1627b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case MSG_REPEAT: 1628b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer if (keyboardView.repeatKey()) { 1629b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer Message repeat = Message.obtain(this, MSG_REPEAT); 1630b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer sendMessageDelayed(repeat, REPEAT_INTERVAL); 1631b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1632b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 1633b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer case MSG_LONGPRESS: 1634b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer keyboardView.openPopupIfRequired((MotionEvent) msg.obj); 1635b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer break; 1636b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1637b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1638b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1639b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer } 1640b8d05222f6f88e9c6ab64f88e9b9eba194a5c221Rakesh Iyer} 1641