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