KeyboardView.java revision 877157e1f5ed44d6490eaf8fab430f9a73caf34d
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 366 mKeyDrawParams = new KeyDrawParams(a); 367 mKeyPreviewDrawParams = new KeyPreviewDrawParams(a, mKeyDrawParams); 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 mPreviewPlacerView = new PreviewPlacerView(context, a); 377 a.recycle(); 378 379 mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout; 380 381 mPaint.setAntiAlias(true); 382 } 383 384 // Read fraction value in TypedArray as float. 385 /* package */ static float getRatio(TypedArray a, int index) { 386 return a.getFraction(index, 1000, 1000, 1) / 1000.0f; 387 } 388 389 /** 390 * Attaches a keyboard to this view. The keyboard can be switched at any time and the 391 * view will re-layout itself to accommodate the keyboard. 392 * @see Keyboard 393 * @see #getKeyboard() 394 * @param keyboard the keyboard to display in this view 395 */ 396 public void setKeyboard(Keyboard keyboard) { 397 mKeyboard = keyboard; 398 LatinImeLogger.onSetKeyboard(keyboard); 399 requestLayout(); 400 invalidateAllKeys(); 401 final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; 402 mKeyDrawParams.updateKeyHeight(keyHeight); 403 mKeyPreviewDrawParams.updateKeyHeight(keyHeight); 404 } 405 406 /** 407 * Returns the current keyboard being displayed by this view. 408 * @return the currently attached keyboard 409 * @see #setKeyboard(Keyboard) 410 */ 411 public Keyboard getKeyboard() { 412 return mKeyboard; 413 } 414 415 /** 416 * Enables or disables the key feedback popup. This is a popup that shows a magnified 417 * version of the depressed key. By default the preview is enabled. 418 * @param previewEnabled whether or not to enable the key feedback preview 419 * @param delay the delay after which the preview is dismissed 420 * @see #isKeyPreviewPopupEnabled() 421 */ 422 public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) { 423 mShowKeyPreviewPopup = previewEnabled; 424 mDelayAfterPreview = delay; 425 } 426 427 /** 428 * Returns the enabled state of the key feedback preview 429 * @return whether or not the key feedback preview is enabled 430 * @see #setKeyPreviewPopupEnabled(boolean, int) 431 */ 432 public boolean isKeyPreviewPopupEnabled() { 433 return mShowKeyPreviewPopup; 434 } 435 436 public void setGesturePreviewMode(boolean drawsGesturePreviewTrail, 437 boolean drawsGestureFloatingPreviewText) { 438 mPreviewPlacerView.setGesturePreviewMode( 439 drawsGesturePreviewTrail, drawsGestureFloatingPreviewText); 440 } 441 442 @Override 443 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 444 if (mKeyboard != null) { 445 // The main keyboard expands to the display width. 446 final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom(); 447 setMeasuredDimension(widthMeasureSpec, height); 448 } else { 449 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 450 } 451 } 452 453 @Override 454 public void onDraw(Canvas canvas) { 455 super.onDraw(canvas); 456 if (canvas.isHardwareAccelerated()) { 457 onDrawKeyboard(canvas); 458 return; 459 } 460 461 final boolean bufferNeedsUpdates = mInvalidateAllKeys || !mInvalidatedKeys.isEmpty(); 462 if (bufferNeedsUpdates || mOffscreenBuffer == null) { 463 if (maybeAllocateOffscreenBuffer()) { 464 mInvalidateAllKeys = true; 465 // TODO: Stop using the offscreen canvas even when in software rendering 466 if (mOffscreenCanvas != null) { 467 mOffscreenCanvas.setBitmap(mOffscreenBuffer); 468 } else { 469 mOffscreenCanvas = new Canvas(mOffscreenBuffer); 470 } 471 } 472 onDrawKeyboard(mOffscreenCanvas); 473 } 474 canvas.drawBitmap(mOffscreenBuffer, 0, 0, null); 475 } 476 477 private boolean maybeAllocateOffscreenBuffer() { 478 final int width = getWidth(); 479 final int height = getHeight(); 480 if (width == 0 || height == 0) { 481 return false; 482 } 483 if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == width 484 && mOffscreenBuffer.getHeight() == height) { 485 return false; 486 } 487 freeOffscreenBuffer(); 488 mOffscreenBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 489 return true; 490 } 491 492 private void freeOffscreenBuffer() { 493 if (mOffscreenBuffer != null) { 494 mOffscreenBuffer.recycle(); 495 mOffscreenBuffer = null; 496 } 497 } 498 499 private void onDrawKeyboard(final Canvas canvas) { 500 if (mKeyboard == null) return; 501 502 final int width = getWidth(); 503 final int height = getHeight(); 504 final Paint paint = mPaint; 505 final KeyDrawParams params = mKeyDrawParams; 506 507 // Calculate clip region and set. 508 final boolean drawAllKeys = mInvalidateAllKeys || mInvalidatedKeys.isEmpty(); 509 final boolean isHardwareAccelerated = canvas.isHardwareAccelerated(); 510 // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on. 511 if (drawAllKeys || isHardwareAccelerated) { 512 mClipRegion.set(0, 0, width, height); 513 } else { 514 mClipRegion.setEmpty(); 515 for (final Key key : mInvalidatedKeys) { 516 if (mKeyboard.hasKey(key)) { 517 final int x = key.mX + getPaddingLeft(); 518 final int y = key.mY + getPaddingTop(); 519 mWorkingRect.set(x, y, x + key.mWidth, y + key.mHeight); 520 mClipRegion.union(mWorkingRect); 521 } 522 } 523 } 524 if (!isHardwareAccelerated) { 525 canvas.clipRegion(mClipRegion, Region.Op.REPLACE); 526 // Draw keyboard background. 527 canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); 528 final Drawable background = getBackground(); 529 if (background != null) { 530 background.draw(canvas); 531 } 532 } 533 534 // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on. 535 if (drawAllKeys || isHardwareAccelerated) { 536 // Draw all keys. 537 for (final Key key : mKeyboard.mKeys) { 538 onDrawKey(key, canvas, paint, params); 539 } 540 } else { 541 // Draw invalidated keys. 542 for (final Key key : mInvalidatedKeys) { 543 if (mKeyboard.hasKey(key)) { 544 onDrawKey(key, canvas, paint, params); 545 } 546 } 547 } 548 549 // Overlay a dark rectangle to dim. 550 if (mNeedsToDimEntireKeyboard) { 551 paint.setColor(Color.BLACK); 552 paint.setAlpha(mBackgroundDimAlpha); 553 // Note: clipRegion() above is in effect if it was called. 554 canvas.drawRect(0, 0, width, height, paint); 555 } 556 557 // ResearchLogging indicator. 558 // TODO: Reimplement using a keyboard background image specific to the ResearchLogger, 559 // and remove this call. 560 if (ProductionFlag.IS_EXPERIMENTAL) { 561 ResearchLogger.getInstance().paintIndicator(this, paint, canvas, width, height); 562 } 563 564 mInvalidatedKeys.clear(); 565 mInvalidateAllKeys = false; 566 } 567 568 public void dimEntireKeyboard(boolean dimmed) { 569 final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed; 570 mNeedsToDimEntireKeyboard = dimmed; 571 if (needsRedrawing) { 572 invalidateAllKeys(); 573 } 574 } 575 576 private void onDrawKey(Key key, Canvas canvas, Paint paint, KeyDrawParams params) { 577 final int keyDrawX = key.mX + key.mVisualInsetsLeft + getPaddingLeft(); 578 final int keyDrawY = key.mY + getPaddingTop(); 579 canvas.translate(keyDrawX, keyDrawY); 580 581 params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE; 582 if (!key.isSpacer()) { 583 onDrawKeyBackground(key, canvas, params); 584 } 585 onDrawKeyTopVisuals(key, canvas, paint, params); 586 587 canvas.translate(-keyDrawX, -keyDrawY); 588 } 589 590 // Draw key background. 591 protected void onDrawKeyBackground(Key key, Canvas canvas, KeyDrawParams params) { 592 final int bgWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight 593 + params.mPadding.left + params.mPadding.right; 594 final int bgHeight = key.mHeight + params.mPadding.top + params.mPadding.bottom; 595 final int bgX = -params.mPadding.left; 596 final int bgY = -params.mPadding.top; 597 final int[] drawableState = key.getCurrentDrawableState(); 598 final Drawable background = params.mKeyBackground; 599 background.setState(drawableState); 600 final Rect bounds = background.getBounds(); 601 if (bgWidth != bounds.right || bgHeight != bounds.bottom) { 602 background.setBounds(0, 0, bgWidth, bgHeight); 603 } 604 canvas.translate(bgX, bgY); 605 background.draw(canvas); 606 if (LatinImeLogger.sVISUALDEBUG) { 607 drawRectangle(canvas, 0, 0, bgWidth, bgHeight, 0x80c00000, new Paint()); 608 } 609 canvas.translate(-bgX, -bgY); 610 } 611 612 // Draw key top visuals. 613 protected void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint, KeyDrawParams params) { 614 final int keyWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight; 615 final int keyHeight = key.mHeight; 616 final float centerX = keyWidth * 0.5f; 617 final float centerY = keyHeight * 0.5f; 618 619 if (LatinImeLogger.sVISUALDEBUG) { 620 drawRectangle(canvas, 0, 0, keyWidth, keyHeight, 0x800000c0, new Paint()); 621 } 622 623 // Draw key label. 624 final Drawable icon = key.getIcon(mKeyboard.mIconsSet, params.mAnimAlpha); 625 float positionX = centerX; 626 if (key.mLabel != null) { 627 final String label = key.mLabel; 628 // For characters, use large font. For labels like "Done", use smaller font. 629 paint.setTypeface(key.selectTypeface(params.mKeyTextStyle)); 630 final int labelSize = key.selectTextSize(params.mKeyLetterSize, 631 params.mKeyLargeLetterSize, params.mKeyLabelSize, params.mKeyLargeLabelSize, 632 params.mKeyHintLabelSize); 633 paint.setTextSize(labelSize); 634 final float labelCharHeight = getCharHeight(KEY_LABEL_REFERENCE_CHAR, paint); 635 final float labelCharWidth = getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint); 636 637 // Vertical label text alignment. 638 final float baseline = centerY + labelCharHeight / 2; 639 640 // Horizontal label text alignment 641 float labelWidth = 0; 642 if (key.isAlignLeft()) { 643 positionX = (int)params.mKeyLabelHorizontalPadding; 644 paint.setTextAlign(Align.LEFT); 645 } else if (key.isAlignRight()) { 646 positionX = keyWidth - (int)params.mKeyLabelHorizontalPadding; 647 paint.setTextAlign(Align.RIGHT); 648 } else if (key.isAlignLeftOfCenter()) { 649 // TODO: Parameterise this? 650 positionX = centerX - labelCharWidth * 7 / 4; 651 paint.setTextAlign(Align.LEFT); 652 } else if (key.hasLabelWithIconLeft() && icon != null) { 653 labelWidth = getLabelWidth(label, paint) + icon.getIntrinsicWidth() 654 + LABEL_ICON_MARGIN * keyWidth; 655 positionX = centerX + labelWidth / 2; 656 paint.setTextAlign(Align.RIGHT); 657 } else if (key.hasLabelWithIconRight() && icon != null) { 658 labelWidth = getLabelWidth(label, paint) + icon.getIntrinsicWidth() 659 + LABEL_ICON_MARGIN * keyWidth; 660 positionX = centerX - labelWidth / 2; 661 paint.setTextAlign(Align.LEFT); 662 } else { 663 positionX = centerX; 664 paint.setTextAlign(Align.CENTER); 665 } 666 if (key.needsXScale()) { 667 paint.setTextScaleX( 668 Math.min(1.0f, (keyWidth * MAX_LABEL_RATIO) / getLabelWidth(label, paint))); 669 } 670 671 paint.setColor(key.isShiftedLetterActivated() 672 ? params.mKeyTextInactivatedColor : params.mKeyTextColor); 673 if (key.isEnabled()) { 674 // Set a drop shadow for the text 675 paint.setShadowLayer(params.mShadowRadius, 0, 0, params.mShadowColor); 676 } else { 677 // Make label invisible 678 paint.setColor(Color.TRANSPARENT); 679 } 680 params.blendAlpha(paint); 681 canvas.drawText(label, 0, label.length(), positionX, baseline, paint); 682 // Turn off drop shadow and reset x-scale. 683 paint.setShadowLayer(0, 0, 0, 0); 684 paint.setTextScaleX(1.0f); 685 686 if (icon != null) { 687 final int iconWidth = icon.getIntrinsicWidth(); 688 final int iconHeight = icon.getIntrinsicHeight(); 689 final int iconY = (keyHeight - iconHeight) / 2; 690 if (key.hasLabelWithIconLeft()) { 691 final int iconX = (int)(centerX - labelWidth / 2); 692 drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); 693 } else if (key.hasLabelWithIconRight()) { 694 final int iconX = (int)(centerX + labelWidth / 2 - iconWidth); 695 drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); 696 } 697 } 698 699 if (LatinImeLogger.sVISUALDEBUG) { 700 final Paint line = new Paint(); 701 drawHorizontalLine(canvas, baseline, keyWidth, 0xc0008000, line); 702 drawVerticalLine(canvas, positionX, keyHeight, 0xc0800080, line); 703 } 704 } 705 706 // Draw hint label. 707 if (key.mHintLabel != null) { 708 final String hint = key.mHintLabel; 709 final int hintColor; 710 final int hintSize; 711 if (key.hasHintLabel()) { 712 hintColor = params.mKeyHintLabelColor; 713 hintSize = params.mKeyHintLabelSize; 714 paint.setTypeface(Typeface.DEFAULT); 715 } else if (key.hasShiftedLetterHint()) { 716 hintColor = key.isShiftedLetterActivated() 717 ? params.mKeyShiftedLetterHintActivatedColor 718 : params.mKeyShiftedLetterHintInactivatedColor; 719 hintSize = params.mKeyShiftedLetterHintSize; 720 } else { // key.hasHintLetter() 721 hintColor = params.mKeyHintLetterColor; 722 hintSize = params.mKeyHintLetterSize; 723 } 724 paint.setColor(hintColor); 725 params.blendAlpha(paint); 726 paint.setTextSize(hintSize); 727 final float hintX, hintY; 728 if (key.hasHintLabel()) { 729 // The hint label is placed just right of the key label. Used mainly on 730 // "phone number" layout. 731 // TODO: Generalize the following calculations. 732 hintX = positionX + getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) * 2; 733 hintY = centerY + getCharHeight(KEY_LABEL_REFERENCE_CHAR, paint) / 2; 734 paint.setTextAlign(Align.LEFT); 735 } else if (key.hasShiftedLetterHint()) { 736 // The hint label is placed at top-right corner of the key. Used mainly on tablet. 737 hintX = keyWidth - params.mKeyShiftedLetterHintPadding 738 - getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2; 739 paint.getFontMetrics(mFontMetrics); 740 hintY = -mFontMetrics.top; 741 paint.setTextAlign(Align.CENTER); 742 } else { // key.hasHintLetter() 743 // The hint letter is placed at top-right corner of the key. Used mainly on phone. 744 hintX = keyWidth - params.mKeyHintLetterPadding 745 - getCharWidth(KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR, paint) / 2; 746 hintY = -paint.ascent(); 747 paint.setTextAlign(Align.CENTER); 748 } 749 canvas.drawText(hint, 0, hint.length(), hintX, hintY, paint); 750 751 if (LatinImeLogger.sVISUALDEBUG) { 752 final Paint line = new Paint(); 753 drawHorizontalLine(canvas, (int)hintY, keyWidth, 0xc0808000, line); 754 drawVerticalLine(canvas, (int)hintX, keyHeight, 0xc0808000, line); 755 } 756 } 757 758 // Draw key icon. 759 if (key.mLabel == null && icon != null) { 760 final int iconWidth = icon.getIntrinsicWidth(); 761 final int iconHeight = icon.getIntrinsicHeight(); 762 final int iconX, alignX; 763 final int iconY = (keyHeight - iconHeight) / 2; 764 if (key.isAlignLeft()) { 765 iconX = (int)params.mKeyLabelHorizontalPadding; 766 alignX = iconX; 767 } else if (key.isAlignRight()) { 768 iconX = keyWidth - (int)params.mKeyLabelHorizontalPadding - iconWidth; 769 alignX = iconX + iconWidth; 770 } else { // Align center 771 iconX = (keyWidth - iconWidth) / 2; 772 alignX = iconX + iconWidth / 2; 773 } 774 drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); 775 776 if (LatinImeLogger.sVISUALDEBUG) { 777 final Paint line = new Paint(); 778 drawVerticalLine(canvas, alignX, keyHeight, 0xc0800080, line); 779 drawRectangle(canvas, iconX, iconY, iconWidth, iconHeight, 0x80c00000, line); 780 } 781 } 782 783 if (key.hasPopupHint() && key.mMoreKeys != null && key.mMoreKeys.length > 0) { 784 drawKeyPopupHint(key, canvas, paint, params); 785 } 786 } 787 788 // Draw popup hint "..." at the bottom right corner of the key. 789 protected void drawKeyPopupHint(Key key, Canvas canvas, Paint paint, KeyDrawParams params) { 790 final int keyWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight; 791 final int keyHeight = key.mHeight; 792 793 paint.setTypeface(params.mKeyTextStyle); 794 paint.setTextSize(params.mKeyHintLetterSize); 795 paint.setColor(params.mKeyHintLabelColor); 796 paint.setTextAlign(Align.CENTER); 797 final float hintX = keyWidth - params.mKeyHintLetterPadding 798 - getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2; 799 final float hintY = keyHeight - params.mKeyPopupHintLetterPadding; 800 canvas.drawText(POPUP_HINT_CHAR, hintX, hintY, paint); 801 802 if (LatinImeLogger.sVISUALDEBUG) { 803 final Paint line = new Paint(); 804 drawHorizontalLine(canvas, (int)hintY, keyWidth, 0xc0808000, line); 805 drawVerticalLine(canvas, (int)hintX, keyHeight, 0xc0808000, line); 806 } 807 } 808 809 private static int getCharGeometryCacheKey(char referenceChar, Paint paint) { 810 final int labelSize = (int)paint.getTextSize(); 811 final Typeface face = paint.getTypeface(); 812 final int codePointOffset = referenceChar << 15; 813 if (face == Typeface.DEFAULT) { 814 return codePointOffset + labelSize; 815 } else if (face == Typeface.DEFAULT_BOLD) { 816 return codePointOffset + labelSize + 0x1000; 817 } else if (face == Typeface.MONOSPACE) { 818 return codePointOffset + labelSize + 0x2000; 819 } else { 820 return codePointOffset + labelSize; 821 } 822 } 823 824 // Working variable for the following methods. 825 private final Rect mTextBounds = new Rect(); 826 827 private float getCharHeight(char[] referenceChar, Paint paint) { 828 final int key = getCharGeometryCacheKey(referenceChar[0], paint); 829 final Float cachedValue = sTextHeightCache.get(key); 830 if (cachedValue != null) 831 return cachedValue; 832 833 paint.getTextBounds(referenceChar, 0, 1, mTextBounds); 834 final float height = mTextBounds.height(); 835 sTextHeightCache.put(key, height); 836 return height; 837 } 838 839 private float getCharWidth(char[] referenceChar, Paint paint) { 840 final int key = getCharGeometryCacheKey(referenceChar[0], paint); 841 final Float cachedValue = sTextWidthCache.get(key); 842 if (cachedValue != null) 843 return cachedValue; 844 845 paint.getTextBounds(referenceChar, 0, 1, mTextBounds); 846 final float width = mTextBounds.width(); 847 sTextWidthCache.put(key, width); 848 return width; 849 } 850 851 public float getLabelWidth(String label, Paint paint) { 852 paint.getTextBounds(label.toString(), 0, label.length(), mTextBounds); 853 return mTextBounds.width(); 854 } 855 856 protected static void drawIcon(Canvas canvas, Drawable icon, int x, int y, int width, 857 int height) { 858 canvas.translate(x, y); 859 icon.setBounds(0, 0, width, height); 860 icon.draw(canvas); 861 canvas.translate(-x, -y); 862 } 863 864 private static void drawHorizontalLine(Canvas canvas, float y, float w, int color, 865 Paint paint) { 866 paint.setStyle(Paint.Style.STROKE); 867 paint.setStrokeWidth(1.0f); 868 paint.setColor(color); 869 canvas.drawLine(0, y, w, y, paint); 870 } 871 872 private static void drawVerticalLine(Canvas canvas, float x, float h, int color, Paint paint) { 873 paint.setStyle(Paint.Style.STROKE); 874 paint.setStrokeWidth(1.0f); 875 paint.setColor(color); 876 canvas.drawLine(x, 0, x, h, paint); 877 } 878 879 private static void drawRectangle(Canvas canvas, float x, float y, float w, float h, int color, 880 Paint paint) { 881 paint.setStyle(Paint.Style.STROKE); 882 paint.setStrokeWidth(1.0f); 883 paint.setColor(color); 884 canvas.translate(x, y); 885 canvas.drawRect(0, 0, w, h, paint); 886 canvas.translate(-x, -y); 887 } 888 889 public Paint newDefaultLabelPaint() { 890 final Paint paint = new Paint(); 891 paint.setAntiAlias(true); 892 paint.setTypeface(mKeyDrawParams.mKeyTextStyle); 893 paint.setTextSize(mKeyDrawParams.mKeyLabelSize); 894 return paint; 895 } 896 897 public void cancelAllMessages() { 898 mDrawingHandler.cancelAllMessages(); 899 if (mPreviewPlacerView != null) { 900 mPreviewPlacerView.cancelAllMessages(); 901 } 902 } 903 904 // Called by {@link PointerTracker} constructor to create a TextView. 905 @Override 906 public TextView inflateKeyPreviewText() { 907 final Context context = getContext(); 908 if (mKeyPreviewLayoutId != 0) { 909 return (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null); 910 } else { 911 return new TextView(context); 912 } 913 } 914 915 @Override 916 public void dismissKeyPreview(PointerTracker tracker) { 917 mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker); 918 } 919 920 private void addKeyPreview(TextView keyPreview) { 921 locatePreviewPlacerView(); 922 mPreviewPlacerView.addView( 923 keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0)); 924 } 925 926 private void locatePreviewPlacerView() { 927 if (mPreviewPlacerView.getParent() != null) { 928 return; 929 } 930 final int[] viewOrigin = new int[2]; 931 getLocationInWindow(viewOrigin); 932 mPreviewPlacerView.setOrigin(viewOrigin[0], viewOrigin[1]); 933 final ViewGroup windowContentView = 934 (ViewGroup)getRootView().findViewById(android.R.id.content); 935 windowContentView.addView(mPreviewPlacerView); 936 } 937 938 public void showGestureFloatingPreviewText(String gestureFloatingPreviewText) { 939 locatePreviewPlacerView(); 940 mPreviewPlacerView.setGestureFloatingPreviewText(gestureFloatingPreviewText); 941 } 942 943 public void dismissGestureFloatingPreviewText() { 944 locatePreviewPlacerView(); 945 mPreviewPlacerView.dismissGestureFloatingPreviewText(); 946 } 947 948 @Override 949 public void showGestureTrail(PointerTracker tracker) { 950 locatePreviewPlacerView(); 951 mPreviewPlacerView.invalidatePointer(tracker); 952 } 953 954 @SuppressWarnings("deprecation") // setBackgroundDrawable is replaced by setBackground in API16 955 @Override 956 public void showKeyPreview(PointerTracker tracker) { 957 if (!mShowKeyPreviewPopup) return; 958 959 final TextView previewText = tracker.getKeyPreviewText(); 960 // If the key preview has no parent view yet, add it to the ViewGroup which can place 961 // key preview absolutely in SoftInputWindow. 962 if (previewText.getParent() == null) { 963 addKeyPreview(previewText); 964 } 965 966 mDrawingHandler.cancelDismissKeyPreview(tracker); 967 final Key key = tracker.getKey(); 968 // If key is invalid or IME is already closed, we must not show key preview. 969 // Trying to show key preview while root window is closed causes 970 // WindowManager.BadTokenException. 971 if (key == null) 972 return; 973 974 final KeyPreviewDrawParams params = mKeyPreviewDrawParams; 975 final String label = key.isShiftedLetterActivated() ? key.mHintLabel : key.mLabel; 976 // What we show as preview should match what we show on a key top in onBufferDraw(). 977 if (label != null) { 978 // TODO Should take care of temporaryShiftLabel here. 979 previewText.setCompoundDrawables(null, null, null, null); 980 if (StringUtils.codePointCount(label) > 1) { 981 previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mKeyLetterSize); 982 previewText.setTypeface(Typeface.DEFAULT_BOLD); 983 } else { 984 previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mPreviewTextSize); 985 previewText.setTypeface(params.mKeyTextStyle); 986 } 987 previewText.setText(label); 988 } else { 989 previewText.setCompoundDrawables(null, null, null, 990 key.getPreviewIcon(mKeyboard.mIconsSet)); 991 previewText.setText(null); 992 } 993 previewText.setBackgroundDrawable(params.mPreviewBackground); 994 995 previewText.measure( 996 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 997 final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight; 998 final int previewWidth = previewText.getMeasuredWidth(); 999 final int previewHeight = params.mPreviewHeight; 1000 // The width and height of visible part of the key preview background. The content marker 1001 // of the background 9-patch have to cover the visible part of the background. 1002 params.mPreviewVisibleWidth = previewWidth - previewText.getPaddingLeft() 1003 - previewText.getPaddingRight(); 1004 params.mPreviewVisibleHeight = previewHeight - previewText.getPaddingTop() 1005 - previewText.getPaddingBottom(); 1006 // The distance between the top edge of the parent key and the bottom of the visible part 1007 // of the key preview background. 1008 params.mPreviewVisibleOffset = params.mPreviewOffset - previewText.getPaddingBottom(); 1009 getLocationInWindow(params.mCoordinates); 1010 // The key preview is horizontally aligned with the center of the visible part of the 1011 // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and 1012 // the left/right background is used if such background is specified. 1013 int previewX = key.mX + key.mVisualInsetsLeft - (previewWidth - keyDrawWidth) / 2 1014 + params.mCoordinates[0]; 1015 if (previewX < 0) { 1016 previewX = 0; 1017 if (params.mPreviewLeftBackground != null) { 1018 previewText.setBackgroundDrawable(params.mPreviewLeftBackground); 1019 } 1020 } else if (previewX > getWidth() - previewWidth) { 1021 previewX = getWidth() - previewWidth; 1022 if (params.mPreviewRightBackground != null) { 1023 previewText.setBackgroundDrawable(params.mPreviewRightBackground); 1024 } 1025 } 1026 // The key preview is placed vertically above the top edge of the parent key with an 1027 // arbitrary offset. 1028 final int previewY = key.mY - previewHeight + params.mPreviewOffset 1029 + params.mCoordinates[1]; 1030 1031 // Set the preview background state 1032 previewText.getBackground().setState( 1033 key.mMoreKeys != null ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET); 1034 previewText.setTextColor(params.mPreviewTextColor); 1035 ViewLayoutUtils.placeViewAt( 1036 previewText, previewX, previewY, previewWidth, previewHeight); 1037 previewText.setVisibility(VISIBLE); 1038 } 1039 1040 /** 1041 * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient 1042 * because the keyboard renders the keys to an off-screen buffer and an invalidate() only 1043 * draws the cached buffer. 1044 * @see #invalidateKey(Key) 1045 */ 1046 public void invalidateAllKeys() { 1047 mInvalidatedKeys.clear(); 1048 mInvalidateAllKeys = true; 1049 invalidate(); 1050 } 1051 1052 /** 1053 * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only 1054 * one key is changing it's content. Any changes that affect the position or size of the key 1055 * may not be honored. 1056 * @param key key in the attached {@link Keyboard}. 1057 * @see #invalidateAllKeys 1058 */ 1059 @Override 1060 public void invalidateKey(Key key) { 1061 if (mInvalidateAllKeys) return; 1062 if (key == null) return; 1063 mInvalidatedKeys.add(key); 1064 final int x = key.mX + getPaddingLeft(); 1065 final int y = key.mY + getPaddingTop(); 1066 invalidate(x, y, x + key.mWidth, y + key.mHeight); 1067 } 1068 1069 public void closing() { 1070 PointerTracker.dismissAllKeyPreviews(); 1071 cancelAllMessages(); 1072 1073 mInvalidateAllKeys = true; 1074 requestLayout(); 1075 } 1076 1077 @Override 1078 public boolean dismissMoreKeysPanel() { 1079 return false; 1080 } 1081 1082 public void purgeKeyboardAndClosing() { 1083 mKeyboard = null; 1084 closing(); 1085 } 1086 1087 @Override 1088 protected void onDetachedFromWindow() { 1089 super.onDetachedFromWindow(); 1090 closing(); 1091 mPreviewPlacerView.removeAllViews(); 1092 freeOffscreenBuffer(); 1093 } 1094} 1095