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