MainKeyboardView.java revision dec599d1723f4ff52f066bd2dd1a4457d30cd33c
1/* 2 * Copyright (C) 2011 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.animation.AnimatorInflater; 20import android.animation.ObjectAnimator; 21import android.content.Context; 22import android.content.SharedPreferences; 23import android.content.pm.PackageManager; 24import android.content.res.TypedArray; 25import android.graphics.Canvas; 26import android.graphics.Color; 27import android.graphics.Paint; 28import android.graphics.Paint.Align; 29import android.graphics.Typeface; 30import android.preference.PreferenceManager; 31import android.util.AttributeSet; 32import android.util.Log; 33import android.view.LayoutInflater; 34import android.view.MotionEvent; 35import android.view.View; 36import android.view.ViewGroup; 37import android.view.inputmethod.InputMethodSubtype; 38import android.widget.TextView; 39 40import com.android.inputmethod.accessibility.AccessibilityUtils; 41import com.android.inputmethod.accessibility.MainKeyboardAccessibilityDelegate; 42import com.android.inputmethod.annotations.ExternallyReferenced; 43import com.android.inputmethod.keyboard.internal.DrawingHandler; 44import com.android.inputmethod.keyboard.internal.DrawingPreviewPlacerView; 45import com.android.inputmethod.keyboard.internal.GestureFloatingTextDrawingPreview; 46import com.android.inputmethod.keyboard.internal.GestureTrailsDrawingPreview; 47import com.android.inputmethod.keyboard.internal.KeyDrawParams; 48import com.android.inputmethod.keyboard.internal.KeyPreviewChoreographer; 49import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams; 50import com.android.inputmethod.keyboard.internal.LanguageOnSpacebarHelper; 51import com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper; 52import com.android.inputmethod.keyboard.internal.SlidingKeyInputDrawingPreview; 53import com.android.inputmethod.keyboard.internal.TimerHandler; 54import com.android.inputmethod.latin.Constants; 55import com.android.inputmethod.latin.R; 56import com.android.inputmethod.latin.SuggestedWords; 57import com.android.inputmethod.latin.settings.DebugSettings; 58import com.android.inputmethod.latin.utils.CoordinateUtils; 59import com.android.inputmethod.latin.utils.SpacebarLanguageUtils; 60import com.android.inputmethod.latin.utils.TypefaceUtils; 61 62import java.util.WeakHashMap; 63 64/** 65 * A view that is responsible for detecting key presses and touch movements. 66 * 67 * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextRatio 68 * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextColor 69 * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextShadowRadius 70 * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextShadowColor 71 * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha 72 * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator 73 * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator 74 * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator 75 * @attr ref R.styleable#MainKeyboardView_keyHysteresisDistance 76 * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdTime 77 * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdDistance 78 * @attr ref R.styleable#MainKeyboardView_keySelectionByDraggingFinger 79 * @attr ref R.styleable#MainKeyboardView_keyRepeatStartTimeout 80 * @attr ref R.styleable#MainKeyboardView_keyRepeatInterval 81 * @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout 82 * @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout 83 * @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout 84 * @attr ref R.styleable#MainKeyboardView_keyPreviewLayout 85 * @attr ref R.styleable#MainKeyboardView_keyPreviewOffset 86 * @attr ref R.styleable#MainKeyboardView_keyPreviewHeight 87 * @attr ref R.styleable#MainKeyboardView_keyPreviewLingerTimeout 88 * @attr ref R.styleable#MainKeyboardView_moreKeysKeyboardLayout 89 * @attr ref R.styleable#MainKeyboardView_backgroundDimAlpha 90 * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint 91 * @attr ref R.styleable#MainKeyboardView_gestureFloatingPreviewTextLingerTimeout 92 * @attr ref R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping 93 * @attr ref R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold 94 * @attr ref R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration 95 * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom 96 * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo 97 * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom 98 * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo 99 * @attr ref R.styleable#MainKeyboardView_gestureSamplingMinimumDistance 100 * @attr ref R.styleable#MainKeyboardView_gestureRecognitionMinimumTime 101 * @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold 102 * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration 103 */ 104public final class MainKeyboardView extends KeyboardView implements PointerTracker.DrawingProxy, 105 MoreKeysPanel.Controller, DrawingHandler.Callbacks, TimerHandler.Callbacks { 106 private static final String TAG = MainKeyboardView.class.getSimpleName(); 107 108 /** Listener for {@link KeyboardActionListener}. */ 109 private KeyboardActionListener mKeyboardActionListener; 110 111 /* Space key and its icon and background. */ 112 private Key mSpaceKey; 113 // Stuff to draw language name on spacebar. 114 private final int mLanguageOnSpacebarFinalAlpha; 115 private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator; 116 private int mLanguageOnSpacebarFormatType; 117 private boolean mHasMultipleEnabledIMEsOrSubtypes; 118 private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE; 119 private final float mLanguageOnSpacebarTextRatio; 120 private float mLanguageOnSpacebarTextSize; 121 private final int mLanguageOnSpacebarTextColor; 122 private final float mLanguageOnSpacebarTextShadowRadius; 123 private final int mLanguageOnSpacebarTextShadowColor; 124 private static final float LANGUAGE_ON_SPACEBAR_TEXT_SHADOW_RADIUS_DISABLED = -1.0f; 125 // The minimum x-scale to fit the language name on spacebar. 126 private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f; 127 128 // Stuff to draw altCodeWhileTyping keys. 129 private final ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator; 130 private final ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator; 131 private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE; 132 133 // Drawing preview placer view 134 private final DrawingPreviewPlacerView mDrawingPreviewPlacerView; 135 private final int[] mOriginCoords = CoordinateUtils.newInstance(); 136 private final GestureFloatingTextDrawingPreview mGestureFloatingTextDrawingPreview; 137 private final GestureTrailsDrawingPreview mGestureTrailsDrawingPreview; 138 private final SlidingKeyInputDrawingPreview mSlidingKeyInputDrawingPreview; 139 140 // Key preview 141 private final KeyPreviewDrawParams mKeyPreviewDrawParams; 142 private final KeyPreviewChoreographer mKeyPreviewChoreographer; 143 144 // More keys keyboard 145 private final Paint mBackgroundDimAlphaPaint = new Paint(); 146 private boolean mNeedsToDimEntireKeyboard; 147 private final View mMoreKeysKeyboardContainer; 148 private final WeakHashMap<Key, Keyboard> mMoreKeysKeyboardCache = new WeakHashMap<>(); 149 private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint; 150 // More keys panel (used by both more keys keyboard and more suggestions view) 151 // TODO: Consider extending to support multiple more keys panels 152 private MoreKeysPanel mMoreKeysPanel; 153 154 // Gesture floating preview text 155 // TODO: Make this parameter customizable by user via settings. 156 private int mGestureFloatingPreviewTextLingerTimeout; 157 158 private final KeyDetector mKeyDetector; 159 private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper; 160 161 private final TimerHandler mKeyTimerHandler; 162 private final int mLanguageOnSpacebarHorizontalMargin; 163 164 private final DrawingHandler mDrawingHandler = new DrawingHandler(this); 165 166 private MainKeyboardAccessibilityDelegate mAccessibilityDelegate; 167 168 public MainKeyboardView(final Context context, final AttributeSet attrs) { 169 this(context, attrs, R.attr.mainKeyboardViewStyle); 170 } 171 172 public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) { 173 super(context, attrs, defStyle); 174 175 mDrawingPreviewPlacerView = new DrawingPreviewPlacerView(context, attrs); 176 177 final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes( 178 attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView); 179 final int ignoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt( 180 R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0); 181 final int gestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt( 182 R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0); 183 mKeyTimerHandler = new TimerHandler( 184 this, ignoreAltCodeKeyTimeout, gestureRecognitionUpdateTime); 185 186 final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension( 187 R.styleable.MainKeyboardView_keyHysteresisDistance, 0.0f); 188 final float keyHysteresisDistanceForSlidingModifier = mainKeyboardViewAttr.getDimension( 189 R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0.0f); 190 mKeyDetector = new KeyDetector( 191 keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier); 192 193 PointerTracker.init(mainKeyboardViewAttr, mKeyTimerHandler, this /* DrawingProxy */); 194 195 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 196 final boolean forceNonDistinctMultitouch = prefs.getBoolean( 197 DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, false); 198 final boolean hasDistinctMultitouch = context.getPackageManager() 199 .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT) 200 && !forceNonDistinctMultitouch; 201 mNonDistinctMultitouchHelper = hasDistinctMultitouch ? null 202 : new NonDistinctMultitouchHelper(); 203 204 final int backgroundDimAlpha = mainKeyboardViewAttr.getInt( 205 R.styleable.MainKeyboardView_backgroundDimAlpha, 0); 206 mBackgroundDimAlphaPaint.setColor(Color.BLACK); 207 mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha); 208 mLanguageOnSpacebarTextRatio = mainKeyboardViewAttr.getFraction( 209 R.styleable.MainKeyboardView_languageOnSpacebarTextRatio, 1, 1, 1.0f); 210 mLanguageOnSpacebarTextColor = mainKeyboardViewAttr.getColor( 211 R.styleable.MainKeyboardView_languageOnSpacebarTextColor, 0); 212 mLanguageOnSpacebarTextShadowRadius = mainKeyboardViewAttr.getFloat( 213 R.styleable.MainKeyboardView_languageOnSpacebarTextShadowRadius, 214 LANGUAGE_ON_SPACEBAR_TEXT_SHADOW_RADIUS_DISABLED); 215 mLanguageOnSpacebarTextShadowColor = mainKeyboardViewAttr.getColor( 216 R.styleable.MainKeyboardView_languageOnSpacebarTextShadowColor, 0); 217 mLanguageOnSpacebarFinalAlpha = mainKeyboardViewAttr.getInt( 218 R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha, 219 Constants.Color.ALPHA_OPAQUE); 220 final int languageOnSpacebarFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId( 221 R.styleable.MainKeyboardView_languageOnSpacebarFadeoutAnimator, 0); 222 final int altCodeKeyWhileTypingFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId( 223 R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0); 224 final int altCodeKeyWhileTypingFadeinAnimatorResId = mainKeyboardViewAttr.getResourceId( 225 R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0); 226 227 mKeyPreviewDrawParams = new KeyPreviewDrawParams(mainKeyboardViewAttr); 228 mKeyPreviewChoreographer = new KeyPreviewChoreographer(mKeyPreviewDrawParams); 229 230 final int moreKeysKeyboardLayoutId = mainKeyboardViewAttr.getResourceId( 231 R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0); 232 mConfigShowMoreKeysKeyboardAtTouchedPoint = mainKeyboardViewAttr.getBoolean( 233 R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false); 234 235 mGestureFloatingPreviewTextLingerTimeout = mainKeyboardViewAttr.getInt( 236 R.styleable.MainKeyboardView_gestureFloatingPreviewTextLingerTimeout, 0); 237 238 mGestureFloatingTextDrawingPreview = new GestureFloatingTextDrawingPreview( 239 mDrawingPreviewPlacerView, mainKeyboardViewAttr); 240 mDrawingPreviewPlacerView.addPreview(mGestureFloatingTextDrawingPreview); 241 242 mGestureTrailsDrawingPreview = new GestureTrailsDrawingPreview( 243 mDrawingPreviewPlacerView, mainKeyboardViewAttr); 244 mDrawingPreviewPlacerView.addPreview(mGestureTrailsDrawingPreview); 245 246 mSlidingKeyInputDrawingPreview = new SlidingKeyInputDrawingPreview( 247 mDrawingPreviewPlacerView, mainKeyboardViewAttr); 248 mDrawingPreviewPlacerView.addPreview(mSlidingKeyInputDrawingPreview); 249 mainKeyboardViewAttr.recycle(); 250 251 mMoreKeysKeyboardContainer = LayoutInflater.from(getContext()) 252 .inflate(moreKeysKeyboardLayoutId, null); 253 mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator( 254 languageOnSpacebarFadeoutAnimatorResId, this); 255 mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator( 256 altCodeKeyWhileTypingFadeoutAnimatorResId, this); 257 mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator( 258 altCodeKeyWhileTypingFadeinAnimatorResId, this); 259 260 mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; 261 262 mLanguageOnSpacebarHorizontalMargin = (int)getResources().getDimension( 263 R.dimen.config_language_on_spacebar_horizontal_margin); 264 } 265 266 @Override 267 public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) { 268 super.setHardwareAcceleratedDrawingEnabled(enabled); 269 mDrawingPreviewPlacerView.setHardwareAcceleratedDrawingEnabled(enabled); 270 } 271 272 private ObjectAnimator loadObjectAnimator(final int resId, final Object target) { 273 if (resId == 0) { 274 // TODO: Stop returning null. 275 return null; 276 } 277 final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator( 278 getContext(), resId); 279 if (animator != null) { 280 animator.setTarget(target); 281 } 282 return animator; 283 } 284 285 private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel, 286 final ObjectAnimator animatorToStart) { 287 if (animatorToCancel == null || animatorToStart == null) { 288 // TODO: Stop using null as a no-operation animator. 289 return; 290 } 291 float startFraction = 0.0f; 292 if (animatorToCancel.isStarted()) { 293 animatorToCancel.cancel(); 294 startFraction = 1.0f - animatorToCancel.getAnimatedFraction(); 295 } 296 final long startTime = (long)(animatorToStart.getDuration() * startFraction); 297 animatorToStart.start(); 298 animatorToStart.setCurrentPlayTime(startTime); 299 } 300 301 // Implements {@link TimerHander.Callbacks} method. 302 @Override 303 public void startWhileTypingFadeinAnimation() { 304 cancelAndStartAnimators( 305 mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator); 306 } 307 308 @Override 309 public void startWhileTypingFadeoutAnimation() { 310 cancelAndStartAnimators( 311 mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator); 312 } 313 314 @ExternallyReferenced 315 public int getLanguageOnSpacebarAnimAlpha() { 316 return mLanguageOnSpacebarAnimAlpha; 317 } 318 319 @ExternallyReferenced 320 public void setLanguageOnSpacebarAnimAlpha(final int alpha) { 321 mLanguageOnSpacebarAnimAlpha = alpha; 322 invalidateKey(mSpaceKey); 323 } 324 325 @ExternallyReferenced 326 public int getAltCodeKeyWhileTypingAnimAlpha() { 327 return mAltCodeKeyWhileTypingAnimAlpha; 328 } 329 330 @ExternallyReferenced 331 public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) { 332 if (mAltCodeKeyWhileTypingAnimAlpha == alpha) { 333 return; 334 } 335 // Update the visual of alt-code-key-while-typing. 336 mAltCodeKeyWhileTypingAnimAlpha = alpha; 337 final Keyboard keyboard = getKeyboard(); 338 if (keyboard == null) { 339 return; 340 } 341 for (final Key key : keyboard.mAltCodeKeysWhileTyping) { 342 invalidateKey(key); 343 } 344 } 345 346 public void setKeyboardActionListener(final KeyboardActionListener listener) { 347 mKeyboardActionListener = listener; 348 PointerTracker.setKeyboardActionListener(listener); 349 } 350 351 // TODO: We should reconsider which coordinate system should be used to represent keyboard 352 // event. 353 public int getKeyX(final int x) { 354 return Constants.isValidCoordinate(x) ? mKeyDetector.getTouchX(x) : x; 355 } 356 357 // TODO: We should reconsider which coordinate system should be used to represent keyboard 358 // event. 359 public int getKeyY(final int y) { 360 return Constants.isValidCoordinate(y) ? mKeyDetector.getTouchY(y) : y; 361 } 362 363 /** 364 * Attaches a keyboard to this view. The keyboard can be switched at any time and the 365 * view will re-layout itself to accommodate the keyboard. 366 * @see Keyboard 367 * @see #getKeyboard() 368 * @param keyboard the keyboard to display in this view 369 */ 370 @Override 371 public void setKeyboard(final Keyboard keyboard) { 372 // Remove any pending messages, except dismissing preview and key repeat. 373 mKeyTimerHandler.cancelLongPressTimers(); 374 super.setKeyboard(keyboard); 375 mKeyDetector.setKeyboard( 376 keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection()); 377 PointerTracker.setKeyDetector(mKeyDetector); 378 mMoreKeysKeyboardCache.clear(); 379 380 mSpaceKey = keyboard.getKey(Constants.CODE_SPACE); 381 final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; 382 mLanguageOnSpacebarTextSize = keyHeight * mLanguageOnSpacebarTextRatio; 383 384 if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) { 385 if (mAccessibilityDelegate == null) { 386 mAccessibilityDelegate = new MainKeyboardAccessibilityDelegate(this, mKeyDetector); 387 } 388 mAccessibilityDelegate.setKeyboard(keyboard); 389 } else { 390 mAccessibilityDelegate = null; 391 } 392 } 393 394 /** 395 * Enables or disables the key feedback popup. This is a popup that shows a magnified 396 * version of the depressed key. By default the preview is enabled. 397 * @param previewEnabled whether or not to enable the key feedback preview 398 * @param delay the delay after which the preview is dismissed 399 * @see #isKeyPreviewPopupEnabled() 400 */ 401 public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) { 402 mKeyPreviewDrawParams.setPopupEnabled(previewEnabled, delay); 403 } 404 405 public void setKeyPreviewAnimationParams(final float showUpStartScale, final int showUpDuration, 406 final float dismissEndScale, final int dismissDuration) { 407 mKeyPreviewDrawParams.setAnimationParams( 408 showUpStartScale, showUpDuration, dismissEndScale, dismissDuration); 409 } 410 411 private void locatePreviewPlacerView() { 412 getLocationInWindow(mOriginCoords); 413 mDrawingPreviewPlacerView.setKeyboardViewGeometry(mOriginCoords, getWidth(), getHeight()); 414 } 415 416 private void installPreviewPlacerView() { 417 final View rootView = getRootView(); 418 if (rootView == null) { 419 Log.w(TAG, "Cannot find root view"); 420 return; 421 } 422 final ViewGroup windowContentView = (ViewGroup)rootView.findViewById(android.R.id.content); 423 // Note: It'd be very weird if we get null by android.R.id.content. 424 if (windowContentView == null) { 425 Log.w(TAG, "Cannot find android.R.id.content view to add DrawingPreviewPlacerView"); 426 return; 427 } 428 windowContentView.addView(mDrawingPreviewPlacerView); 429 } 430 431 /** 432 * Returns the enabled state of the key feedback preview 433 * @return whether or not the key feedback preview is enabled 434 * @see #setKeyPreviewPopupEnabled(boolean, int) 435 */ 436 public boolean isKeyPreviewPopupEnabled() { 437 return mKeyPreviewDrawParams.isPopupEnabled(); 438 } 439 440 // Implements {@link DrawingHandler.Callbacks} method. 441 @Override 442 public void dismissAllKeyPreviews() { 443 mKeyPreviewChoreographer.dismissAllKeyPreviews(); 444 PointerTracker.setReleasedKeyGraphicsToAllKeys(); 445 } 446 447 @Override 448 public void showKeyPreview(final Key key) { 449 // If key is invalid or IME is already closed, we must not show key preview. 450 // Trying to show key preview while root window is closed causes 451 // WindowManager.BadTokenException. 452 if (key == null) { 453 return; 454 } 455 456 final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams; 457 final Keyboard keyboard = getKeyboard(); 458 if (!previewParams.isPopupEnabled()) { 459 previewParams.setVisibleOffset(-keyboard.mVerticalGap); 460 return; 461 } 462 463 locatePreviewPlacerView(); 464 final TextView previewTextView = mKeyPreviewChoreographer.getKeyPreviewTextView( 465 key, mDrawingPreviewPlacerView); 466 getLocationInWindow(mOriginCoords); 467 mKeyPreviewChoreographer.placeKeyPreview(key, previewTextView, keyboard.mIconsSet, 468 mKeyDrawParams, getWidth(), mOriginCoords); 469 mKeyPreviewChoreographer.showKeyPreview(key, previewTextView, isHardwareAccelerated()); 470 } 471 472 // Implements {@link TimerHandler.Callbacks} method. 473 @Override 474 public void dismissKeyPreviewWithoutDelay(final Key key) { 475 mKeyPreviewChoreographer.dismissKeyPreview(key, false /* withAnimation */); 476 // To redraw key top letter. 477 invalidateKey(key); 478 } 479 480 @Override 481 public void dismissKeyPreview(final Key key) { 482 if (!isHardwareAccelerated()) { 483 // TODO: Implement preference option to control key preview method and duration. 484 mDrawingHandler.dismissKeyPreview(mKeyPreviewDrawParams.getLingerTimeout(), key); 485 return; 486 } 487 mKeyPreviewChoreographer.dismissKeyPreview(key, true /* withAnimation */); 488 } 489 490 public void setSlidingKeyInputPreviewEnabled(final boolean enabled) { 491 mSlidingKeyInputDrawingPreview.setPreviewEnabled(enabled); 492 } 493 494 @Override 495 public void showSlidingKeyInputPreview(final PointerTracker tracker) { 496 locatePreviewPlacerView(); 497 mSlidingKeyInputDrawingPreview.setPreviewPosition(tracker); 498 } 499 500 @Override 501 public void dismissSlidingKeyInputPreview() { 502 mSlidingKeyInputDrawingPreview.dismissSlidingKeyInputPreview(); 503 } 504 505 private void setGesturePreviewMode(final boolean isGestureTrailEnabled, 506 final boolean isGestureFloatingPreviewTextEnabled) { 507 mGestureFloatingTextDrawingPreview.setPreviewEnabled(isGestureFloatingPreviewTextEnabled); 508 mGestureTrailsDrawingPreview.setPreviewEnabled(isGestureTrailEnabled); 509 } 510 511 // Implements {@link DrawingHandler.Callbacks} method. 512 @Override 513 public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) { 514 locatePreviewPlacerView(); 515 mGestureFloatingTextDrawingPreview.setSuggetedWords(suggestedWords); 516 } 517 518 public void dismissGestureFloatingPreviewText() { 519 locatePreviewPlacerView(); 520 mDrawingHandler.dismissGestureFloatingPreviewText(mGestureFloatingPreviewTextLingerTimeout); 521 } 522 523 @Override 524 public void showGestureTrail(final PointerTracker tracker, 525 final boolean showsFloatingPreviewText) { 526 locatePreviewPlacerView(); 527 if (showsFloatingPreviewText) { 528 mGestureFloatingTextDrawingPreview.setPreviewPosition(tracker); 529 } 530 mGestureTrailsDrawingPreview.setPreviewPosition(tracker); 531 } 532 533 // Note that this method is called from a non-UI thread. 534 @SuppressWarnings("static-method") 535 public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) { 536 PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable); 537 } 538 539 public void setGestureHandlingEnabledByUser(final boolean isGestureHandlingEnabledByUser, 540 final boolean isGestureTrailEnabled, 541 final boolean isGestureFloatingPreviewTextEnabled) { 542 PointerTracker.setGestureHandlingEnabledByUser(isGestureHandlingEnabledByUser); 543 setGesturePreviewMode(isGestureHandlingEnabledByUser && isGestureTrailEnabled, 544 isGestureHandlingEnabledByUser && isGestureFloatingPreviewTextEnabled); 545 } 546 547 @Override 548 protected void onAttachedToWindow() { 549 super.onAttachedToWindow(); 550 installPreviewPlacerView(); 551 } 552 553 @Override 554 protected void onDetachedFromWindow() { 555 super.onDetachedFromWindow(); 556 mDrawingPreviewPlacerView.removeAllViews(); 557 } 558 559 private MoreKeysPanel onCreateMoreKeysPanel(final Key key, final Context context) { 560 if (key.getMoreKeys() == null) { 561 return null; 562 } 563 Keyboard moreKeysKeyboard = mMoreKeysKeyboardCache.get(key); 564 if (moreKeysKeyboard == null) { 565 moreKeysKeyboard = new MoreKeysKeyboard.Builder( 566 context, key, this, mKeyPreviewDrawParams).build(); 567 mMoreKeysKeyboardCache.put(key, moreKeysKeyboard); 568 } 569 570 final View container = mMoreKeysKeyboardContainer; 571 final MoreKeysKeyboardView moreKeysKeyboardView = 572 (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view); 573 moreKeysKeyboardView.setKeyboard(moreKeysKeyboard); 574 container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 575 return moreKeysKeyboardView; 576 } 577 578 // Implements {@link TimerHandler.Callbacks} method. 579 /** 580 * Called when a key is long pressed. 581 * @param tracker the pointer tracker which pressed the parent key 582 */ 583 @Override 584 public void onLongPress(final PointerTracker tracker) { 585 if (isShowingMoreKeysPanel()) { 586 return; 587 } 588 final Key key = tracker.getKey(); 589 if (key == null) { 590 return; 591 } 592 final KeyboardActionListener listener = mKeyboardActionListener; 593 if (key.hasNoPanelAutoMoreKey()) { 594 final int moreKeyCode = key.getMoreKeys()[0].mCode; 595 tracker.onLongPressed(); 596 listener.onPressKey(moreKeyCode, 0 /* repeatCount */, true /* isSinglePointer */); 597 listener.onCodeInput(moreKeyCode, Constants.NOT_A_COORDINATE, 598 Constants.NOT_A_COORDINATE, false /* isKeyRepeat */); 599 listener.onReleaseKey(moreKeyCode, false /* withSliding */); 600 return; 601 } 602 final int code = key.getCode(); 603 if (code == Constants.CODE_SPACE || code == Constants.CODE_LANGUAGE_SWITCH) { 604 // Long pressing the space key invokes IME switcher dialog. 605 if (listener.onCustomRequest(Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER)) { 606 tracker.onLongPressed(); 607 listener.onReleaseKey(code, false /* withSliding */); 608 return; 609 } 610 } 611 openMoreKeysPanel(key, tracker); 612 } 613 614 private void openMoreKeysPanel(final Key key, final PointerTracker tracker) { 615 final MoreKeysPanel moreKeysPanel = onCreateMoreKeysPanel(key, getContext()); 616 if (moreKeysPanel == null) { 617 return; 618 } 619 620 final int[] lastCoords = CoordinateUtils.newInstance(); 621 tracker.getLastCoordinates(lastCoords); 622 final boolean keyPreviewEnabled = isKeyPreviewPopupEnabled() && !key.noKeyPreview(); 623 // The more keys keyboard is usually horizontally aligned with the center of the parent key. 624 // If showMoreKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more 625 // keys keyboard is placed at the touch point of the parent key. 626 final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled) 627 ? CoordinateUtils.x(lastCoords) 628 : key.getX() + key.getWidth() / 2; 629 // The more keys keyboard is usually vertically aligned with the top edge of the parent key 630 // (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically 631 // aligned with the bottom edge of the visible part of the key preview. 632 // {@code mPreviewVisibleOffset} has been set appropriately in 633 // {@link KeyboardView#showKeyPreview(PointerTracker)}. 634 final int pointY = key.getY() + mKeyPreviewDrawParams.getVisibleOffset(); 635 moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener); 636 tracker.onShowMoreKeysPanel(moreKeysPanel); 637 // TODO: Implement zoom in animation of more keys panel. 638 dismissKeyPreviewWithoutDelay(key); 639 } 640 641 public boolean isInDraggingFinger() { 642 if (isShowingMoreKeysPanel()) { 643 return true; 644 } 645 return PointerTracker.isAnyInDraggingFinger(); 646 } 647 648 @Override 649 public void onShowMoreKeysPanel(final MoreKeysPanel panel) { 650 locatePreviewPlacerView(); 651 panel.showInParent(mDrawingPreviewPlacerView); 652 mMoreKeysPanel = panel; 653 dimEntireKeyboard(true /* dimmed */); 654 } 655 656 public boolean isShowingMoreKeysPanel() { 657 return mMoreKeysPanel != null && mMoreKeysPanel.isShowingInParent(); 658 } 659 660 @Override 661 public void onCancelMoreKeysPanel() { 662 PointerTracker.dismissAllMoreKeysPanels(); 663 } 664 665 @Override 666 public void onDismissMoreKeysPanel() { 667 dimEntireKeyboard(false /* dimmed */); 668 if (isShowingMoreKeysPanel()) { 669 mMoreKeysPanel.removeFromParent(); 670 mMoreKeysPanel = null; 671 } 672 } 673 674 public void startDoubleTapShiftKeyTimer() { 675 mKeyTimerHandler.startDoubleTapShiftKeyTimer(); 676 } 677 678 public void cancelDoubleTapShiftKeyTimer() { 679 mKeyTimerHandler.cancelDoubleTapShiftKeyTimer(); 680 } 681 682 public boolean isInDoubleTapShiftKeyTimeout() { 683 return mKeyTimerHandler.isInDoubleTapShiftKeyTimeout(); 684 } 685 686 @Override 687 public boolean onTouchEvent(final MotionEvent me) { 688 if (getKeyboard() == null) { 689 return false; 690 } 691 if (mNonDistinctMultitouchHelper != null) { 692 if (me.getPointerCount() > 1 && mKeyTimerHandler.isInKeyRepeat()) { 693 // Key repeating timer will be canceled if 2 or more keys are in action. 694 mKeyTimerHandler.cancelKeyRepeatTimers(); 695 } 696 // Non distinct multitouch screen support 697 mNonDistinctMultitouchHelper.processMotionEvent(me, mKeyDetector); 698 return true; 699 } 700 return processMotionEvent(me); 701 } 702 703 public boolean processMotionEvent(final MotionEvent me) { 704 final int index = me.getActionIndex(); 705 final int id = me.getPointerId(index); 706 final PointerTracker tracker = PointerTracker.getPointerTracker(id); 707 // When a more keys panel is showing, we should ignore other fingers' single touch events 708 // other than the finger that is showing the more keys panel. 709 if (isShowingMoreKeysPanel() && !tracker.isShowingMoreKeysPanel() 710 && PointerTracker.getActivePointerTrackerCount() == 1) { 711 return true; 712 } 713 tracker.processMotionEvent(me, mKeyDetector); 714 return true; 715 } 716 717 public void cancelAllOngoingEvents() { 718 mKeyTimerHandler.cancelAllMessages(); 719 mDrawingHandler.cancelAllMessages(); 720 dismissAllKeyPreviews(); 721 dismissGestureFloatingPreviewText(); 722 dismissSlidingKeyInputPreview(); 723 PointerTracker.dismissAllMoreKeysPanels(); 724 PointerTracker.cancelAllPointerTrackers(); 725 } 726 727 public void closing() { 728 cancelAllOngoingEvents(); 729 mMoreKeysKeyboardCache.clear(); 730 } 731 732 public void onHideWindow() { 733 final MainKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; 734 if (accessibilityDelegate != null) { 735 accessibilityDelegate.onHideWindow(); 736 } 737 } 738 739 /** 740 * {@inheritDoc} 741 */ 742 @Override 743 public boolean onHoverEvent(final MotionEvent event) { 744 final MainKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; 745 if (accessibilityDelegate != null) { 746 return accessibilityDelegate.onHoverEvent(event); 747 } 748 return super.onHoverEvent(event); 749 } 750 751 public void updateShortcutKey(final boolean available) { 752 final Keyboard keyboard = getKeyboard(); 753 if (keyboard == null) { 754 return; 755 } 756 final Key shortcutKey = keyboard.getKey(Constants.CODE_SHORTCUT); 757 if (shortcutKey == null) { 758 return; 759 } 760 shortcutKey.setEnabled(available); 761 invalidateKey(shortcutKey); 762 } 763 764 public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged, 765 final int languageOnSpacebarFormatType, 766 final boolean hasMultipleEnabledIMEsOrSubtypes) { 767 mLanguageOnSpacebarFormatType = languageOnSpacebarFormatType; 768 mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes; 769 final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator; 770 if (animator == null) { 771 mLanguageOnSpacebarFormatType = LanguageOnSpacebarHelper.FORMAT_TYPE_NONE; 772 } else { 773 if (subtypeChanged 774 && languageOnSpacebarFormatType != LanguageOnSpacebarHelper.FORMAT_TYPE_NONE) { 775 setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE); 776 if (animator.isStarted()) { 777 animator.cancel(); 778 } 779 animator.start(); 780 } else { 781 if (!animator.isStarted()) { 782 mLanguageOnSpacebarAnimAlpha = mLanguageOnSpacebarFinalAlpha; 783 } 784 } 785 } 786 invalidateKey(mSpaceKey); 787 } 788 789 private void dimEntireKeyboard(final boolean dimmed) { 790 final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed; 791 mNeedsToDimEntireKeyboard = dimmed; 792 if (needsRedrawing) { 793 invalidateAllKeys(); 794 } 795 } 796 797 @Override 798 protected void onDraw(final Canvas canvas) { 799 super.onDraw(canvas); 800 801 // Overlay a dark rectangle to dim. 802 if (mNeedsToDimEntireKeyboard) { 803 canvas.drawRect(0.0f, 0.0f, getWidth(), getHeight(), mBackgroundDimAlphaPaint); 804 } 805 } 806 807 @Override 808 protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint, 809 final KeyDrawParams params) { 810 if (key.altCodeWhileTyping() && key.isEnabled()) { 811 params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha; 812 } 813 super.onDrawKeyTopVisuals(key, canvas, paint, params); 814 final int code = key.getCode(); 815 if (code == Constants.CODE_SPACE) { 816 // If input language are explicitly selected. 817 if (mLanguageOnSpacebarFormatType != LanguageOnSpacebarHelper.FORMAT_TYPE_NONE) { 818 drawLanguageOnSpacebar(key, canvas, paint); 819 } 820 // Whether space key needs to show the "..." popup hint for special purposes 821 if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) { 822 drawKeyPopupHint(key, canvas, paint, params); 823 } 824 } else if (code == Constants.CODE_LANGUAGE_SWITCH) { 825 drawKeyPopupHint(key, canvas, paint, params); 826 } 827 } 828 829 private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) { 830 final int maxTextWidth = width - mLanguageOnSpacebarHorizontalMargin * 2; 831 paint.setTextScaleX(1.0f); 832 final float textWidth = TypefaceUtils.getStringWidth(text, paint); 833 if (textWidth < width) { 834 return true; 835 } 836 837 final float scaleX = maxTextWidth / textWidth; 838 if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) { 839 return false; 840 } 841 842 paint.setTextScaleX(scaleX); 843 return TypefaceUtils.getStringWidth(text, paint) < maxTextWidth; 844 } 845 846 // Layout language name on spacebar. 847 private String layoutLanguageOnSpacebar(final Paint paint, 848 final InputMethodSubtype subtype, final int width) { 849 // Choose appropriate language name to fit into the width. 850 if (mLanguageOnSpacebarFormatType == LanguageOnSpacebarHelper.FORMAT_TYPE_FULL_LOCALE) { 851 final String fullText = SpacebarLanguageUtils.getFullDisplayName(subtype); 852 if (fitsTextIntoWidth(width, fullText, paint)) { 853 return fullText; 854 } 855 } 856 857 final String middleText = SpacebarLanguageUtils.getMiddleDisplayName(subtype); 858 if (fitsTextIntoWidth(width, middleText, paint)) { 859 return middleText; 860 } 861 862 return ""; 863 } 864 865 private void drawLanguageOnSpacebar(final Key key, final Canvas canvas, final Paint paint) { 866 final int width = key.getWidth(); 867 final int height = key.getHeight(); 868 paint.setTextAlign(Align.CENTER); 869 paint.setTypeface(Typeface.DEFAULT); 870 paint.setTextSize(mLanguageOnSpacebarTextSize); 871 final InputMethodSubtype subtype = getKeyboard().mId.mSubtype; 872 final String language = layoutLanguageOnSpacebar(paint, subtype, width); 873 // Draw language text with shadow 874 final float descent = paint.descent(); 875 final float textHeight = -paint.ascent() + descent; 876 final float baseline = height / 2 + textHeight / 2; 877 if (mLanguageOnSpacebarTextShadowRadius > 0.0f) { 878 paint.setShadowLayer(mLanguageOnSpacebarTextShadowRadius, 0, 0, 879 mLanguageOnSpacebarTextShadowColor); 880 } else { 881 paint.clearShadowLayer(); 882 } 883 paint.setColor(mLanguageOnSpacebarTextColor); 884 paint.setAlpha(mLanguageOnSpacebarAnimAlpha); 885 canvas.drawText(language, width / 2, baseline - descent, paint); 886 paint.clearShadowLayer(); 887 paint.setTextScaleX(1.0f); 888 } 889 890 @Override 891 public void deallocateMemory() { 892 super.deallocateMemory(); 893 mDrawingPreviewPlacerView.deallocateMemory(); 894 } 895} 896