KeyboardView.java revision 27e48447a449d2eb534dfa2dc07060727e1a8fb0
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.android.inputmethod.keyboard; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.graphics.Bitmap; 22import android.graphics.Canvas; 23import android.graphics.Color; 24import android.graphics.Paint; 25import android.graphics.Paint.Align; 26import android.graphics.PorterDuff; 27import android.graphics.Rect; 28import android.graphics.Region; 29import android.graphics.Typeface; 30import android.graphics.drawable.Drawable; 31import android.os.Message; 32import android.util.AttributeSet; 33import android.util.SparseArray; 34import android.util.TypedValue; 35import android.view.LayoutInflater; 36import android.view.View; 37import android.view.ViewGroup; 38import android.widget.TextView; 39 40import com.android.inputmethod.keyboard.internal.PreviewPlacerView; 41import com.android.inputmethod.latin.Constants; 42import com.android.inputmethod.latin.LatinImeLogger; 43import com.android.inputmethod.latin.R; 44import com.android.inputmethod.latin.StaticInnerHandlerWrapper; 45import com.android.inputmethod.latin.StringUtils; 46import com.android.inputmethod.latin.define.ProductionFlag; 47import com.android.inputmethod.research.ResearchLogger; 48 49import java.util.HashSet; 50 51/** 52 * A view that renders a virtual {@link Keyboard}. 53 * 54 * @attr ref R.styleable#KeyboardView_backgroundDimAlpha 55 * @attr ref R.styleable#KeyboardView_keyBackground 56 * @attr ref R.styleable#KeyboardView_keyLetterRatio 57 * @attr ref R.styleable#KeyboardView_keyLargeLetterRatio 58 * @attr ref R.styleable#KeyboardView_keyLabelRatio 59 * @attr ref R.styleable#KeyboardView_keyHintLetterRatio 60 * @attr ref R.styleable#KeyboardView_keyShiftedLetterHintRatio 61 * @attr ref R.styleable#KeyboardView_keyHintLabelRatio 62 * @attr ref R.styleable#KeyboardView_keyLabelHorizontalPadding 63 * @attr ref R.styleable#KeyboardView_keyHintLetterPadding 64 * @attr ref R.styleable#KeyboardView_keyPopupHintLetterPadding 65 * @attr ref R.styleable#KeyboardView_keyShiftedLetterHintPadding 66 * @attr ref R.styleable#KeyboardView_keyTextStyle 67 * @attr ref R.styleable#KeyboardView_keyPreviewLayout 68 * @attr ref R.styleable#KeyboardView_keyPreviewTextRatio 69 * @attr ref R.styleable#KeyboardView_keyPreviewOffset 70 * @attr ref R.styleable#KeyboardView_keyPreviewHeight 71 * @attr ref R.styleable#KeyboardView_keyTextColor 72 * @attr ref R.styleable#KeyboardView_keyTextColorDisabled 73 * @attr ref R.styleable#KeyboardView_keyHintLetterColor 74 * @attr ref R.styleable#KeyboardView_keyHintLabelColor 75 * @attr ref R.styleable#KeyboardView_keyShiftedLetterHintInactivatedColor 76 * @attr ref R.styleable#KeyboardView_keyShiftedLetterHintActivatedColor 77 * @attr ref R.styleable#KeyboardView_shadowColor 78 * @attr ref R.styleable#KeyboardView_shadowRadius 79 */ 80public class KeyboardView extends View implements PointerTracker.DrawingProxy { 81 // Miscellaneous constants 82 private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable }; 83 84 // XML attributes 85 protected final float mVerticalCorrection; 86 protected final int mMoreKeysLayout; 87 private final int mBackgroundDimAlpha; 88 89 // HORIZONTAL ELLIPSIS "...", character for popup hint. 90 private static final String POPUP_HINT_CHAR = "\u2026"; 91 92 // Margin between the label and the icon on a key that has both of them. 93 // Specified by the fraction of the key width. 94 // TODO: Use resource parameter for this value. 95 private static final float LABEL_ICON_MARGIN = 0.05f; 96 97 // The maximum key label width in the proportion to the key width. 98 private static final float MAX_LABEL_RATIO = 0.90f; 99 100 // Main keyboard 101 private Keyboard mKeyboard; 102 protected final KeyDrawParams mKeyDrawParams; 103 104 // Key preview 105 private final int mKeyPreviewLayoutId; 106 protected final KeyPreviewDrawParams mKeyPreviewDrawParams; 107 private boolean mShowKeyPreviewPopup = true; 108 private int mDelayAfterPreview; 109 private final PreviewPlacerView mPreviewPlacerView; 110 111 /** True if {@link KeyboardView} should handle gesture events. */ 112 protected boolean mShouldHandleGesture; 113 114 // Drawing 115 /** True if the entire keyboard needs to be dimmed. */ 116 private boolean mNeedsToDimEntireKeyboard; 117 /** Whether the keyboard bitmap buffer needs to be redrawn before it's blitted. **/ 118 private boolean mBufferNeedsUpdate; 119 /** True if all keys should be drawn */ 120 private boolean mInvalidateAllKeys; 121 /** The keys that should be drawn */ 122 private final HashSet<Key> mInvalidatedKeys = new HashSet<Key>(); 123 /** The working rectangle variable */ 124 private final Rect mWorkingRect = new Rect(); 125 /** The keyboard bitmap buffer for faster updates */ 126 /** The clip region to draw keys */ 127 private final Region mClipRegion = new Region(); 128 private Bitmap mOffscreenBuffer; 129 /** The canvas for the above mutable keyboard bitmap */ 130 private Canvas mOffscreenCanvas; 131 private final Paint mPaint = new Paint(); 132 private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics(); 133 // This sparse array caches key label text height in pixel indexed by key label text size. 134 private static final SparseArray<Float> sTextHeightCache = new SparseArray<Float>(); 135 // This sparse array caches key label text width in pixel indexed by key label text size. 136 private static final SparseArray<Float> sTextWidthCache = new SparseArray<Float>(); 137 private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' }; 138 private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' }; 139 140 private final DrawingHandler mDrawingHandler = new DrawingHandler(this); 141 142 public static class DrawingHandler extends StaticInnerHandlerWrapper<KeyboardView> { 143 private static final int MSG_DISMISS_KEY_PREVIEW = 0; 144 145 public DrawingHandler(KeyboardView outerInstance) { 146 super(outerInstance); 147 } 148 149 @Override 150 public void handleMessage(Message msg) { 151 final KeyboardView keyboardView = getOuterInstance(); 152 if (keyboardView == null) return; 153 final PointerTracker tracker = (PointerTracker) msg.obj; 154 switch (msg.what) { 155 case MSG_DISMISS_KEY_PREVIEW: 156 tracker.getKeyPreviewText().setVisibility(View.INVISIBLE); 157 break; 158 } 159 } 160 161 public void dismissKeyPreview(long delay, PointerTracker tracker) { 162 sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, tracker), delay); 163 } 164 165 public void cancelDismissKeyPreview(PointerTracker tracker) { 166 removeMessages(MSG_DISMISS_KEY_PREVIEW, tracker); 167 } 168 169 public void cancelAllDismissKeyPreviews() { 170 removeMessages(MSG_DISMISS_KEY_PREVIEW); 171 } 172 173 public void cancelAllMessages() { 174 cancelAllDismissKeyPreviews(); 175 } 176 } 177 178 protected static class KeyDrawParams { 179 // XML attributes 180 public final int mKeyTextColor; 181 public final int mKeyTextInactivatedColor; 182 public final Typeface mKeyTextStyle; 183 public final float mKeyLabelHorizontalPadding; 184 public final float mKeyHintLetterPadding; 185 public final float mKeyPopupHintLetterPadding; 186 public final float mKeyShiftedLetterHintPadding; 187 public final int mShadowColor; 188 public final float mShadowRadius; 189 public final Drawable mKeyBackground; 190 public final int mKeyHintLetterColor; 191 public final int mKeyHintLabelColor; 192 public final int mKeyShiftedLetterHintInactivatedColor; 193 public final int mKeyShiftedLetterHintActivatedColor; 194 195 /* package */ final float mKeyLetterRatio; 196 private final float mKeyLargeLetterRatio; 197 private final float mKeyLabelRatio; 198 private final float mKeyLargeLabelRatio; 199 private final float mKeyHintLetterRatio; 200 private final float mKeyShiftedLetterHintRatio; 201 private final float mKeyHintLabelRatio; 202 private static final float UNDEFINED_RATIO = -1.0f; 203 204 public final Rect mPadding = new Rect(); 205 public int mKeyLetterSize; 206 public int mKeyLargeLetterSize; 207 public int mKeyLabelSize; 208 public int mKeyLargeLabelSize; 209 public int mKeyHintLetterSize; 210 public int mKeyShiftedLetterHintSize; 211 public int mKeyHintLabelSize; 212 public int mAnimAlpha; 213 214 public KeyDrawParams(TypedArray a) { 215 mKeyBackground = a.getDrawable(R.styleable.KeyboardView_keyBackground); 216 if (a.hasValue(R.styleable.KeyboardView_keyLetterSize)) { 217 mKeyLetterRatio = UNDEFINED_RATIO; 218 mKeyLetterSize = a.getDimensionPixelSize(R.styleable.KeyboardView_keyLetterSize, 0); 219 } else { 220 mKeyLetterRatio = getRatio(a, R.styleable.KeyboardView_keyLetterRatio); 221 } 222 if (a.hasValue(R.styleable.KeyboardView_keyLabelSize)) { 223 mKeyLabelRatio = UNDEFINED_RATIO; 224 mKeyLabelSize = a.getDimensionPixelSize(R.styleable.KeyboardView_keyLabelSize, 0); 225 } else { 226 mKeyLabelRatio = getRatio(a, R.styleable.KeyboardView_keyLabelRatio); 227 } 228 mKeyLargeLabelRatio = getRatio(a, R.styleable.KeyboardView_keyLargeLabelRatio); 229 mKeyLargeLetterRatio = getRatio(a, R.styleable.KeyboardView_keyLargeLetterRatio); 230 mKeyHintLetterRatio = getRatio(a, R.styleable.KeyboardView_keyHintLetterRatio); 231 mKeyShiftedLetterHintRatio = getRatio(a, 232 R.styleable.KeyboardView_keyShiftedLetterHintRatio); 233 mKeyHintLabelRatio = getRatio(a, R.styleable.KeyboardView_keyHintLabelRatio); 234 mKeyLabelHorizontalPadding = a.getDimension( 235 R.styleable.KeyboardView_keyLabelHorizontalPadding, 0); 236 mKeyHintLetterPadding = a.getDimension( 237 R.styleable.KeyboardView_keyHintLetterPadding, 0); 238 mKeyPopupHintLetterPadding = a.getDimension( 239 R.styleable.KeyboardView_keyPopupHintLetterPadding, 0); 240 mKeyShiftedLetterHintPadding = a.getDimension( 241 R.styleable.KeyboardView_keyShiftedLetterHintPadding, 0); 242 mKeyTextColor = a.getColor(R.styleable.KeyboardView_keyTextColor, 0xFF000000); 243 mKeyTextInactivatedColor = a.getColor( 244 R.styleable.KeyboardView_keyTextInactivatedColor, 0xFF000000); 245 mKeyHintLetterColor = a.getColor(R.styleable.KeyboardView_keyHintLetterColor, 0); 246 mKeyHintLabelColor = a.getColor(R.styleable.KeyboardView_keyHintLabelColor, 0); 247 mKeyShiftedLetterHintInactivatedColor = a.getColor( 248 R.styleable.KeyboardView_keyShiftedLetterHintInactivatedColor, 0); 249 mKeyShiftedLetterHintActivatedColor = a.getColor( 250 R.styleable.KeyboardView_keyShiftedLetterHintActivatedColor, 0); 251 mKeyTextStyle = Typeface.defaultFromStyle( 252 a.getInt(R.styleable.KeyboardView_keyTextStyle, Typeface.NORMAL)); 253 mShadowColor = a.getColor(R.styleable.KeyboardView_shadowColor, 0); 254 mShadowRadius = a.getFloat(R.styleable.KeyboardView_shadowRadius, 0f); 255 256 mKeyBackground.getPadding(mPadding); 257 } 258 259 public void updateKeyHeight(int keyHeight) { 260 if (mKeyLetterRatio >= 0.0f) { 261 mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio); 262 } 263 if (mKeyLabelRatio >= 0.0f) { 264 mKeyLabelSize = (int)(keyHeight * mKeyLabelRatio); 265 } 266 mKeyLargeLabelSize = (int)(keyHeight * mKeyLargeLabelRatio); 267 mKeyLargeLetterSize = (int)(keyHeight * mKeyLargeLetterRatio); 268 mKeyHintLetterSize = (int)(keyHeight * mKeyHintLetterRatio); 269 mKeyShiftedLetterHintSize = (int)(keyHeight * mKeyShiftedLetterHintRatio); 270 mKeyHintLabelSize = (int)(keyHeight * mKeyHintLabelRatio); 271 } 272 273 public void blendAlpha(Paint paint) { 274 final int color = paint.getColor(); 275 paint.setARGB((paint.getAlpha() * mAnimAlpha) / Constants.Color.ALPHA_OPAQUE, 276 Color.red(color), Color.green(color), Color.blue(color)); 277 } 278 } 279 280 /* package */ static class KeyPreviewDrawParams { 281 // XML attributes. 282 public final Drawable mPreviewBackground; 283 public final Drawable mPreviewLeftBackground; 284 public final Drawable mPreviewRightBackground; 285 public final int mPreviewTextColor; 286 public final int mPreviewOffset; 287 public final int mPreviewHeight; 288 public final Typeface mKeyTextStyle; 289 public final int mLingerTimeout; 290 291 private final float mPreviewTextRatio; 292 private final float mKeyLetterRatio; 293 294 // The graphical geometry of the key preview. 295 // <-width-> 296 // +-------+ ^ 297 // | | | 298 // |preview| height (visible) 299 // | | | 300 // + + ^ v 301 // \ / |offset 302 // +-\ /-+ v 303 // | +-+ | 304 // |parent | 305 // | key| 306 // +-------+ 307 // The background of a {@link TextView} being used for a key preview may have invisible 308 // paddings. To align the more keys keyboard panel's visible part with the visible part of 309 // the background, we need to record the width and height of key preview that don't include 310 // invisible paddings. 311 public int mPreviewVisibleWidth; 312 public int mPreviewVisibleHeight; 313 // The key preview may have an arbitrary offset and its background that may have a bottom 314 // padding. To align the more keys keyboard and the key preview we also need to record the 315 // offset between the top edge of parent key and the bottom of the visible part of key 316 // preview background. 317 public int mPreviewVisibleOffset; 318 319 public int mPreviewTextSize; 320 public int mKeyLetterSize; 321 public final int[] mCoordinates = new int[2]; 322 323 private static final int PREVIEW_ALPHA = 240; 324 325 public KeyPreviewDrawParams(TypedArray a, KeyDrawParams keyDrawParams) { 326 mPreviewBackground = a.getDrawable(R.styleable.KeyboardView_keyPreviewBackground); 327 mPreviewLeftBackground = a.getDrawable( 328 R.styleable.KeyboardView_keyPreviewLeftBackground); 329 mPreviewRightBackground = a.getDrawable( 330 R.styleable.KeyboardView_keyPreviewRightBackground); 331 setAlpha(mPreviewBackground, PREVIEW_ALPHA); 332 setAlpha(mPreviewLeftBackground, PREVIEW_ALPHA); 333 setAlpha(mPreviewRightBackground, PREVIEW_ALPHA); 334 mPreviewOffset = a.getDimensionPixelOffset( 335 R.styleable.KeyboardView_keyPreviewOffset, 0); 336 mPreviewHeight = a.getDimensionPixelSize( 337 R.styleable.KeyboardView_keyPreviewHeight, 80); 338 mPreviewTextRatio = getRatio(a, R.styleable.KeyboardView_keyPreviewTextRatio); 339 mPreviewTextColor = a.getColor(R.styleable.KeyboardView_keyPreviewTextColor, 0); 340 mLingerTimeout = a.getInt(R.styleable.KeyboardView_keyPreviewLingerTimeout, 0); 341 342 mKeyLetterRatio = keyDrawParams.mKeyLetterRatio; 343 mKeyTextStyle = keyDrawParams.mKeyTextStyle; 344 } 345 346 public void updateKeyHeight(int keyHeight) { 347 if (mPreviewTextRatio >= 0.0f) { 348 mPreviewTextSize = (int)(keyHeight * mPreviewTextRatio); 349 } 350 if (mKeyLetterRatio >= 0.0f) { 351 mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio); 352 } 353 } 354 355 private static void setAlpha(Drawable drawable, int alpha) { 356 if (drawable == null) return; 357 drawable.setAlpha(alpha); 358 } 359 } 360 361 public KeyboardView(Context context, AttributeSet attrs) { 362 this(context, attrs, R.attr.keyboardViewStyle); 363 } 364 365 public KeyboardView(Context context, AttributeSet attrs, int defStyle) { 366 super(context, attrs, defStyle); 367 368 final TypedArray a = context.obtainStyledAttributes( 369 attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView); 370 371 mKeyDrawParams = new KeyDrawParams(a); 372 mKeyPreviewDrawParams = new KeyPreviewDrawParams(a, mKeyDrawParams); 373 mKeyPreviewLayoutId = a.getResourceId(R.styleable.KeyboardView_keyPreviewLayout, 0); 374 if (mKeyPreviewLayoutId == 0) { 375 mShowKeyPreviewPopup = false; 376 } 377 mVerticalCorrection = a.getDimensionPixelOffset( 378 R.styleable.KeyboardView_verticalCorrection, 0); 379 mMoreKeysLayout = a.getResourceId(R.styleable.KeyboardView_moreKeysLayout, 0); 380 mBackgroundDimAlpha = a.getInt(R.styleable.KeyboardView_backgroundDimAlpha, 0); 381 mPreviewPlacerView = new PreviewPlacerView(context, a); 382 a.recycle(); 383 384 mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout; 385 386 mPaint.setAntiAlias(true); 387 } 388 389 // Read fraction value in TypedArray as float. 390 /* package */ static float getRatio(TypedArray a, int index) { 391 return a.getFraction(index, 1000, 1000, 1) / 1000.0f; 392 } 393 394 /** 395 * Attaches a keyboard to this view. The keyboard can be switched at any time and the 396 * view will re-layout itself to accommodate the keyboard. 397 * @see Keyboard 398 * @see #getKeyboard() 399 * @param keyboard the keyboard to display in this view 400 */ 401 public void setKeyboard(Keyboard keyboard) { 402 mKeyboard = keyboard; 403 LatinImeLogger.onSetKeyboard(keyboard); 404 requestLayout(); 405 invalidateAllKeys(); 406 final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; 407 mKeyDrawParams.updateKeyHeight(keyHeight); 408 mKeyPreviewDrawParams.updateKeyHeight(keyHeight); 409 } 410 411 /** 412 * Returns the current keyboard being displayed by this view. 413 * @return the currently attached keyboard 414 * @see #setKeyboard(Keyboard) 415 */ 416 public Keyboard getKeyboard() { 417 return mKeyboard; 418 } 419 420 /** 421 * Enables or disables the key feedback popup. This is a popup that shows a magnified 422 * version of the depressed key. By default the preview is enabled. 423 * @param previewEnabled whether or not to enable the key feedback preview 424 * @param delay the delay after which the preview is dismissed 425 * @see #isKeyPreviewPopupEnabled() 426 */ 427 public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) { 428 mShowKeyPreviewPopup = previewEnabled; 429 mDelayAfterPreview = delay; 430 } 431 432 /** 433 * Returns the enabled state of the key feedback preview 434 * @return whether or not the key feedback preview is enabled 435 * @see #setKeyPreviewPopupEnabled(boolean, int) 436 */ 437 public boolean isKeyPreviewPopupEnabled() { 438 return mShowKeyPreviewPopup; 439 } 440 441 public void setGestureHandlingMode(boolean shouldHandleGesture, 442 boolean drawsGesturePreviewTrail, boolean drawsGestureFloatingPreviewText) { 443 mShouldHandleGesture = shouldHandleGesture; 444 mPreviewPlacerView.setGesturePreviewMode( 445 drawsGesturePreviewTrail, drawsGestureFloatingPreviewText); 446 } 447 448 @Override 449 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 450 if (mKeyboard != null) { 451 // The main keyboard expands to the display width. 452 final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom(); 453 setMeasuredDimension(widthMeasureSpec, height); 454 } else { 455 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 456 } 457 } 458 459 @Override 460 public void onDraw(Canvas canvas) { 461 super.onDraw(canvas); 462 if (canvas.isHardwareAccelerated()) { 463 onDrawKeyboard(canvas); 464 return; 465 } 466 if (mBufferNeedsUpdate || mOffscreenBuffer == null) { 467 mBufferNeedsUpdate = false; 468 if (maybeAllocateOffscreenBuffer()) { 469 mInvalidateAllKeys = true; 470 // TODO: Stop using the offscreen canvas even when in software rendering 471 if (mOffscreenCanvas != null) { 472 mOffscreenCanvas.setBitmap(mOffscreenBuffer); 473 } else { 474 mOffscreenCanvas = new Canvas(mOffscreenBuffer); 475 } 476 } 477 onDrawKeyboard(mOffscreenCanvas); 478 } 479 canvas.drawBitmap(mOffscreenBuffer, 0, 0, null); 480 } 481 482 private boolean maybeAllocateOffscreenBuffer() { 483 final int width = getWidth(); 484 final int height = getHeight(); 485 if (width == 0 || height == 0) { 486 return false; 487 } 488 if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == width 489 && mOffscreenBuffer.getHeight() == height) { 490 return false; 491 } 492 freeOffscreenBuffer(); 493 mOffscreenBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 494 return true; 495 } 496 497 private void freeOffscreenBuffer() { 498 if (mOffscreenBuffer != null) { 499 mOffscreenBuffer.recycle(); 500 mOffscreenBuffer = null; 501 } 502 } 503 504 private void onDrawKeyboard(final Canvas canvas) { 505 if (mKeyboard == null) return; 506 507 final int width = getWidth(); 508 final int height = getHeight(); 509 final Paint paint = mPaint; 510 final KeyDrawParams params = mKeyDrawParams; 511 512 // Calculate clip region and set. 513 final boolean drawAllKeys = mInvalidateAllKeys || mInvalidatedKeys.isEmpty(); 514 final boolean isHardwareAccelerated = canvas.isHardwareAccelerated(); 515 // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on. 516 if (drawAllKeys || isHardwareAccelerated) { 517 mClipRegion.set(0, 0, width, height); 518 } else { 519 mClipRegion.setEmpty(); 520 for (final Key key : mInvalidatedKeys) { 521 if (mKeyboard.hasKey(key)) { 522 final int x = key.mX + getPaddingLeft(); 523 final int y = key.mY + getPaddingTop(); 524 mWorkingRect.set(x, y, x + key.mWidth, y + key.mHeight); 525 mClipRegion.union(mWorkingRect); 526 } 527 } 528 } 529 if (!isHardwareAccelerated) { 530 canvas.clipRegion(mClipRegion, Region.Op.REPLACE); 531 } 532 533 // Draw keyboard background. 534 canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); 535 final Drawable background = getBackground(); 536 if (background != null) { 537 background.draw(canvas); 538 } 539 540 // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on. 541 if (drawAllKeys || isHardwareAccelerated) { 542 // Draw all keys. 543 for (final Key key : mKeyboard.mKeys) { 544 onDrawKey(key, canvas, paint, params); 545 } 546 } else { 547 // Draw invalidated keys. 548 for (final Key key : mInvalidatedKeys) { 549 if (mKeyboard.hasKey(key)) { 550 onDrawKey(key, canvas, paint, params); 551 } 552 } 553 } 554 555 // Overlay a dark rectangle to dim. 556 if (mNeedsToDimEntireKeyboard) { 557 paint.setColor(Color.BLACK); 558 paint.setAlpha(mBackgroundDimAlpha); 559 // Note: clipRegion() above is in effect if it was called. 560 canvas.drawRect(0, 0, width, height, paint); 561 } 562 563 // ResearchLogging indicator. 564 // TODO: Reimplement using a keyboard background image specific to the ResearchLogger, 565 // and remove this call. 566 if (ProductionFlag.IS_EXPERIMENTAL) { 567 ResearchLogger.getInstance().paintIndicator(this, paint, canvas, width, height); 568 } 569 570 mInvalidatedKeys.clear(); 571 mInvalidateAllKeys = false; 572 } 573 574 public void dimEntireKeyboard(boolean dimmed) { 575 final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed; 576 mNeedsToDimEntireKeyboard = dimmed; 577 if (needsRedrawing) { 578 invalidateAllKeys(); 579 } 580 } 581 582 private void onDrawKey(Key key, Canvas canvas, Paint paint, KeyDrawParams params) { 583 final int keyDrawX = key.mX + key.mVisualInsetsLeft + getPaddingLeft(); 584 final int keyDrawY = key.mY + getPaddingTop(); 585 canvas.translate(keyDrawX, keyDrawY); 586 587 params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE; 588 if (!key.isSpacer()) { 589 onDrawKeyBackground(key, canvas, params); 590 } 591 onDrawKeyTopVisuals(key, canvas, paint, params); 592 593 canvas.translate(-keyDrawX, -keyDrawY); 594 } 595 596 // Draw key background. 597 protected void onDrawKeyBackground(Key key, Canvas canvas, KeyDrawParams params) { 598 final int bgWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight 599 + params.mPadding.left + params.mPadding.right; 600 final int bgHeight = key.mHeight + params.mPadding.top + params.mPadding.bottom; 601 final int bgX = -params.mPadding.left; 602 final int bgY = -params.mPadding.top; 603 final int[] drawableState = key.getCurrentDrawableState(); 604 final Drawable background = params.mKeyBackground; 605 background.setState(drawableState); 606 final Rect bounds = background.getBounds(); 607 if (bgWidth != bounds.right || bgHeight != bounds.bottom) { 608 background.setBounds(0, 0, bgWidth, bgHeight); 609 } 610 canvas.translate(bgX, bgY); 611 background.draw(canvas); 612 if (LatinImeLogger.sVISUALDEBUG) { 613 drawRectangle(canvas, 0, 0, bgWidth, bgHeight, 0x80c00000, new Paint()); 614 } 615 canvas.translate(-bgX, -bgY); 616 } 617 618 // Draw key top visuals. 619 protected void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint, KeyDrawParams params) { 620 final int keyWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight; 621 final int keyHeight = key.mHeight; 622 final float centerX = keyWidth * 0.5f; 623 final float centerY = keyHeight * 0.5f; 624 625 if (LatinImeLogger.sVISUALDEBUG) { 626 drawRectangle(canvas, 0, 0, keyWidth, keyHeight, 0x800000c0, new Paint()); 627 } 628 629 // Draw key label. 630 final Drawable icon = key.getIcon(mKeyboard.mIconsSet, params.mAnimAlpha); 631 float positionX = centerX; 632 if (key.mLabel != null) { 633 final String label = key.mLabel; 634 // For characters, use large font. For labels like "Done", use smaller font. 635 paint.setTypeface(key.selectTypeface(params.mKeyTextStyle)); 636 final int labelSize = key.selectTextSize(params.mKeyLetterSize, 637 params.mKeyLargeLetterSize, params.mKeyLabelSize, params.mKeyLargeLabelSize, 638 params.mKeyHintLabelSize); 639 paint.setTextSize(labelSize); 640 final float labelCharHeight = getCharHeight(KEY_LABEL_REFERENCE_CHAR, paint); 641 final float labelCharWidth = getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint); 642 643 // Vertical label text alignment. 644 final float baseline = centerY + labelCharHeight / 2; 645 646 // Horizontal label text alignment 647 float labelWidth = 0; 648 if (key.isAlignLeft()) { 649 positionX = (int)params.mKeyLabelHorizontalPadding; 650 paint.setTextAlign(Align.LEFT); 651 } else if (key.isAlignRight()) { 652 positionX = keyWidth - (int)params.mKeyLabelHorizontalPadding; 653 paint.setTextAlign(Align.RIGHT); 654 } else if (key.isAlignLeftOfCenter()) { 655 // TODO: Parameterise this? 656 positionX = centerX - labelCharWidth * 7 / 4; 657 paint.setTextAlign(Align.LEFT); 658 } else if (key.hasLabelWithIconLeft() && icon != null) { 659 labelWidth = getLabelWidth(label, paint) + icon.getIntrinsicWidth() 660 + LABEL_ICON_MARGIN * keyWidth; 661 positionX = centerX + labelWidth / 2; 662 paint.setTextAlign(Align.RIGHT); 663 } else if (key.hasLabelWithIconRight() && icon != null) { 664 labelWidth = getLabelWidth(label, paint) + icon.getIntrinsicWidth() 665 + LABEL_ICON_MARGIN * keyWidth; 666 positionX = centerX - labelWidth / 2; 667 paint.setTextAlign(Align.LEFT); 668 } else { 669 positionX = centerX; 670 paint.setTextAlign(Align.CENTER); 671 } 672 if (key.needsXScale()) { 673 paint.setTextScaleX( 674 Math.min(1.0f, (keyWidth * MAX_LABEL_RATIO) / getLabelWidth(label, paint))); 675 } 676 677 paint.setColor(key.isShiftedLetterActivated() 678 ? params.mKeyTextInactivatedColor : params.mKeyTextColor); 679 if (key.isEnabled()) { 680 // Set a drop shadow for the text 681 paint.setShadowLayer(params.mShadowRadius, 0, 0, params.mShadowColor); 682 } else { 683 // Make label invisible 684 paint.setColor(Color.TRANSPARENT); 685 } 686 params.blendAlpha(paint); 687 canvas.drawText(label, 0, label.length(), positionX, baseline, paint); 688 // Turn off drop shadow and reset x-scale. 689 paint.setShadowLayer(0, 0, 0, 0); 690 paint.setTextScaleX(1.0f); 691 692 if (icon != null) { 693 final int iconWidth = icon.getIntrinsicWidth(); 694 final int iconHeight = icon.getIntrinsicHeight(); 695 final int iconY = (keyHeight - iconHeight) / 2; 696 if (key.hasLabelWithIconLeft()) { 697 final int iconX = (int)(centerX - labelWidth / 2); 698 drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); 699 } else if (key.hasLabelWithIconRight()) { 700 final int iconX = (int)(centerX + labelWidth / 2 - iconWidth); 701 drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); 702 } 703 } 704 705 if (LatinImeLogger.sVISUALDEBUG) { 706 final Paint line = new Paint(); 707 drawHorizontalLine(canvas, baseline, keyWidth, 0xc0008000, line); 708 drawVerticalLine(canvas, positionX, keyHeight, 0xc0800080, line); 709 } 710 } 711 712 // Draw hint label. 713 if (key.mHintLabel != null) { 714 final String hint = key.mHintLabel; 715 final int hintColor; 716 final int hintSize; 717 if (key.hasHintLabel()) { 718 hintColor = params.mKeyHintLabelColor; 719 hintSize = params.mKeyHintLabelSize; 720 paint.setTypeface(Typeface.DEFAULT); 721 } else if (key.hasShiftedLetterHint()) { 722 hintColor = key.isShiftedLetterActivated() 723 ? params.mKeyShiftedLetterHintActivatedColor 724 : params.mKeyShiftedLetterHintInactivatedColor; 725 hintSize = params.mKeyShiftedLetterHintSize; 726 } else { // key.hasHintLetter() 727 hintColor = params.mKeyHintLetterColor; 728 hintSize = params.mKeyHintLetterSize; 729 } 730 paint.setColor(hintColor); 731 params.blendAlpha(paint); 732 paint.setTextSize(hintSize); 733 final float hintX, hintY; 734 if (key.hasHintLabel()) { 735 // The hint label is placed just right of the key label. Used mainly on 736 // "phone number" layout. 737 // TODO: Generalize the following calculations. 738 hintX = positionX + getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) * 2; 739 hintY = centerY + getCharHeight(KEY_LABEL_REFERENCE_CHAR, paint) / 2; 740 paint.setTextAlign(Align.LEFT); 741 } else if (key.hasShiftedLetterHint()) { 742 // The hint label is placed at top-right corner of the key. Used mainly on tablet. 743 hintX = keyWidth - params.mKeyShiftedLetterHintPadding 744 - getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2; 745 paint.getFontMetrics(mFontMetrics); 746 hintY = -mFontMetrics.top; 747 paint.setTextAlign(Align.CENTER); 748 } else { // key.hasHintLetter() 749 // The hint letter is placed at top-right corner of the key. Used mainly on phone. 750 hintX = keyWidth - params.mKeyHintLetterPadding 751 - getCharWidth(KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR, paint) / 2; 752 hintY = -paint.ascent(); 753 paint.setTextAlign(Align.CENTER); 754 } 755 canvas.drawText(hint, 0, hint.length(), hintX, hintY, paint); 756 757 if (LatinImeLogger.sVISUALDEBUG) { 758 final Paint line = new Paint(); 759 drawHorizontalLine(canvas, (int)hintY, keyWidth, 0xc0808000, line); 760 drawVerticalLine(canvas, (int)hintX, keyHeight, 0xc0808000, line); 761 } 762 } 763 764 // Draw key icon. 765 if (key.mLabel == null && icon != null) { 766 final int iconWidth = icon.getIntrinsicWidth(); 767 final int iconHeight = icon.getIntrinsicHeight(); 768 final int iconX, alignX; 769 final int iconY = (keyHeight - iconHeight) / 2; 770 if (key.isAlignLeft()) { 771 iconX = (int)params.mKeyLabelHorizontalPadding; 772 alignX = iconX; 773 } else if (key.isAlignRight()) { 774 iconX = keyWidth - (int)params.mKeyLabelHorizontalPadding - iconWidth; 775 alignX = iconX + iconWidth; 776 } else { // Align center 777 iconX = (keyWidth - iconWidth) / 2; 778 alignX = iconX + iconWidth / 2; 779 } 780 drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); 781 782 if (LatinImeLogger.sVISUALDEBUG) { 783 final Paint line = new Paint(); 784 drawVerticalLine(canvas, alignX, keyHeight, 0xc0800080, line); 785 drawRectangle(canvas, iconX, iconY, iconWidth, iconHeight, 0x80c00000, line); 786 } 787 } 788 789 if (key.hasPopupHint() && key.mMoreKeys != null && key.mMoreKeys.length > 0) { 790 drawKeyPopupHint(key, canvas, paint, params); 791 } 792 } 793 794 // Draw popup hint "..." at the bottom right corner of the key. 795 protected void drawKeyPopupHint(Key key, Canvas canvas, Paint paint, KeyDrawParams params) { 796 final int keyWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight; 797 final int keyHeight = key.mHeight; 798 799 paint.setTypeface(params.mKeyTextStyle); 800 paint.setTextSize(params.mKeyHintLetterSize); 801 paint.setColor(params.mKeyHintLabelColor); 802 paint.setTextAlign(Align.CENTER); 803 final float hintX = keyWidth - params.mKeyHintLetterPadding 804 - getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2; 805 final float hintY = keyHeight - params.mKeyPopupHintLetterPadding; 806 canvas.drawText(POPUP_HINT_CHAR, hintX, hintY, paint); 807 808 if (LatinImeLogger.sVISUALDEBUG) { 809 final Paint line = new Paint(); 810 drawHorizontalLine(canvas, (int)hintY, keyWidth, 0xc0808000, line); 811 drawVerticalLine(canvas, (int)hintX, keyHeight, 0xc0808000, line); 812 } 813 } 814 815 private static int getCharGeometryCacheKey(char referenceChar, Paint paint) { 816 final int labelSize = (int)paint.getTextSize(); 817 final Typeface face = paint.getTypeface(); 818 final int codePointOffset = referenceChar << 15; 819 if (face == Typeface.DEFAULT) { 820 return codePointOffset + labelSize; 821 } else if (face == Typeface.DEFAULT_BOLD) { 822 return codePointOffset + labelSize + 0x1000; 823 } else if (face == Typeface.MONOSPACE) { 824 return codePointOffset + labelSize + 0x2000; 825 } else { 826 return codePointOffset + labelSize; 827 } 828 } 829 830 // Working variable for the following methods. 831 private final Rect mTextBounds = new Rect(); 832 833 private float getCharHeight(char[] referenceChar, Paint paint) { 834 final int key = getCharGeometryCacheKey(referenceChar[0], paint); 835 final Float cachedValue = sTextHeightCache.get(key); 836 if (cachedValue != null) 837 return cachedValue; 838 839 paint.getTextBounds(referenceChar, 0, 1, mTextBounds); 840 final float height = mTextBounds.height(); 841 sTextHeightCache.put(key, height); 842 return height; 843 } 844 845 private float getCharWidth(char[] referenceChar, Paint paint) { 846 final int key = getCharGeometryCacheKey(referenceChar[0], paint); 847 final Float cachedValue = sTextWidthCache.get(key); 848 if (cachedValue != null) 849 return cachedValue; 850 851 paint.getTextBounds(referenceChar, 0, 1, mTextBounds); 852 final float width = mTextBounds.width(); 853 sTextWidthCache.put(key, width); 854 return width; 855 } 856 857 public float getLabelWidth(String label, Paint paint) { 858 paint.getTextBounds(label.toString(), 0, label.length(), mTextBounds); 859 return mTextBounds.width(); 860 } 861 862 protected static void drawIcon(Canvas canvas, Drawable icon, int x, int y, int width, 863 int height) { 864 canvas.translate(x, y); 865 icon.setBounds(0, 0, width, height); 866 icon.draw(canvas); 867 canvas.translate(-x, -y); 868 } 869 870 private static void drawHorizontalLine(Canvas canvas, float y, float w, int color, 871 Paint paint) { 872 paint.setStyle(Paint.Style.STROKE); 873 paint.setStrokeWidth(1.0f); 874 paint.setColor(color); 875 canvas.drawLine(0, y, w, y, paint); 876 } 877 878 private static void drawVerticalLine(Canvas canvas, float x, float h, int color, Paint paint) { 879 paint.setStyle(Paint.Style.STROKE); 880 paint.setStrokeWidth(1.0f); 881 paint.setColor(color); 882 canvas.drawLine(x, 0, x, h, paint); 883 } 884 885 private static void drawRectangle(Canvas canvas, float x, float y, float w, float h, int color, 886 Paint paint) { 887 paint.setStyle(Paint.Style.STROKE); 888 paint.setStrokeWidth(1.0f); 889 paint.setColor(color); 890 canvas.translate(x, y); 891 canvas.drawRect(0, 0, w, h, paint); 892 canvas.translate(-x, -y); 893 } 894 895 public Paint newDefaultLabelPaint() { 896 final Paint paint = new Paint(); 897 paint.setAntiAlias(true); 898 paint.setTypeface(mKeyDrawParams.mKeyTextStyle); 899 paint.setTextSize(mKeyDrawParams.mKeyLabelSize); 900 return paint; 901 } 902 903 public void cancelAllMessages() { 904 mDrawingHandler.cancelAllMessages(); 905 if (mPreviewPlacerView != null) { 906 mPreviewPlacerView.cancelAllMessages(); 907 } 908 } 909 910 // Called by {@link PointerTracker} constructor to create a TextView. 911 @Override 912 public TextView inflateKeyPreviewText() { 913 final Context context = getContext(); 914 if (mKeyPreviewLayoutId != 0) { 915 return (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null); 916 } else { 917 return new TextView(context); 918 } 919 } 920 921 @Override 922 public void dismissKeyPreview(PointerTracker tracker) { 923 mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker); 924 } 925 926 private void addKeyPreview(TextView keyPreview) { 927 locatePreviewPlacerView(); 928 mPreviewPlacerView.addView( 929 keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0)); 930 } 931 932 private void locatePreviewPlacerView() { 933 if (mPreviewPlacerView.getParent() != null) { 934 return; 935 } 936 final int[] viewOrigin = new int[2]; 937 getLocationInWindow(viewOrigin); 938 mPreviewPlacerView.setOrigin(viewOrigin[0], viewOrigin[1]); 939 final ViewGroup windowContentView = 940 (ViewGroup)getRootView().findViewById(android.R.id.content); 941 windowContentView.addView(mPreviewPlacerView); 942 } 943 944 public void showGestureFloatingPreviewText(String gestureFloatingPreviewText) { 945 locatePreviewPlacerView(); 946 mPreviewPlacerView.setGestureFloatingPreviewText(gestureFloatingPreviewText); 947 } 948 949 public void dismissGestureFloatingPreviewText() { 950 locatePreviewPlacerView(); 951 mPreviewPlacerView.dismissGestureFloatingPreviewText(); 952 } 953 954 @Override 955 public void showGestureTrail(PointerTracker tracker) { 956 locatePreviewPlacerView(); 957 mPreviewPlacerView.invalidatePointer(tracker); 958 } 959 960 @SuppressWarnings("deprecation") // setBackgroundDrawable is replaced by setBackground in API16 961 @Override 962 public void showKeyPreview(PointerTracker tracker) { 963 if (!mShowKeyPreviewPopup) return; 964 965 final TextView previewText = tracker.getKeyPreviewText(); 966 // If the key preview has no parent view yet, add it to the ViewGroup which can place 967 // key preview absolutely in SoftInputWindow. 968 if (previewText.getParent() == null) { 969 addKeyPreview(previewText); 970 } 971 972 mDrawingHandler.cancelDismissKeyPreview(tracker); 973 final Key key = tracker.getKey(); 974 // If key is invalid or IME is already closed, we must not show key preview. 975 // Trying to show key preview while root window is closed causes 976 // WindowManager.BadTokenException. 977 if (key == null) 978 return; 979 980 final KeyPreviewDrawParams params = mKeyPreviewDrawParams; 981 final String label = key.isShiftedLetterActivated() ? key.mHintLabel : key.mLabel; 982 // What we show as preview should match what we show on a key top in onBufferDraw(). 983 if (label != null) { 984 // TODO Should take care of temporaryShiftLabel here. 985 previewText.setCompoundDrawables(null, null, null, null); 986 if (StringUtils.codePointCount(label) > 1) { 987 previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mKeyLetterSize); 988 previewText.setTypeface(Typeface.DEFAULT_BOLD); 989 } else { 990 previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mPreviewTextSize); 991 previewText.setTypeface(params.mKeyTextStyle); 992 } 993 previewText.setText(label); 994 } else { 995 previewText.setCompoundDrawables(null, null, null, 996 key.getPreviewIcon(mKeyboard.mIconsSet)); 997 previewText.setText(null); 998 } 999 previewText.setBackgroundDrawable(params.mPreviewBackground); 1000 1001 previewText.measure( 1002 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 1003 final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight; 1004 final int previewWidth = previewText.getMeasuredWidth(); 1005 final int previewHeight = params.mPreviewHeight; 1006 // The width and height of visible part of the key preview background. The content marker 1007 // of the background 9-patch have to cover the visible part of the background. 1008 params.mPreviewVisibleWidth = previewWidth - previewText.getPaddingLeft() 1009 - previewText.getPaddingRight(); 1010 params.mPreviewVisibleHeight = previewHeight - previewText.getPaddingTop() 1011 - previewText.getPaddingBottom(); 1012 // The distance between the top edge of the parent key and the bottom of the visible part 1013 // of the key preview background. 1014 params.mPreviewVisibleOffset = params.mPreviewOffset - previewText.getPaddingBottom(); 1015 getLocationInWindow(params.mCoordinates); 1016 // The key preview is horizontally aligned with the center of the visible part of the 1017 // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and 1018 // the left/right background is used if such background is specified. 1019 int previewX = key.mX + key.mVisualInsetsLeft - (previewWidth - keyDrawWidth) / 2 1020 + params.mCoordinates[0]; 1021 if (previewX < 0) { 1022 previewX = 0; 1023 if (params.mPreviewLeftBackground != null) { 1024 previewText.setBackgroundDrawable(params.mPreviewLeftBackground); 1025 } 1026 } else if (previewX > getWidth() - previewWidth) { 1027 previewX = getWidth() - previewWidth; 1028 if (params.mPreviewRightBackground != null) { 1029 previewText.setBackgroundDrawable(params.mPreviewRightBackground); 1030 } 1031 } 1032 // The key preview is placed vertically above the top edge of the parent key with an 1033 // arbitrary offset. 1034 final int previewY = key.mY - previewHeight + params.mPreviewOffset 1035 + params.mCoordinates[1]; 1036 1037 // Set the preview background state 1038 previewText.getBackground().setState( 1039 key.mMoreKeys != null ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET); 1040 previewText.setTextColor(params.mPreviewTextColor); 1041 ViewLayoutUtils.placeViewAt( 1042 previewText, previewX, previewY, previewWidth, previewHeight); 1043 previewText.setVisibility(VISIBLE); 1044 } 1045 1046 /** 1047 * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient 1048 * because the keyboard renders the keys to an off-screen buffer and an invalidate() only 1049 * draws the cached buffer. 1050 * @see #invalidateKey(Key) 1051 */ 1052 public void invalidateAllKeys() { 1053 mInvalidatedKeys.clear(); 1054 mInvalidateAllKeys = true; 1055 mBufferNeedsUpdate = true; 1056 invalidate(); 1057 } 1058 1059 /** 1060 * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only 1061 * one key is changing it's content. Any changes that affect the position or size of the key 1062 * may not be honored. 1063 * @param key key in the attached {@link Keyboard}. 1064 * @see #invalidateAllKeys 1065 */ 1066 @Override 1067 public void invalidateKey(Key key) { 1068 if (mInvalidateAllKeys) return; 1069 if (key == null) return; 1070 mInvalidatedKeys.add(key); 1071 final int x = key.mX + getPaddingLeft(); 1072 final int y = key.mY + getPaddingTop(); 1073 mWorkingRect.set(x, y, x + key.mWidth, y + key.mHeight); 1074 mBufferNeedsUpdate = true; 1075 invalidate(mWorkingRect); 1076 } 1077 1078 public void closing() { 1079 PointerTracker.dismissAllKeyPreviews(); 1080 cancelAllMessages(); 1081 1082 mInvalidateAllKeys = true; 1083 requestLayout(); 1084 } 1085 1086 @Override 1087 public boolean dismissMoreKeysPanel() { 1088 return false; 1089 } 1090 1091 public void purgeKeyboardAndClosing() { 1092 mKeyboard = null; 1093 closing(); 1094 } 1095 1096 @Override 1097 protected void onDetachedFromWindow() { 1098 super.onDetachedFromWindow(); 1099 closing(); 1100 mPreviewPlacerView.removeAllViews(); 1101 freeOffscreenBuffer(); 1102 } 1103} 1104