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