/ime/latinime/src/com/googlecode/eyesfree/inputmethod/latin/LatinKeyboardBaseView.java
Java | 1809 lines | 1220 code | 204 blank | 385 comment | 255 complexity | 0ac10b54221dc7e48a00a88aa1d3d2b7 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.googlecode.eyesfree.inputmethod.latin; 18 19import android.content.Context; 20import android.content.pm.PackageManager; 21import android.content.res.Resources; 22import android.content.res.TypedArray; 23import android.graphics.Bitmap; 24import android.graphics.Canvas; 25import android.graphics.Paint; 26import android.graphics.Paint.Align; 27import android.graphics.PorterDuff; 28import android.graphics.Rect; 29import android.graphics.Region.Op; 30import android.graphics.Typeface; 31import android.graphics.drawable.Drawable; 32import android.inputmethodservice.Keyboard; 33import android.inputmethodservice.Keyboard.Key; 34import android.os.Build; 35import android.os.Handler; 36import android.os.Message; 37import android.os.SystemClock; 38import android.util.AttributeSet; 39import android.util.Log; 40import android.util.TypedValue; 41import android.view.Gravity; 42import android.view.KeyEvent; 43import android.view.LayoutInflater; 44import android.view.MotionEvent; 45import android.view.View; 46import android.view.ViewConfiguration; 47import android.view.ViewGroup.LayoutParams; 48import android.widget.PopupWindow; 49import android.widget.TextView; 50 51import com.googlecode.eyesfree.inputmethod.MultitouchGestureDetector; 52import com.googlecode.eyesfree.inputmethod.SegmentDetector; 53import com.googlecode.eyesfree.inputmethod.SegmentDetector.SegmentListener; 54import com.googlecode.eyesfree.inputmethod.SimpleMultitouchGestureListener; 55import com.googlecode.eyesfree.utils.compat.MotionEventCompatUtils; 56 57import java.util.ArrayList; 58import java.util.HashMap; 59import java.util.LinkedList; 60import java.util.List; 61import java.util.WeakHashMap; 62 63/** 64 * A view that renders a virtual {@link LatinKeyboard}. It handles rendering of keys and detecting 65 * key presses and touch movements. TODO: References to LatinKeyboard in this class should be 66 * replaced with ones to its base class. 67 * 68 * @attr ref R.styleable#LatinKeyboardBaseView_keyBackground 69 * @attr ref R.styleable#LatinKeyboardBaseView_keyPreviewLayout 70 * @attr ref R.styleable#LatinKeyboardBaseView_keyPreviewOffset 71 * @attr ref R.styleable#LatinKeyboardBaseView_labelTextSize 72 * @attr ref R.styleable#LatinKeyboardBaseView_keyTextSize 73 * @attr ref R.styleable#LatinKeyboardBaseView_keyTextColor 74 * @attr ref R.styleable#LatinKeyboardBaseView_verticalCorrection 75 * @attr ref R.styleable#LatinKeyboardBaseView_popupLayout 76 */ 77public class LatinKeyboardBaseView extends View implements PointerTracker.UIProxy { 78 private static final String TAG = "LatinKeyboardBaseView"; 79 private static final boolean DEBUG = false; 80 81 public static final int NOT_A_TOUCH_COORDINATE = -1; 82 83 public interface OnKeyboardActionListener { 84 85 /** 86 * Called when the user presses a key. This is sent before the {@link #onKey} is called. For 87 * keys that repeat, this is only called once. 88 * 89 * @param key the key being pressed 90 */ 91 void onPress(Key key); 92 93 /** 94 * Called when the user leaves a key without lifting their finger. This is send before the 95 * {@link #onKey} is called. For keys that repeat, this is only called once. 96 * 97 * @param key the key that was left 98 */ 99 void onLeaving(Key key); 100 101 /** 102 * Called when the user releases a key. This is sent after the {@link #onKey} is called. For 103 * keys that repeat, this is only called once. 104 * 105 * @param key the key that was released 106 */ 107 void onRelease(Key key); 108 109 /** 110 * Send a key press to the listener. 111 * 112 * @param primaryCode this is the key that was pressed 113 * @param keyCodes the codes for all the possible alternative keys with the primary code 114 * being the first. If the primary key code is a single character such as an 115 * alphabet or number or symbol, the alternatives will include other characters 116 * that may be on the same key or adjacent keys. These codes are useful to 117 * correct for accidental presses of a key adjacent to the intended key. 118 * @param x x-coordinate pixel of touched event. If onKey is not called by onTouchEvent, the 119 * value should be NOT_A_TOUCH_COORDINATE. 120 * @param y y-coordinate pixel of touched event. If onKey is not called by onTouchEvent, the 121 * value should be NOT_A_TOUCH_COORDINATE. 122 */ 123 void onKey(int primaryCode, int[] keyCodes, int x, int y); 124 125 /** 126 * Sends a sequence of characters to the listener. 127 * 128 * @param text the sequence of characters to be displayed. 129 */ 130 void onText(CharSequence text); 131 132 /** 133 * Called when user released a finger outside any key. 134 */ 135 void onCancel(); 136 137 /** 138 * Called when the user quickly moves the finger from right to left. 139 * 140 * @param pointerCount 141 * @return <code>true</code> if the event was consumed. 142 */ 143 boolean swipeLeft(int pointerCount); 144 145 /** 146 * Called when the user quickly moves the finger from left to right. 147 * 148 * @param pointerCount 149 * @return <code>true</code> if the event was consumed. 150 */ 151 boolean swipeRight(int pointerCount); 152 153 /** 154 * Called when the user quickly moves the finger from up to down. 155 * 156 * @param pointerCount 157 * @return <code>true</code> if the event was consumed. 158 */ 159 boolean swipeDown(int pointerCount); 160 161 /** 162 * Called when the user quickly moves the finger from down to up. 163 * 164 * @param pointerCount 165 * @return <code>true</code> if the event was consumed. 166 */ 167 boolean swipeUp(int pointerCount); 168 169 /** 170 * Called when the user quickly taps the finger on screen. 171 * 172 * @param pointerCount 173 * @return <code>true</code> if the event was consumed. 174 */ 175 boolean singleTap(int pointerCount); 176 177 /** 178 * Called when the user double taps the finger on screen. 179 * 180 * @param pointerCount 181 * @return <code>true</code> if the event was consumed. 182 */ 183 boolean doubleTap(int pointerCount); 184 185 /** 186 * Called when the user long-presses a finger on screen. 187 * 188 * @param pointerCount 189 * @return <code>true</code> if the event was consumed. 190 */ 191 boolean longPress(int pointerCount); 192 193 /** 194 * Called when the user moves the finger outside the keyboard area. 195 */ 196 void exploreKeyboardArea(); 197 198 /** 199 * Called when the user moves the finger outside the keyboard area. 200 */ 201 void enteredSegment(int segment); 202 203 /** 204 * Called when the user selects a segment. 205 */ 206 void selectedSegment(int segment); 207 208 /** 209 * Called when the user moves the finger outside the keyboard area. 210 */ 211 void leftKeyboardArea(); 212 213 /** 214 * Called when the user moves the finger outside the keyboard area, then back inside the 215 * keyboard area. 216 */ 217 void enteredKeyboardArea(); 218 219 /** 220 * Called when a finger is lifted outside the keyboard area. 221 */ 222 void upOutsideKeyboardArea(); 223 } 224 225 // Timing constants 226 private final int mKeyRepeatInterval; 227 228 // Miscellaneous constants 229 /* package */static final int NOT_A_KEY = -1; 230 private static final int[] LONG_PRESSABLE_STATE_SET = { 231 android.R.attr.state_long_pressable 232 }; 233 private static final int NUMBER_HINT_VERTICAL_ADJUSTMENT_PIXEL = -1; 234 235 // XML attribute 236 private int mKeyTextSize; 237 private int mKeyTextColor; 238 private Typeface mKeyTextStyle = Typeface.DEFAULT; 239 private int mLabelTextSize; 240 private int mSymbolColorScheme = 0; 241 private int mShadowColor; 242 private float mShadowRadius; 243 private Drawable mKeyBackground; 244 private float mBackgroundDimAmount; 245 private float mKeyHysteresisDistance; 246 private float mVerticalCorrection; 247 private int mPreviewOffset; 248 private int mPreviewHeight; 249 private int mPopupLayout; 250 251 // Main keyboard 252 private Keyboard mKeyboard; 253 private Key[] mKeys; 254 // TODO this attribute should be gotten from Keyboard. 255 private int mKeyboardVerticalGap; 256 257 // Key preview popup 258 private TextView mPreviewText; 259 private PopupWindow mPreviewPopup; 260 private int mPreviewTextSizeLarge; 261 private int[] mOffsetInWindow; 262 private int mOldPreviewKeyIndex = NOT_A_KEY; 263 private boolean mShowPreview = true; 264 private boolean mShowTouchPoints = true; 265 private int mPopupPreviewOffsetX; 266 private int mPopupPreviewOffsetY; 267 private int mWindowY; 268 private int mPopupPreviewDisplayedY; 269 private final int mDelayBeforePreview; 270 private final int mDelayAfterPreview; 271 272 // Popup mini keyboard 273 private PopupWindow mMiniKeyboardPopup; 274 private LatinKeyboardBaseView mMiniKeyboard; 275 private View mMiniKeyboardParent; 276 private final WeakHashMap<Key, View> mMiniKeyboardCache = new WeakHashMap<Key, View>(); 277 private int mMiniKeyboardOriginX; 278 private int mMiniKeyboardOriginY; 279 private long mMiniKeyboardPopupTime; 280 private int[] mWindowOffset; 281 private final float mMiniKeyboardSlideAllowance; 282 private int mMiniKeyboardTrackerId; 283 284 /** Listener for {@link OnKeyboardActionListener}. */ 285 private OnKeyboardActionListener mKeyboardActionListener; 286 287 private final ArrayList<PointerTracker> mPointerTrackers = new ArrayList<PointerTracker>(); 288 289 // TODO: Let the PointerTracker class manage this pointer queue 290 private final PointerQueue mPointerQueue = new PointerQueue(); 291 292 private final boolean mHasDistinctMultitouch; 293 private int mOldPointerCount = 1; 294 295 // Accessibility 296 private final int mTouchSlop; 297 private boolean mIsAccessibilityEnabled; 298 private boolean mInKeyboardArea = false; 299 300 protected KeyDetector mKeyDetector = new ProximityKeyDetector(); 301 302 // Segment detector 303 private SegmentDetector mSegmentDetector; 304 305 // Swipe gesture detector 306 private MultitouchGestureDetector mGestureDetector; 307 308 // Drawing 309 /** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/ 310 private boolean mDrawPending; 311 /** The dirty region in the keyboard bitmap */ 312 private final Rect mDirtyRect = new Rect(); 313 /** The keyboard bitmap for faster updates */ 314 private Bitmap mBuffer; 315 /** 316 * Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. 317 */ 318 private boolean mKeyboardChanged; 319 private Key mInvalidatedKey; 320 /** The canvas for the above mutable keyboard bitmap */ 321 private Canvas mCanvas; 322 private final Paint mPaint; 323 private final Rect mPadding; 324 private final Rect mClipRegion = new Rect(0, 0, 0, 0); 325 // This map caches key label text height in pixel as value and key label 326 // text size as map key. 327 private final HashMap<Integer, Integer> mTextHeightCache = new HashMap<Integer, Integer>(); 328 // Distance from horizontal center of the key, proportional to key label 329 // text height. 330 private final float KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR = 0.55f; 331 private final String KEY_LABEL_HEIGHT_REFERENCE_CHAR = "H"; 332 333 private final UIHandler mHandler = new UIHandler(); 334 335 class UIHandler extends Handler { 336 private static final int MSG_POPUP_PREVIEW = 1; 337 private static final int MSG_DISMISS_PREVIEW = 2; 338 private static final int MSG_REPEAT_KEY = 3; 339 private static final int MSG_LONGPRESS_KEY = 4; 340 341 private boolean mInKeyRepeat; 342 343 @Override 344 public void handleMessage(Message msg) { 345 switch (msg.what) { 346 case MSG_POPUP_PREVIEW: 347 showKey(msg.arg1, (PointerTracker) msg.obj); 348 break; 349 case MSG_DISMISS_PREVIEW: 350 mPreviewPopup.dismiss(); 351 break; 352 case MSG_REPEAT_KEY: { 353 final PointerTracker tracker = (PointerTracker) msg.obj; 354 tracker.repeatKey(msg.arg1); 355 startKeyRepeatTimer(mKeyRepeatInterval, msg.arg1, tracker); 356 break; 357 } 358 case MSG_LONGPRESS_KEY: { 359 final PointerTracker tracker = (PointerTracker) msg.obj; 360 openPopupIfRequired(msg.arg1, tracker); 361 break; 362 } 363 } 364 } 365 366 public void popupPreview(long delay, int keyIndex, PointerTracker tracker) { 367 removeMessages(MSG_POPUP_PREVIEW); 368 if (mPreviewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) { 369 // Show right away, if it's already visible and finger is moving 370 // around 371 showKey(keyIndex, tracker); 372 } else { 373 sendMessageDelayed(obtainMessage(MSG_POPUP_PREVIEW, keyIndex, 0, tracker), 374 delay); 375 } 376 } 377 378 public void cancelPopupPreview() { 379 removeMessages(MSG_POPUP_PREVIEW); 380 } 381 382 public void dismissPreview(long delay) { 383 if (mPreviewPopup.isShowing()) { 384 sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay); 385 } 386 } 387 388 public void cancelDismissPreview() { 389 removeMessages(MSG_DISMISS_PREVIEW); 390 } 391 392 public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) { 393 mInKeyRepeat = true; 394 sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, keyIndex, 0, tracker), delay); 395 } 396 397 public void cancelKeyRepeatTimer() { 398 mInKeyRepeat = false; 399 removeMessages(MSG_REPEAT_KEY); 400 } 401 402 public boolean isInKeyRepeat() { 403 return mInKeyRepeat; 404 } 405 406 public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) { 407 // Accessibility disables long press because users are likely to 408 // need to pause on a key 409 // for an unspecified duration in order to hear the key's spoken 410 // description. 411 if (mIsAccessibilityEnabled) { 412 return; 413 } 414 415 removeMessages(MSG_LONGPRESS_KEY); 416 sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, keyIndex, 0, tracker), delay); 417 } 418 419 public void cancelLongPressTimer() { 420 removeMessages(MSG_LONGPRESS_KEY); 421 } 422 423 public void cancelKeyTimers() { 424 cancelKeyRepeatTimer(); 425 cancelLongPressTimer(); 426 } 427 428 public void cancelAllMessages() { 429 cancelKeyTimers(); 430 cancelPopupPreview(); 431 cancelDismissPreview(); 432 } 433 } 434 435 static class PointerQueue { 436 private LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>(); 437 438 public void add(PointerTracker tracker) { 439 mQueue.add(tracker); 440 } 441 442 public int lastIndexOf(PointerTracker tracker) { 443 LinkedList<PointerTracker> queue = mQueue; 444 for (int index = queue.size() - 1; index >= 0; index--) { 445 PointerTracker t = queue.get(index); 446 if (t == tracker) 447 return index; 448 } 449 return -1; 450 } 451 452 public void releaseAllPointersOlderThan(PointerTracker tracker, long eventTime) { 453 LinkedList<PointerTracker> queue = mQueue; 454 int oldestPos = 0; 455 for (PointerTracker t = queue.get(oldestPos); t != tracker; t = queue.get(oldestPos)) { 456 if (t.isModifier()) { 457 oldestPos++; 458 } else { 459 t.onUpEvent(t.getLastX(), t.getLastY(), eventTime); 460 t.setAlreadyProcessed(); 461 queue.remove(oldestPos); 462 } 463 } 464 } 465 466 public void releaseAllPointersExcept(PointerTracker tracker, long eventTime) { 467 for (PointerTracker t : mQueue) { 468 if (t == tracker) 469 continue; 470 t.onUpEvent(t.getLastX(), t.getLastY(), eventTime); 471 t.setAlreadyProcessed(); 472 } 473 mQueue.clear(); 474 if (tracker != null) 475 mQueue.add(tracker); 476 } 477 478 public void remove(PointerTracker tracker) { 479 mQueue.remove(tracker); 480 } 481 } 482 483 public LatinKeyboardBaseView(Context context, AttributeSet attrs) { 484 this(context, attrs, R.attr.keyboardViewStyle); 485 } 486 487 public LatinKeyboardBaseView(Context context, AttributeSet attrs, int defStyle) { 488 super(context, attrs, defStyle); 489 490 TypedArray a = context.obtainStyledAttributes( 491 attrs, R.styleable.LatinKeyboardBaseView, defStyle, R.style.LatinKeyboardBaseView); 492 LayoutInflater inflate = 493 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 494 int previewLayout = 0; 495 int keyTextSize = 0; 496 497 int n = a.getIndexCount(); 498 499 for (int i = 0; i < n; i++) { 500 int attr = a.getIndex(i); 501 502 switch (attr) { 503 case R.styleable.LatinKeyboardBaseView_keyBackground: 504 mKeyBackground = a.getDrawable(attr); 505 break; 506 case R.styleable.LatinKeyboardBaseView_keyHysteresisDistance: 507 mKeyHysteresisDistance = a.getDimensionPixelOffset(attr, 0); 508 break; 509 case R.styleable.LatinKeyboardBaseView_verticalCorrection: 510 mVerticalCorrection = a.getDimensionPixelOffset(attr, 0); 511 break; 512 case R.styleable.LatinKeyboardBaseView_keyPreviewLayout: 513 previewLayout = a.getResourceId(attr, 0); 514 break; 515 case R.styleable.LatinKeyboardBaseView_keyPreviewOffset: 516 mPreviewOffset = a.getDimensionPixelOffset(attr, 0); 517 break; 518 case R.styleable.LatinKeyboardBaseView_keyPreviewHeight: 519 mPreviewHeight = a.getDimensionPixelSize(attr, 80); 520 break; 521 case R.styleable.LatinKeyboardBaseView_keyTextSize: 522 mKeyTextSize = a.getDimensionPixelSize(attr, 18); 523 break; 524 case R.styleable.LatinKeyboardBaseView_keyTextColor: 525 mKeyTextColor = a.getColor(attr, 0xFF000000); 526 break; 527 case R.styleable.LatinKeyboardBaseView_labelTextSize: 528 mLabelTextSize = a.getDimensionPixelSize(attr, 14); 529 break; 530 case R.styleable.LatinKeyboardBaseView_popupLayout: 531 mPopupLayout = a.getResourceId(attr, 0); 532 break; 533 case R.styleable.LatinKeyboardBaseView_shadowColor: 534 mShadowColor = a.getColor(attr, 0); 535 break; 536 case R.styleable.LatinKeyboardBaseView_shadowRadius: 537 mShadowRadius = a.getFloat(attr, 0f); 538 break; 539 // TODO: Use Theme 540 // (android.R.styleable.Theme_backgroundDimAmount) 541 case R.styleable.LatinKeyboardBaseView_backgroundDimAmount: 542 mBackgroundDimAmount = a.getFloat(attr, 0.5f); 543 break; 544 // case android.R.styleable. 545 case R.styleable.LatinKeyboardBaseView_keyTextStyle: 546 int textStyle = a.getInt(attr, 0); 547 switch (textStyle) { 548 case 0: 549 mKeyTextStyle = Typeface.DEFAULT; 550 break; 551 case 1: 552 mKeyTextStyle = Typeface.DEFAULT_BOLD; 553 break; 554 default: 555 mKeyTextStyle = Typeface.defaultFromStyle(textStyle); 556 break; 557 } 558 break; 559 case R.styleable.LatinKeyboardBaseView_symbolColorScheme: 560 mSymbolColorScheme = a.getInt(attr, 0); 561 break; 562 } 563 } 564 565 final Resources res = getResources(); 566 567 mPreviewPopup = new PopupWindow(context); 568 if (previewLayout != 0) { 569 mPreviewText = (TextView) inflate.inflate(previewLayout, null); 570 mPreviewTextSizeLarge = (int) res.getDimension(R.dimen.key_preview_text_size_large); 571 mPreviewPopup.setContentView(mPreviewText); 572 mPreviewPopup.setBackgroundDrawable(null); 573 } else { 574 mShowPreview = false; 575 } 576 mPreviewPopup.setTouchable(false); 577 mPreviewPopup.setAnimationStyle(R.style.KeyPreviewAnimation); 578 mDelayBeforePreview = res.getInteger(R.integer.config_delay_before_preview); 579 mDelayAfterPreview = res.getInteger(R.integer.config_delay_after_preview); 580 581 mMiniKeyboardParent = this; 582 mMiniKeyboardPopup = new PopupWindow(context); 583 mMiniKeyboardPopup.setBackgroundDrawable(null); 584 mMiniKeyboardPopup.setAnimationStyle(R.style.MiniKeyboardAnimation); 585 586 mPaint = new Paint(); 587 mPaint.setAntiAlias(true); 588 mPaint.setTextSize(keyTextSize); 589 mPaint.setTextAlign(Align.CENTER); 590 mPaint.setAlpha(255); 591 592 mPadding = new Rect(0, 0, 0, 0); 593 mKeyBackground.getPadding(mPadding); 594 mMiniKeyboardSlideAllowance = res.getDimension(R.dimen.mini_keyboard_slide_allowance); 595 596 mSegmentDetector = new SegmentDetector(); 597 mSegmentDetector.setListener(segmentListener); 598 mSegmentDetector.updateDimensions(this); 599 600 mGestureDetector = new MultitouchGestureDetector(context); 601 mGestureDetector.setListener(multitouchListener); 602 mGestureDetector.setIsLongPressEnabled(false); 603 mGestureDetector.setIsDoubleTapEnabled(true); 604 mGestureDetector.setDoubleTapMinFingers(2); 605 606 mHasDistinctMultitouch = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) 607 && context.getPackageManager().hasSystemFeature( 608 PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT); 609 mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval); 610 mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 611 } 612 613 public void setOnKeyboardActionListener(OnKeyboardActionListener listener) { 614 mKeyboardActionListener = listener; 615 for (PointerTracker tracker : mPointerTrackers) { 616 tracker.setOnKeyboardActionListener(listener); 617 } 618 } 619 620 /** 621 * Returns the {@link OnKeyboardActionListener} object. 622 * 623 * @return the listener attached to this keyboard 624 */ 625 protected OnKeyboardActionListener getOnKeyboardActionListener() { 626 return mKeyboardActionListener; 627 } 628 629 /** 630 * Attaches a keyboard to this view. The keyboard can be switched at any time and the view will 631 * re-layout itself to accommodate the keyboard. 632 * 633 * @see Keyboard 634 * @see #getKeyboard() 635 * @param keyboard the keyboard to display in this view 636 */ 637 public void setKeyboard(Keyboard keyboard) { 638 if (mKeyboard != null) { 639 dismissKeyPreview(); 640 } 641 // Remove any pending messages, except dismissing preview 642 mHandler.cancelKeyTimers(); 643 mHandler.cancelPopupPreview(); 644 mKeyboard = keyboard; 645 LatinImeLogger.onSetKeyboard(keyboard); 646 mKeys = mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), 647 -getPaddingTop() + mVerticalCorrection); 648 mKeyboardVerticalGap = (int) getResources().getDimension(R.dimen.key_bottom_gap); 649 for (PointerTracker tracker : mPointerTrackers) { 650 tracker.setKeyboard(mKeys, mKeyHysteresisDistance); 651 } 652 requestLayout(); 653 // Hint to reallocate the buffer if the size changed 654 mKeyboardChanged = true; 655 invalidateAllKeys(); 656 computeProximityThreshold(keyboard); 657 mMiniKeyboardCache.clear(); 658 } 659 660 /** 661 * Returns the current keyboard being displayed by this view. 662 * 663 * @return the currently attached keyboard 664 * @see #setKeyboard(Keyboard) 665 */ 666 public Keyboard getKeyboard() { 667 return mKeyboard; 668 } 669 670 /** 671 * Return whether the device has distinct multi-touch panel. 672 * 673 * @return true if the device has distinct multi-touch panel. 674 */ 675 @Override 676 public boolean hasDistinctMultitouch() { 677 return mHasDistinctMultitouch; 678 } 679 680 /** 681 * Enables or disables accessibility. 682 * 683 * @param accessibilityEnabled whether or not to enable accessibility 684 */ 685 public void setAccessibilityEnabled(boolean accessibilityEnabled) { 686 mIsAccessibilityEnabled = accessibilityEnabled; 687 688 // Propagate this change to all existing pointer trackers. 689 for (PointerTracker pointerTracker : mPointerTrackers) { 690 pointerTracker.setAccessibilityEnabled(accessibilityEnabled); 691 } 692 } 693 694 /** 695 * Return whether the device has accessibility turned on. 696 * 697 * @return true if the device has accessibility turned on. 698 */ 699 @Override 700 public boolean isAccessibilityEnabled() { 701 return mIsAccessibilityEnabled; 702 } 703 704 /** 705 * Sets the state of the shift key of the keyboard, if any. 706 * 707 * @param shifted whether or not to enable the state of the shift key 708 * @return true if the shift key state changed, false if there was no change 709 */ 710 public boolean setShifted(boolean shifted) { 711 if (mKeyboard != null) { 712 if (mKeyboard.setShifted(shifted)) { 713 // The whole keyboard probably needs to be redrawn 714 invalidateAllKeys(); 715 return true; 716 } 717 } 718 return false; 719 } 720 721 /** 722 * Returns the state of the shift key of the keyboard, if any. 723 * 724 * @return true if the shift is in a pressed state, false otherwise. If there is no shift key on 725 * the keyboard or there is no keyboard attached, it returns false. 726 */ 727 public boolean isShifted() { 728 if (mKeyboard != null) { 729 return mKeyboard.isShifted(); 730 } 731 return false; 732 } 733 734 /** 735 * Enables or disables long-press. 736 * 737 * @param longPressEnabled whether or not to enable long-press 738 */ 739 public void setLongPressEnabled(boolean longPressEnabled) { 740 mGestureDetector.setIsLongPressEnabled(longPressEnabled); 741 } 742 743 /** 744 * Enables or disables the key feedback popup. This is a popup that shows a magnified version of 745 * the depressed key. By default the preview is enabled. 746 * 747 * @param previewEnabled whether or not to enable the key feedback popup 748 * @see #isPreviewEnabled() 749 */ 750 public void setPreviewEnabled(boolean previewEnabled) { 751 mShowPreview = previewEnabled; 752 } 753 754 /** 755 * Returns the enabled state of the key feedback popup. 756 * 757 * @return whether or not the key feedback popup is enabled 758 * @see #setPreviewEnabled(boolean) 759 */ 760 public boolean isPreviewEnabled() { 761 return mShowPreview; 762 } 763 764 public int getSymbolColorScheme() { 765 return mSymbolColorScheme; 766 } 767 768 public void setPopupParent(View v) { 769 mMiniKeyboardParent = v; 770 } 771 772 public void setPopupOffset(int x, int y) { 773 mPopupPreviewOffsetX = x; 774 mPopupPreviewOffsetY = y; 775 mPreviewPopup.dismiss(); 776 } 777 778 /** 779 * When enabled, calls to {@link OnKeyboardActionListener#onKey} will include key codes for 780 * adjacent keys. When disabled, only the primary key code will be reported. 781 * 782 * @param enabled whether or not the proximity correction is enabled 783 */ 784 public void setProximityCorrectionEnabled(boolean enabled) { 785 mKeyDetector.setProximityCorrectionEnabled(enabled); 786 } 787 788 /** 789 * Returns true if proximity correction is enabled. 790 */ 791 public boolean isProximityCorrectionEnabled() { 792 return mKeyDetector.isProximityCorrectionEnabled(); 793 } 794 795 protected CharSequence adjustCase(CharSequence label) { 796 if (mKeyboard.isShifted() && label != null && label.length() < 3 797 && Character.isLowerCase(label.charAt(0))) { 798 label = label.toString().toUpperCase(); 799 } 800 return label; 801 } 802 803 @Override 804 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 805 // Round up a little 806 if (mKeyboard == null) { 807 setMeasuredDimension( 808 getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom()); 809 } else { 810 int width = mKeyboard.getMinWidth() + getPaddingLeft() + getPaddingRight(); 811 if (MeasureSpec.getSize(widthMeasureSpec) < width + 10) { 812 width = MeasureSpec.getSize(widthMeasureSpec); 813 } 814 setMeasuredDimension( 815 getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), 816 mKeyboard.getHeight() + getPaddingTop() + getPaddingBottom()); 817 } 818 } 819 820 /** 821 * Compute the average distance between adjacent keys (horizontally and vertically) and square 822 * it to get the proximity threshold. We use a square here and in computing the touch distance 823 * from a key's center to avoid taking a square root. 824 * 825 * @param keyboard 826 */ 827 private void computeProximityThreshold(Keyboard keyboard) { 828 if (keyboard == null) 829 return; 830 final Key[] keys = mKeys; 831 if (keys == null) 832 return; 833 int length = keys.length; 834 int dimensionSum = 0; 835 for (int i = 0; i < length; i++) { 836 Key key = keys[i]; 837 dimensionSum += Math.min(key.width, key.height + mKeyboardVerticalGap) + key.gap; 838 } 839 if (dimensionSum < 0 || length == 0) 840 return; 841 mKeyDetector.setProximityThreshold((int) (dimensionSum * 1.4f / length)); 842 } 843 844 @Override 845 public void onSizeChanged(int w, int h, int oldw, int oldh) { 846 super.onSizeChanged(w, h, oldw, oldh); 847 // Release the buffer, if any and it will be reallocated on the next 848 // draw 849 mBuffer = null; 850 mSegmentDetector.updateDimensions(this); 851 } 852 853 @Override 854 public void onDraw(Canvas canvas) { 855 super.onDraw(canvas); 856 if (mDrawPending || mBuffer == null || mKeyboardChanged) { 857 onBufferDraw(); 858 } 859 canvas.drawBitmap(mBuffer, 0, 0, null); 860 } 861 862 private void onBufferDraw() { 863 if (mBuffer == null || mKeyboardChanged) { 864 if (mBuffer == null || mKeyboardChanged && 865 (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) { 866 // Make sure our bitmap is at least 1x1 867 final int width = Math.max(1, getWidth()); 868 final int height = Math.max(1, getHeight()); 869 mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 870 mCanvas = new Canvas(mBuffer); 871 } 872 invalidateAllKeys(); 873 mKeyboardChanged = false; 874 } 875 final Canvas canvas = mCanvas; 876 canvas.clipRect(mDirtyRect, Op.REPLACE); 877 878 if (mKeyboard == null) 879 return; 880 881 final Paint paint = mPaint; 882 final Drawable keyBackground = mKeyBackground; 883 final Rect clipRegion = mClipRegion; 884 final Rect padding = mPadding; 885 final int kbdPaddingLeft = getPaddingLeft(); 886 final int kbdPaddingTop = getPaddingTop(); 887 final Key[] keys = mKeys; 888 final Key invalidKey = mInvalidatedKey; 889 890 paint.setColor(mKeyTextColor); 891 boolean drawSingleKey = false; 892 if (invalidKey != null && canvas.getClipBounds(clipRegion)) { 893 // TODO we should use Rect.inset and Rect.contains here. 894 // Is clipRegion completely contained within the invalidated key? 895 if (invalidKey.x + kbdPaddingLeft - 1 <= clipRegion.left && 896 invalidKey.y + kbdPaddingTop - 1 <= clipRegion.top && 897 invalidKey.x + invalidKey.width + kbdPaddingLeft + 1 >= clipRegion.right && 898 invalidKey.y + invalidKey.height + kbdPaddingTop + 1 >= clipRegion.bottom) { 899 drawSingleKey = true; 900 } 901 } 902 canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); 903 final int keyCount = keys.length; 904 for (int i = 0; i < keyCount; i++) { 905 final Key key = keys[i]; 906 if (drawSingleKey && invalidKey != key) { 907 continue; 908 } 909 int[] drawableState = key.getCurrentDrawableState(); 910 keyBackground.setState(drawableState); 911 912 // Switch the character to uppercase if shift is pressed 913 String label = key.label == null ? null : adjustCase(key.label).toString(); 914 915 final Rect bounds = keyBackground.getBounds(); 916 if (key.width != bounds.right || key.height != bounds.bottom) { 917 keyBackground.setBounds(0, 0, key.width, key.height); 918 } 919 canvas.translate(key.x + kbdPaddingLeft, key.y + kbdPaddingTop); 920 keyBackground.draw(canvas); 921 922 boolean shouldDrawIcon = true; 923 if (label != null) { 924 // For characters, use large font. For labels like "Done", use 925 // small font. 926 final int labelSize; 927 if (label.length() > 1 && key.codes.length < 2) { 928 labelSize = mLabelTextSize; 929 paint.setTypeface(Typeface.DEFAULT_BOLD); 930 } else { 931 labelSize = mKeyTextSize; 932 paint.setTypeface(mKeyTextStyle); 933 } 934 paint.setTextSize(labelSize); 935 936 Integer labelHeightValue = mTextHeightCache.get(labelSize); 937 final int labelHeight; 938 if (labelHeightValue != null) { 939 labelHeight = labelHeightValue; 940 } else { 941 Rect textBounds = new Rect(); 942 paint.getTextBounds(KEY_LABEL_HEIGHT_REFERENCE_CHAR, 0, 1, textBounds); 943 labelHeight = textBounds.height(); 944 mTextHeightCache.put(labelSize, labelHeight); 945 } 946 947 // Draw a drop shadow for the text 948 paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor); 949 final int centerX = (key.width + padding.left - padding.right) / 2; 950 final int centerY = (key.height + padding.top - padding.bottom) / 2; 951 final float baseline = centerY 952 + labelHeight * KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR; 953 canvas.drawText(label, centerX, baseline, paint); 954 // Turn off drop shadow 955 paint.setShadowLayer(0, 0, 0, 0); 956 957 // Usually don't draw icon if label is not null, but we draw 958 // icon for the number 959 // hint and popup hint. 960 shouldDrawIcon = shouldDrawLabelAndIcon(key); 961 } 962 if (key.icon != null && shouldDrawIcon) { 963 // Special handing for the upper-right number hint icons 964 final int drawableWidth; 965 final int drawableHeight; 966 final int drawableX; 967 final int drawableY; 968 if (shouldDrawIconFully(key)) { 969 drawableWidth = key.width; 970 drawableHeight = key.height; 971 drawableX = 0; 972 drawableY = NUMBER_HINT_VERTICAL_ADJUSTMENT_PIXEL; 973 } else { 974 drawableWidth = key.icon.getIntrinsicWidth(); 975 drawableHeight = key.icon.getIntrinsicHeight(); 976 drawableX = (key.width + padding.left - padding.right - drawableWidth) / 2; 977 drawableY = (key.height + padding.top - padding.bottom - drawableHeight) / 2; 978 } 979 canvas.translate(drawableX, drawableY); 980 key.icon.setBounds(0, 0, drawableWidth, drawableHeight); 981 key.icon.draw(canvas); 982 canvas.translate(-drawableX, -drawableY); 983 } 984 canvas.translate(-key.x - kbdPaddingLeft, -key.y - kbdPaddingTop); 985 } 986 mInvalidatedKey = null; 987 // Overlay a dark rectangle to dim the keyboard 988 if (mMiniKeyboard != null) { 989 paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24); 990 canvas.drawRect(0, 0, getWidth(), getHeight(), paint); 991 } 992 993 if (DEBUG) { 994 if (mShowTouchPoints) { 995 for (PointerTracker tracker : mPointerTrackers) { 996 int startX = tracker.getStartX(); 997 int startY = tracker.getStartY(); 998 int lastX = tracker.getLastX(); 999 int lastY = tracker.getLastY(); 1000 paint.setAlpha(128); 1001 paint.setColor(0xFFFF0000); 1002 canvas.drawCircle(startX, startY, 3, paint); 1003 canvas.drawLine(startX, startY, lastX, lastY, paint); 1004 paint.setColor(0xFF0000FF); 1005 canvas.drawCircle(lastX, lastY, 3, paint); 1006 paint.setColor(0xFF00FF00); 1007 canvas.drawCircle((startX + lastX) / 2, (startY + lastY) / 2, 2, paint); 1008 } 1009 } 1010 } 1011 1012 mDrawPending = false; 1013 mDirtyRect.setEmpty(); 1014 } 1015 1016 // TODO: clean up this method. 1017 private void dismissKeyPreview() { 1018 for (PointerTracker tracker : mPointerTrackers) 1019 tracker.updateKey(NOT_A_KEY); 1020 showPreview(NOT_A_KEY, null); 1021 } 1022 1023 @Override 1024 public void showPreview(int keyIndex, PointerTracker tracker) { 1025 int oldKeyIndex = mOldPreviewKeyIndex; 1026 mOldPreviewKeyIndex = keyIndex; 1027 final boolean isLanguageSwitchEnabled = (mKeyboard instanceof LatinKeyboard) 1028 && ((LatinKeyboard) mKeyboard).isLanguageSwitchEnabled(); 1029 // We should re-draw popup preview when 1) we need to hide the preview, 1030 // 2) we will show 1031 // the space key preview and 3) pointer moves off the space key to other 1032 // letter key, we 1033 // should hide the preview of the previous key. 1034 final boolean hidePreviewOrShowSpaceKeyPreview = (tracker == null) 1035 || tracker.isSpaceKey(keyIndex) || tracker.isSpaceKey(oldKeyIndex); 1036 // If key changed and preview is on or the key is space (language switch 1037 // is enabled) 1038 if (oldKeyIndex != keyIndex 1039 && (mShowPreview 1040 || (hidePreviewOrShowSpaceKeyPreview && isLanguageSwitchEnabled))) { 1041 if (keyIndex == NOT_A_KEY) { 1042 mHandler.cancelPopupPreview(); 1043 mHandler.dismissPreview(mDelayAfterPreview); 1044 } else if (tracker != null) { 1045 mHandler.popupPreview(mDelayBeforePreview, keyIndex, tracker); 1046 } 1047 } 1048 } 1049 1050 private void showKey(final int keyIndex, PointerTracker tracker) { 1051 Key key = tracker.getKey(keyIndex); 1052 if (key == null) 1053 return; 1054 // Should not draw hint icon in key preview 1055 if (key.icon != null && !shouldDrawLabelAndIcon(key)) { 1056 mPreviewText.setCompoundDrawables(null, null, null, 1057 key.iconPreview != null ? key.iconPreview : key.icon); 1058 mPreviewText.setText(null); 1059 } else { 1060 mPreviewText.setCompoundDrawables(null, null, null, null); 1061 mPreviewText.setText(adjustCase(tracker.getPreviewText(key))); 1062 if (key.label.length() > 1 && key.codes.length < 2) { 1063 mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyTextSize); 1064 mPreviewText.setTypeface(Typeface.DEFAULT_BOLD); 1065 } else { 1066 mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSizeLarge); 1067 mPreviewText.setTypeface(mKeyTextStyle); 1068 } 1069 } 1070 mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 1071 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 1072 int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), key.width 1073 + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight()); 1074 final int popupHeight = mPreviewHeight; 1075 LayoutParams lp = mPreviewText.getLayoutParams(); 1076 if (lp != null) { 1077 lp.width = popupWidth; 1078 lp.height = popupHeight; 1079 } 1080 1081 int popupPreviewX = key.x - (popupWidth - key.width) / 2; 1082 int popupPreviewY = key.y - popupHeight + mPreviewOffset; 1083 1084 mHandler.cancelDismissPreview(); 1085 if (mOffsetInWindow == null) { 1086 mOffsetInWindow = new int[2]; 1087 getLocationInWindow(mOffsetInWindow); 1088 mOffsetInWindow[0] += mPopupPreviewOffsetX; // Offset may be zero 1089 mOffsetInWindow[1] += mPopupPreviewOffsetY; // Offset may be zero 1090 int[] windowLocation = new int[2]; 1091 getLocationOnScreen(windowLocation); 1092 mWindowY = windowLocation[1]; 1093 } 1094 // Set the preview background state 1095 mPreviewText.getBackground().setState( 1096 key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET); 1097 popupPreviewX += mOffsetInWindow[0]; 1098 popupPreviewY += mOffsetInWindow[1]; 1099 1100 // If the popup cannot be shown above the key, put it on the side 1101 if (popupPreviewY + mWindowY < 0) { 1102 // If the key you're pressing is on the left side of the keyboard, 1103 // show the popup on 1104 // the right, offset by enough to see at least one key to the 1105 // left/right. 1106 if (key.x + key.width <= getWidth() / 2) { 1107 popupPreviewX += (int) (key.width * 2.5); 1108 } else { 1109 popupPreviewX -= (int) (key.width * 2.5); 1110 } 1111 popupPreviewY += popupHeight; 1112 } 1113 1114 if (mPreviewPopup.isShowing()) { 1115 mPreviewPopup.update(popupPreviewX, popupPreviewY, popupWidth, popupHeight); 1116 } else { 1117 mPreviewPopup.setWidth(popupWidth); 1118 mPreviewPopup.setHeight(popupHeight); 1119 mPreviewPopup.showAtLocation(mMiniKeyboardParent, Gravity.NO_GRAVITY, 1120 popupPreviewX, popupPreviewY); 1121 } 1122 // Record popup preview position to display mini-keyboard later at the 1123 // same positon 1124 mPopupPreviewDisplayedY = popupPreviewY; 1125 mPreviewText.setVisibility(VISIBLE); 1126 } 1127 1128 /** 1129 * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient 1130 * because the keyboard renders the keys to an off-screen buffer and an invalidate() only draws 1131 * the cached buffer. 1132 * 1133 * @see #invalidateKey(Key) 1134 */ 1135 public void invalidateAllKeys() { 1136 mDirtyRect.union(0, 0, getWidth(), getHeight()); 1137 mDrawPending = true; 1138 invalidate(); 1139 } 1140 1141 /** 1142 * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only one 1143 * key is changing it's content. Any changes that affect the position or size of the key may not 1144 * be honored. 1145 * 1146 * @param key key in the attached {@link Keyboard}. 1147 * @see #invalidateAllKeys 1148 */ 1149 @Override 1150 public void invalidateKey(Key key) { 1151 if (key == null) 1152 return; 1153 mInvalidatedKey = key; 1154 // TODO we should clean up this and record key's region to use in 1155 // onBufferDraw. 1156 mDirtyRect.union(key.x + getPaddingLeft(), key.y + getPaddingTop(), 1157 key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop()); 1158 onBufferDraw(); 1159 invalidate(key.x + getPaddingLeft(), key.y + getPaddingTop(), 1160 key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop()); 1161 } 1162 1163 private boolean openPopupIfRequired(int keyIndex, PointerTracker tracker) { 1164 // Check if we have a popup layout specified first. 1165 if (mPopupLayout == 0) { 1166 return false; 1167 } 1168 1169 Key popupKey = tracker.getKey(keyIndex); 1170 if (popupKey == null) 1171 return false; 1172 boolean result = onLongPress(popupKey); 1173 if (result) { 1174 dismissKeyPreview(); 1175 mMiniKeyboardTrackerId = tracker.mPointerId; 1176 // Mark this tracker "already processed" and remove it from the 1177 // pointer queue 1178 tracker.setAlreadyProcessed(); 1179 mPointerQueue.remove(tracker); 1180 } 1181 return result; 1182 } 1183 1184 private View inflateMiniKeyboardContainer(Key popupKey) { 1185 int popupKeyboardId = popupKey.popupResId; 1186 LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( 1187 Context.LAYOUT_INFLATER_SERVICE); 1188 View container = inflater.inflate(mPopupLayout, null); 1189 if (container == null) 1190 throw new NullPointerException(); 1191 1192 LatinKeyboardBaseView miniKeyboard = 1193 (LatinKeyboardBaseView) container.findViewById(R.id.LatinKeyboardBaseView); 1194 miniKeyboard.setOnKeyboardActionListener(new OnKeyboardActionListener() { 1195 @Override 1196 public void onKey(int primaryCode, int[] keyCodes, int x, int y) { 1197 mKeyboardActionListener.onKey(primaryCode, keyCodes, x, y); 1198 dismissPopupKeyboard(); 1199 } 1200 1201 @Override 1202 public void onText(CharSequence text) { 1203 mKeyboardActionListener.onText(text); 1204 dismissPopupKeyboard(); 1205 } 1206 1207 @Override 1208 public void onCancel() { 1209 dismissPopupKeyboard(); 1210 } 1211 1212 @Override 1213 public boolean swipeLeft(int pointerCount) { 1214 return false; 1215 } 1216 1217 @Override 1218 public boolean swipeRight(int pointerCount) { 1219 return false; 1220 } 1221 1222 @Override 1223 public boolean swipeUp(int pointerCount) { 1224 return false; 1225 } 1226 1227 @Override 1228 public boolean swipeDown(int pointerCount) { 1229 return false; 1230 } 1231 1232 @Override 1233 public boolean singleTap(int pointerCount) { 1234 return false; 1235 } 1236 1237 @Override 1238 public boolean doubleTap(int pointerCount) { 1239 return false; 1240 } 1241 1242 @Override 1243 public boolean longPress(int pointerCount) { 1244 return false; 1245 } 1246 1247 @Override 1248 public void exploreKeyboardArea() { 1249 } 1250 1251 @Override 1252 public void enteredSegment(int segment) { 1253 } 1254 1255 @Override 1256 public void selectedSegment(int segment) { 1257 } 1258 1259 @Override 1260 public void leftKeyboardArea() { 1261 } 1262 1263 @Override 1264 public void enteredKeyboardArea() { 1265 } 1266 1267 @Override 1268 public void upOutsideKeyboardArea() { 1269 } 1270 1271 @Override 1272 public void onPress(Key key) { 1273 mKeyboardActionListener.onPress(key); 1274 } 1275 1276 @Override 1277 public void onLeaving(Key key) { 1278 mKeyboardActionListener.onLeaving(key); 1279 } 1280 1281 @Override 1282 public void onRelease(Key key) { 1283 mKeyboardActionListener.onRelease(key); 1284 } 1285 }); 1286 // Override default ProximityKeyDetector. 1287 miniKeyboard.mKeyDetector = new MiniKeyboardKeyDetector(mMiniKeyboardSlideAllowance); 1288 // Remove gesture detector on mini-keyboard 1289 miniKeyboard.mGestureDetector = null; 1290 1291 Keyboard keyboard; 1292 if (popupKey.popupCharacters != null) { 1293 keyboard = new Keyboard(getContext(), popupKeyboardId, popupKey.popupCharacters, 1294 -1, getPaddingLeft() + getPaddingRight()); 1295 } else { 1296 keyboard = new Keyboard(getContext(), popupKeyboardId); 1297 } 1298 miniKeyboard.setKeyboard(keyboard); 1299 miniKeyboard.setPopupParent(this); 1300 1301 container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), 1302 MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST)); 1303 1304 return container; 1305 } 1306 1307 private static boolean isOneRowKeys(List<Key> keys) { 1308 if (keys.size() == 0) 1309 return false; 1310 final int edgeFlags = keys.get(0).edgeFlags; 1311 // HACK: The first key of mini keyboard which was inflated from xml and 1312 // has multiple rows, 1313 // does not have both top and bo…
Large files files are truncated, but you can click here to view the full file