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