MainKeyboardView.java revision 8c6052bce1447076dcc61ea7d44df5d67bdf5a61
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.Animator; 20import android.animation.AnimatorInflater; 21import android.animation.AnimatorListenerAdapter; 22import android.animation.AnimatorSet; 23import android.animation.ObjectAnimator; 24import android.content.Context; 25import android.content.SharedPreferences; 26import android.content.pm.PackageManager; 27import android.content.res.Resources; 28import android.content.res.TypedArray; 29import android.graphics.Canvas; 30import android.graphics.Color; 31import android.graphics.Paint; 32import android.graphics.Paint.Align; 33import android.graphics.Typeface; 34import android.graphics.drawable.Drawable; 35import android.preference.PreferenceManager; 36import android.util.AttributeSet; 37import android.util.DisplayMetrics; 38import android.util.Log; 39import android.util.TypedValue; 40import android.view.LayoutInflater; 41import android.view.MotionEvent; 42import android.view.View; 43import android.view.ViewGroup; 44import android.view.animation.AccelerateInterpolator; 45import android.view.animation.DecelerateInterpolator; 46import android.view.inputmethod.InputMethodSubtype; 47import android.widget.TextView; 48 49import com.android.inputmethod.accessibility.AccessibilityUtils; 50import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; 51import com.android.inputmethod.annotations.ExternallyReferenced; 52import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; 53import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; 54import com.android.inputmethod.keyboard.internal.DrawingHandler; 55import com.android.inputmethod.keyboard.internal.GestureFloatingPreviewText; 56import com.android.inputmethod.keyboard.internal.GestureTrailsPreview; 57import com.android.inputmethod.keyboard.internal.KeyDrawParams; 58import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams; 59import com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper; 60import com.android.inputmethod.keyboard.internal.PreviewPlacerView; 61import com.android.inputmethod.keyboard.internal.SlidingKeyInputPreview; 62import com.android.inputmethod.keyboard.internal.TimerHandler; 63import com.android.inputmethod.latin.Constants; 64import com.android.inputmethod.latin.LatinImeLogger; 65import com.android.inputmethod.latin.R; 66import com.android.inputmethod.latin.SuggestedWords; 67import com.android.inputmethod.latin.define.ProductionFlag; 68import com.android.inputmethod.latin.settings.DebugSettings; 69import com.android.inputmethod.latin.utils.CollectionUtils; 70import com.android.inputmethod.latin.utils.CoordinateUtils; 71import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; 72import com.android.inputmethod.latin.utils.TypefaceUtils; 73import com.android.inputmethod.latin.utils.UsabilityStudyLogUtils; 74import com.android.inputmethod.latin.utils.ViewLayoutUtils; 75import com.android.inputmethod.research.ResearchLogger; 76 77import java.util.ArrayDeque; 78import java.util.HashMap; 79import java.util.HashSet; 80import java.util.WeakHashMap; 81 82/** 83 * A view that is responsible for detecting key presses and touch movements. 84 * 85 * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedEnabled 86 * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedIcon 87 * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextRatio 88 * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextColor 89 * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextShadowColor 90 * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha 91 * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator 92 * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator 93 * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator 94 * @attr ref R.styleable#MainKeyboardView_keyHysteresisDistance 95 * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdTime 96 * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdDistance 97 * @attr ref R.styleable#MainKeyboardView_keySelectionByDraggingFinger 98 * @attr ref R.styleable#MainKeyboardView_keyRepeatStartTimeout 99 * @attr ref R.styleable#MainKeyboardView_keyRepeatInterval 100 * @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout 101 * @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout 102 * @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout 103 * @attr ref R.styleable#MainKeyboardView_keyPreviewLayout 104 * @attr ref R.styleable#MainKeyboardView_keyPreviewOffset 105 * @attr ref R.styleable#MainKeyboardView_keyPreviewHeight 106 * @attr ref R.styleable#MainKeyboardView_keyPreviewLingerTimeout 107 * @attr ref R.styleable#MainKeyboardView_moreKeysKeyboardLayout 108 * @attr ref R.styleable#MainKeyboardView_backgroundDimAlpha 109 * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint 110 * @attr ref R.styleable#MainKeyboardView_gestureFloatingPreviewTextLingerTimeout 111 * @attr ref R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping 112 * @attr ref R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold 113 * @attr ref R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration 114 * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom 115 * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo 116 * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom 117 * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo 118 * @attr ref R.styleable#MainKeyboardView_gestureSamplingMinimumDistance 119 * @attr ref R.styleable#MainKeyboardView_gestureRecognitionMinimumTime 120 * @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold 121 * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration 122 */ 123public final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler, 124 PointerTracker.DrawingProxy, MoreKeysPanel.Controller, DrawingHandler.Callbacks, 125 TimerHandler.Callbacks { 126 private static final String TAG = MainKeyboardView.class.getSimpleName(); 127 128 /** Listener for {@link KeyboardActionListener}. */ 129 private KeyboardActionListener mKeyboardActionListener; 130 131 /* Space key and its icons */ 132 private Key mSpaceKey; 133 private Drawable mSpaceIcon; 134 // Stuff to draw language name on spacebar. 135 private final int mLanguageOnSpacebarFinalAlpha; 136 private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator; 137 private boolean mNeedsToDisplayLanguage; 138 private boolean mHasMultipleEnabledIMEsOrSubtypes; 139 private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE; 140 private final float mLanguageOnSpacebarTextRatio; 141 private float mLanguageOnSpacebarTextSize; 142 private final int mLanguageOnSpacebarTextColor; 143 private final int mLanguageOnSpacebarTextShadowColor; 144 // The minimum x-scale to fit the language name on spacebar. 145 private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f; 146 // Stuff to draw auto correction LED on spacebar. 147 private boolean mAutoCorrectionSpacebarLedOn; 148 private final boolean mAutoCorrectionSpacebarLedEnabled; 149 private final Drawable mAutoCorrectionSpacebarLedIcon; 150 private static final int SPACE_LED_LENGTH_PERCENT = 80; 151 152 // Stuff to draw altCodeWhileTyping keys. 153 private final ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator; 154 private final ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator; 155 private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE; 156 157 // Preview placer view 158 private final PreviewPlacerView mPreviewPlacerView; 159 private final int[] mOriginCoords = CoordinateUtils.newInstance(); 160 private final GestureFloatingPreviewText mGestureFloatingPreviewText; 161 private final GestureTrailsPreview mGestureTrailsPreview; 162 private final SlidingKeyInputPreview mSlidingKeyInputPreview; 163 164 // Key preview 165 private static final boolean FADE_OUT_KEY_TOP_LETTER_WHEN_KEY_IS_PRESSED = false; 166 private final int mKeyPreviewLayoutId; 167 private final int mKeyPreviewOffset; 168 private final int mKeyPreviewHeight; 169 // Free {@link TextView} pool that can be used for key preview. 170 private final ArrayDeque<TextView> mFreeKeyPreviewTextViews = CollectionUtils.newArrayDeque(); 171 // Map from {@link Key} to {@link TextView} that is currently being displayed as key preview. 172 private final HashMap<Key,TextView> mShowingKeyPreviewTextViews = CollectionUtils.newHashMap(); 173 private final KeyPreviewDrawParams mKeyPreviewDrawParams = new KeyPreviewDrawParams(); 174 private boolean mShowKeyPreviewPopup = true; 175 private int mKeyPreviewLingerTimeout; 176 private int mKeyPreviewZoomInDuration; 177 private int mKeyPreviewZoomOutDuration; 178 private static final float KEY_PREVIEW_START_ZOOM_IN_SCALE = 0.7f; 179 private static final float KEY_PREVIEW_END_ZOOM_IN_SCALE = 1.0f; 180 private static final float KEY_PREVIEW_END_ZOOM_OUT_SCALE = 0.7f; 181 private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR = 182 new AccelerateInterpolator(); 183 private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = 184 new DecelerateInterpolator(); 185 186 // More keys keyboard 187 private final Paint mBackgroundDimAlphaPaint = new Paint(); 188 private boolean mNeedsToDimEntireKeyboard; 189 private final View mMoreKeysKeyboardContainer; 190 private final WeakHashMap<Key, Keyboard> mMoreKeysKeyboardCache = 191 CollectionUtils.newWeakHashMap(); 192 private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint; 193 // More keys panel (used by both more keys keyboard and more suggestions view) 194 // TODO: Consider extending to support multiple more keys panels 195 private MoreKeysPanel mMoreKeysPanel; 196 197 // Gesture floating preview text 198 // TODO: Make this parameter customizable by user via settings. 199 private int mGestureFloatingPreviewTextLingerTimeout; 200 201 private final KeyDetector mKeyDetector; 202 private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper; 203 204 private final TimerHandler mKeyTimerHandler; 205 private final int mLanguageOnSpacebarHorizontalMargin; 206 207 private final DrawingHandler mDrawingHandler = 208 new DrawingHandler(this); 209 210 public MainKeyboardView(final Context context, final AttributeSet attrs) { 211 this(context, attrs, R.attr.mainKeyboardViewStyle); 212 } 213 214 public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) { 215 super(context, attrs, defStyle); 216 217 final Resources res = getResources(); 218 PointerTracker.init(res); 219 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 220 final boolean forceNonDistinctMultitouch = prefs.getBoolean( 221 DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, false); 222 final boolean hasDistinctMultitouch = context.getPackageManager() 223 .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT) 224 && !forceNonDistinctMultitouch; 225 mNonDistinctMultitouchHelper = hasDistinctMultitouch ? null 226 : new NonDistinctMultitouchHelper(); 227 228 mPreviewPlacerView = new PreviewPlacerView(context, attrs); 229 230 final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes( 231 attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView); 232 final int backgroundDimAlpha = mainKeyboardViewAttr.getInt( 233 R.styleable.MainKeyboardView_backgroundDimAlpha, 0); 234 mBackgroundDimAlphaPaint.setColor(Color.BLACK); 235 mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha); 236 mAutoCorrectionSpacebarLedEnabled = mainKeyboardViewAttr.getBoolean( 237 R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false); 238 mAutoCorrectionSpacebarLedIcon = mainKeyboardViewAttr.getDrawable( 239 R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon); 240 mLanguageOnSpacebarTextRatio = mainKeyboardViewAttr.getFraction( 241 R.styleable.MainKeyboardView_languageOnSpacebarTextRatio, 1, 1, 1.0f); 242 mLanguageOnSpacebarTextColor = mainKeyboardViewAttr.getColor( 243 R.styleable.MainKeyboardView_languageOnSpacebarTextColor, 0); 244 mLanguageOnSpacebarTextShadowColor = mainKeyboardViewAttr.getColor( 245 R.styleable.MainKeyboardView_languageOnSpacebarTextShadowColor, 0); 246 mLanguageOnSpacebarFinalAlpha = mainKeyboardViewAttr.getInt( 247 R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha, 248 Constants.Color.ALPHA_OPAQUE); 249 final int languageOnSpacebarFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId( 250 R.styleable.MainKeyboardView_languageOnSpacebarFadeoutAnimator, 0); 251 final int altCodeKeyWhileTypingFadeoutAnimatorResId = mainKeyboardViewAttr.getResourceId( 252 R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0); 253 final int altCodeKeyWhileTypingFadeinAnimatorResId = mainKeyboardViewAttr.getResourceId( 254 R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0); 255 256 final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension( 257 R.styleable.MainKeyboardView_keyHysteresisDistance, 0.0f); 258 final float keyHysteresisDistanceForSlidingModifier = mainKeyboardViewAttr.getDimension( 259 R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0.0f); 260 mKeyDetector = new KeyDetector( 261 keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier); 262 final int ignoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt( 263 R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0); 264 final int gestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt( 265 R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0); 266 mKeyTimerHandler = new TimerHandler( 267 this, ignoreAltCodeKeyTimeout, gestureRecognitionUpdateTime); 268 mKeyPreviewOffset = mainKeyboardViewAttr.getDimensionPixelOffset( 269 R.styleable.MainKeyboardView_keyPreviewOffset, 0); 270 mKeyPreviewHeight = mainKeyboardViewAttr.getDimensionPixelSize( 271 R.styleable.MainKeyboardView_keyPreviewHeight, 0); 272 mKeyPreviewLingerTimeout = mainKeyboardViewAttr.getInt( 273 R.styleable.MainKeyboardView_keyPreviewLingerTimeout, 0); 274 mKeyPreviewLayoutId = mainKeyboardViewAttr.getResourceId( 275 R.styleable.MainKeyboardView_keyPreviewLayout, 0); 276 if (mKeyPreviewLayoutId == 0) { 277 mShowKeyPreviewPopup = false; 278 } 279 mKeyPreviewZoomInDuration = mainKeyboardViewAttr.getInt( 280 R.styleable.MainKeyboardView_keyPreviewZoomInDuration, 0); 281 mKeyPreviewZoomOutDuration = mainKeyboardViewAttr.getInt( 282 R.styleable.MainKeyboardView_keyPreviewZoomOutDuration, 0); 283 final int moreKeysKeyboardLayoutId = mainKeyboardViewAttr.getResourceId( 284 R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0); 285 mConfigShowMoreKeysKeyboardAtTouchedPoint = mainKeyboardViewAttr.getBoolean( 286 R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false); 287 288 mGestureFloatingPreviewTextLingerTimeout = mainKeyboardViewAttr.getInt( 289 R.styleable.MainKeyboardView_gestureFloatingPreviewTextLingerTimeout, 0); 290 PointerTracker.setParameters(mainKeyboardViewAttr); 291 292 mGestureFloatingPreviewText = new GestureFloatingPreviewText( 293 mPreviewPlacerView, mainKeyboardViewAttr); 294 mPreviewPlacerView.addPreview(mGestureFloatingPreviewText); 295 296 mGestureTrailsPreview = new GestureTrailsPreview( 297 mPreviewPlacerView, mainKeyboardViewAttr); 298 mPreviewPlacerView.addPreview(mGestureTrailsPreview); 299 300 mSlidingKeyInputPreview = new SlidingKeyInputPreview( 301 mPreviewPlacerView, mainKeyboardViewAttr); 302 mPreviewPlacerView.addPreview(mSlidingKeyInputPreview); 303 mainKeyboardViewAttr.recycle(); 304 305 mMoreKeysKeyboardContainer = LayoutInflater.from(getContext()) 306 .inflate(moreKeysKeyboardLayoutId, null); 307 mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator( 308 languageOnSpacebarFadeoutAnimatorResId, this); 309 mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator( 310 altCodeKeyWhileTypingFadeoutAnimatorResId, this); 311 mAltCodeKeyWhileTypingFadeinAnimator = loadObjectAnimator( 312 altCodeKeyWhileTypingFadeinAnimatorResId, this); 313 314 mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; 315 316 mLanguageOnSpacebarHorizontalMargin = (int)res.getDimension( 317 R.dimen.config_language_on_spacebar_horizontal_margin); 318 } 319 320 @Override 321 public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) { 322 super.setHardwareAcceleratedDrawingEnabled(enabled); 323 mPreviewPlacerView.setHardwareAcceleratedDrawingEnabled(enabled); 324 } 325 326 private ObjectAnimator loadObjectAnimator(final int resId, final Object target) { 327 if (resId == 0) { 328 // TODO: Stop returning null. 329 return null; 330 } 331 final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator( 332 getContext(), resId); 333 if (animator != null) { 334 animator.setTarget(target); 335 } 336 return animator; 337 } 338 339 private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel, 340 final ObjectAnimator animatorToStart) { 341 if (animatorToCancel == null || animatorToStart == null) { 342 // TODO: Stop using null as a no-operation animator. 343 return; 344 } 345 float startFraction = 0.0f; 346 if (animatorToCancel.isStarted()) { 347 animatorToCancel.cancel(); 348 startFraction = 1.0f - animatorToCancel.getAnimatedFraction(); 349 } 350 final long startTime = (long)(animatorToStart.getDuration() * startFraction); 351 animatorToStart.start(); 352 animatorToStart.setCurrentPlayTime(startTime); 353 } 354 355 // Implements {@link TimerHander.Callbacks} method. 356 @Override 357 public void startWhileTypingFadeinAnimation() { 358 cancelAndStartAnimators( 359 mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator); 360 } 361 362 @Override 363 public void startWhileTypingFadeoutAnimation() { 364 cancelAndStartAnimators( 365 mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator); 366 } 367 368 @ExternallyReferenced 369 public int getLanguageOnSpacebarAnimAlpha() { 370 return mLanguageOnSpacebarAnimAlpha; 371 } 372 373 @ExternallyReferenced 374 public void setLanguageOnSpacebarAnimAlpha(final int alpha) { 375 mLanguageOnSpacebarAnimAlpha = alpha; 376 invalidateKey(mSpaceKey); 377 } 378 379 @ExternallyReferenced 380 public int getAltCodeKeyWhileTypingAnimAlpha() { 381 return mAltCodeKeyWhileTypingAnimAlpha; 382 } 383 384 @ExternallyReferenced 385 public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) { 386 if (mAltCodeKeyWhileTypingAnimAlpha == alpha) { 387 return; 388 } 389 // Update the visual of alt-code-key-while-typing. 390 mAltCodeKeyWhileTypingAnimAlpha = alpha; 391 final Keyboard keyboard = getKeyboard(); 392 if (keyboard == null) { 393 return; 394 } 395 for (final Key key : keyboard.mAltCodeKeysWhileTyping) { 396 invalidateKey(key); 397 } 398 } 399 400 public void setKeyboardActionListener(final KeyboardActionListener listener) { 401 mKeyboardActionListener = listener; 402 PointerTracker.setKeyboardActionListener(listener); 403 } 404 405 /** 406 * Returns the {@link KeyboardActionListener} object. 407 * @return the listener attached to this keyboard 408 */ 409 @Override 410 public KeyboardActionListener getKeyboardActionListener() { 411 return mKeyboardActionListener; 412 } 413 414 @Override 415 public KeyDetector getKeyDetector() { 416 return mKeyDetector; 417 } 418 419 @Override 420 public DrawingProxy getDrawingProxy() { 421 return this; 422 } 423 424 @Override 425 public TimerProxy getTimerProxy() { 426 return mKeyTimerHandler; 427 } 428 429 /** 430 * Attaches a keyboard to this view. The keyboard can be switched at any time and the 431 * view will re-layout itself to accommodate the keyboard. 432 * @see Keyboard 433 * @see #getKeyboard() 434 * @param keyboard the keyboard to display in this view 435 */ 436 @Override 437 public void setKeyboard(final Keyboard keyboard) { 438 // Remove any pending messages, except dismissing preview and key repeat. 439 mKeyTimerHandler.cancelLongPressTimer(); 440 super.setKeyboard(keyboard); 441 mKeyDetector.setKeyboard( 442 keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection()); 443 PointerTracker.setKeyDetector(mKeyDetector); 444 mMoreKeysKeyboardCache.clear(); 445 446 mSpaceKey = keyboard.getKey(Constants.CODE_SPACE); 447 mSpaceIcon = (mSpaceKey != null) 448 ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null; 449 final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; 450 mLanguageOnSpacebarTextSize = keyHeight * mLanguageOnSpacebarTextRatio; 451 if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { 452 final int orientation = getContext().getResources().getConfiguration().orientation; 453 ResearchLogger.mainKeyboardView_setKeyboard(keyboard, orientation); 454 } 455 456 // This always needs to be set since the accessibility state can 457 // potentially change without the keyboard being set again. 458 AccessibleKeyboardViewProxy.getInstance().setKeyboard(); 459 } 460 461 /** 462 * Enables or disables the key feedback popup. This is a popup that shows a magnified 463 * version of the depressed key. By default the preview is enabled. 464 * @param previewEnabled whether or not to enable the key feedback preview 465 * @param delay the delay after which the preview is dismissed 466 * @see #isKeyPreviewPopupEnabled() 467 */ 468 public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) { 469 mShowKeyPreviewPopup = previewEnabled; 470 mKeyPreviewLingerTimeout = delay; 471 } 472 473 private void locatePreviewPlacerView() { 474 if (mPreviewPlacerView.getParent() != null) { 475 return; 476 } 477 final int width = getWidth(); 478 final int height = getHeight(); 479 if (width == 0 || height == 0) { 480 // In transient state. 481 return; 482 } 483 getLocationInWindow(mOriginCoords); 484 final DisplayMetrics dm = getResources().getDisplayMetrics(); 485 if (CoordinateUtils.y(mOriginCoords) < dm.heightPixels / 4) { 486 // In transient state. 487 return; 488 } 489 final View rootView = getRootView(); 490 if (rootView == null) { 491 Log.w(TAG, "Cannot find root view"); 492 return; 493 } 494 final ViewGroup windowContentView = (ViewGroup)rootView.findViewById(android.R.id.content); 495 // Note: It'd be very weird if we get null by android.R.id.content. 496 if (windowContentView == null) { 497 Log.w(TAG, "Cannot find android.R.id.content view to add PreviewPlacerView"); 498 } else { 499 windowContentView.addView(mPreviewPlacerView); 500 mPreviewPlacerView.setKeyboardViewGeometry(mOriginCoords, width, height); 501 } 502 } 503 504 /** 505 * Returns the enabled state of the key feedback preview 506 * @return whether or not the key feedback preview is enabled 507 * @see #setKeyPreviewPopupEnabled(boolean, int) 508 */ 509 public boolean isKeyPreviewPopupEnabled() { 510 return mShowKeyPreviewPopup; 511 } 512 513 private TextView getKeyPreviewTextView(final Key key) { 514 TextView previewTextView = mShowingKeyPreviewTextViews.remove(key); 515 if (previewTextView != null) { 516 return previewTextView; 517 } 518 previewTextView = mFreeKeyPreviewTextViews.poll(); 519 if (previewTextView != null) { 520 return previewTextView; 521 } 522 final Context context = getContext(); 523 if (mKeyPreviewLayoutId != 0) { 524 previewTextView = (TextView)LayoutInflater.from(context) 525 .inflate(mKeyPreviewLayoutId, null); 526 } else { 527 previewTextView = new TextView(context); 528 } 529 locatePreviewPlacerView(); 530 mPreviewPlacerView.addView( 531 previewTextView, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0)); 532 return previewTextView; 533 } 534 535 // Implements {@link DrawingHandler.Callbacks} method. 536 @Override 537 public void dismissAllKeyPreviews() { 538 for (final Key key : new HashSet<Key>(mShowingKeyPreviewTextViews.keySet())) { 539 dismissKeyPreviewWithoutDelay(key); 540 } 541 PointerTracker.setReleasedKeyGraphicsToAllKeys(); 542 } 543 544 // Background state set 545 private static final int[][][] KEY_PREVIEW_BACKGROUND_STATE_TABLE = { 546 { // STATE_MIDDLE 547 EMPTY_STATE_SET, 548 { R.attr.state_has_morekeys } 549 }, 550 { // STATE_LEFT 551 { R.attr.state_left_edge }, 552 { R.attr.state_left_edge, R.attr.state_has_morekeys } 553 }, 554 { // STATE_RIGHT 555 { R.attr.state_right_edge }, 556 { R.attr.state_right_edge, R.attr.state_has_morekeys } 557 } 558 }; 559 private static final int STATE_MIDDLE = 0; 560 private static final int STATE_LEFT = 1; 561 private static final int STATE_RIGHT = 2; 562 private static final int STATE_NORMAL = 0; 563 private static final int STATE_HAS_MOREKEYS = 1; 564 565 // TODO: Take this method out of this class. 566 @Override 567 public void showKeyPreview(final Key key) { 568 // If key is invalid or IME is already closed, we must not show key preview. 569 // Trying to show key preview while root window is closed causes 570 // WindowManager.BadTokenException. 571 if (key == null) { 572 return; 573 } 574 575 final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams; 576 final Keyboard keyboard = getKeyboard(); 577 if (!mShowKeyPreviewPopup) { 578 previewParams.mPreviewVisibleOffset = -keyboard.mVerticalGap; 579 return; 580 } 581 582 final TextView previewTextView = getKeyPreviewTextView(key); 583 final KeyDrawParams drawParams = mKeyDrawParams; 584 previewTextView.setTextColor(drawParams.mPreviewTextColor); 585 final Drawable background = previewTextView.getBackground(); 586 final String label = key.getPreviewLabel(); 587 // What we show as preview should match what we show on a key top in onDraw(). 588 if (label != null) { 589 // TODO Should take care of temporaryShiftLabel here. 590 previewTextView.setCompoundDrawables(null, null, null, null); 591 previewTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, 592 key.selectPreviewTextSize(drawParams)); 593 previewTextView.setTypeface(key.selectPreviewTypeface(drawParams)); 594 previewTextView.setText(label); 595 } else { 596 previewTextView.setCompoundDrawables(null, null, null, 597 key.getPreviewIcon(keyboard.mIconsSet)); 598 previewTextView.setText(null); 599 } 600 601 previewTextView.measure( 602 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 603 final int keyDrawWidth = key.getDrawWidth(); 604 final int previewWidth = previewTextView.getMeasuredWidth(); 605 final int previewHeight = mKeyPreviewHeight; 606 // The width and height of visible part of the key preview background. The content marker 607 // of the background 9-patch have to cover the visible part of the background. 608 previewParams.mPreviewVisibleWidth = previewWidth - previewTextView.getPaddingLeft() 609 - previewTextView.getPaddingRight(); 610 previewParams.mPreviewVisibleHeight = previewHeight - previewTextView.getPaddingTop() 611 - previewTextView.getPaddingBottom(); 612 // The distance between the top edge of the parent key and the bottom of the visible part 613 // of the key preview background. 614 previewParams.mPreviewVisibleOffset = 615 mKeyPreviewOffset - previewTextView.getPaddingBottom(); 616 getLocationInWindow(mOriginCoords); 617 // The key preview is horizontally aligned with the center of the visible part of the 618 // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and 619 // the left/right background is used if such background is specified. 620 final int statePosition; 621 int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2 622 + CoordinateUtils.x(mOriginCoords); 623 if (previewX < 0) { 624 previewX = 0; 625 statePosition = STATE_LEFT; 626 } else if (previewX > getWidth() - previewWidth) { 627 previewX = getWidth() - previewWidth; 628 statePosition = STATE_RIGHT; 629 } else { 630 statePosition = STATE_MIDDLE; 631 } 632 // The key preview is placed vertically above the top edge of the parent key with an 633 // arbitrary offset. 634 final int previewY = key.getY() - previewHeight + mKeyPreviewOffset 635 + CoordinateUtils.y(mOriginCoords); 636 637 if (background != null) { 638 final int hasMoreKeys = (key.getMoreKeys() != null) ? STATE_HAS_MOREKEYS : STATE_NORMAL; 639 background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[statePosition][hasMoreKeys]); 640 } 641 ViewLayoutUtils.placeViewAt( 642 previewTextView, previewX, previewY, previewWidth, previewHeight); 643 644 if (!isHardwareAccelerated()) { 645 previewTextView.setVisibility(VISIBLE); 646 mShowingKeyPreviewTextViews.put(key, previewTextView); 647 return; 648 } 649 previewTextView.setPivotX(previewWidth / 2.0f); 650 previewTextView.setPivotY(previewHeight); 651 652 final Animator zoomIn = createZoomInAniation(key, previewTextView); 653 final Animator zoomOut = createZoomOutAnimation(key, previewTextView); 654 final KeyPreviewAnimations animation = new KeyPreviewAnimations(zoomIn, zoomOut); 655 previewTextView.setTag(animation); 656 animation.startZoomIn(); 657 } 658 659 // TODO: Move this internal class out to a separate external class. 660 private static class KeyPreviewAnimations extends AnimatorListenerAdapter { 661 private final Animator mZoomIn; 662 private final Animator mZoomOut; 663 664 public KeyPreviewAnimations(final Animator zoomIn, final Animator zoomOut) { 665 mZoomIn = zoomIn; 666 mZoomOut = zoomOut; 667 } 668 669 public void startZoomIn() { 670 mZoomIn.start(); 671 } 672 673 public void startZoomOut() { 674 if (mZoomIn.isRunning()) { 675 mZoomIn.addListener(this); 676 return; 677 } 678 mZoomOut.start(); 679 } 680 681 @Override 682 public void onAnimationEnd(final Animator animation) { 683 mZoomOut.start(); 684 } 685 } 686 687 // TODO: Take this method out of this class. 688 private Animator createZoomInAniation(final Key key, final TextView previewTextView) { 689 final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat( 690 previewTextView, SCALE_X, KEY_PREVIEW_START_ZOOM_IN_SCALE, 691 KEY_PREVIEW_END_ZOOM_IN_SCALE); 692 final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat( 693 previewTextView, SCALE_Y, KEY_PREVIEW_START_ZOOM_IN_SCALE, 694 KEY_PREVIEW_END_ZOOM_IN_SCALE); 695 final AnimatorSet zoomInAnimation = new AnimatorSet(); 696 zoomInAnimation.play(scaleXAnimation).with(scaleYAnimation); 697 // TODO: Implement preference option to control key preview animation duration. 698 zoomInAnimation.setDuration(mKeyPreviewZoomInDuration); 699 zoomInAnimation.setInterpolator(DECELERATE_INTERPOLATOR); 700 zoomInAnimation.addListener(new AnimatorListenerAdapter() { 701 @Override 702 public void onAnimationStart(final Animator animation) { 703 previewTextView.setVisibility(VISIBLE); 704 mShowingKeyPreviewTextViews.put(key, previewTextView); 705 } 706 }); 707 return zoomInAnimation; 708 } 709 710 // TODO: Take this method out of this class. 711 private Animator createZoomOutAnimation(final Key key, final TextView previewTextView) { 712 final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat( 713 previewTextView, SCALE_X, KEY_PREVIEW_END_ZOOM_OUT_SCALE); 714 final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat( 715 previewTextView, SCALE_Y, KEY_PREVIEW_END_ZOOM_OUT_SCALE); 716 final AnimatorSet zoomOutAnimation = new AnimatorSet(); 717 zoomOutAnimation.play(scaleXAnimation).with(scaleYAnimation); 718 // TODO: Implement preference option to control key preview animation duration. 719 zoomOutAnimation.setDuration(mKeyPreviewZoomOutDuration); 720 zoomOutAnimation.setInterpolator(ACCELERATE_INTERPOLATOR); 721 zoomOutAnimation.addListener(new AnimatorListenerAdapter() { 722 @Override 723 public void onAnimationEnd(final Animator animation) { 724 dismissKeyPreviewWithoutDelay(key); 725 } 726 }); 727 return zoomOutAnimation; 728 } 729 730 // Implements {@link TimerHandler.Callbacks} method. 731 // TODO: Take this method out of this class. 732 @Override 733 public void dismissKeyPreviewWithoutDelay(final Key key) { 734 if (key == null) { 735 return; 736 } 737 final TextView previewTextView = mShowingKeyPreviewTextViews.remove(key); 738 if (previewTextView != null) { 739 final Object tag = previewTextView.getTag(); 740 if (tag instanceof Animator) { 741 ((Animator)tag).cancel(); 742 } 743 previewTextView.setTag(null); 744 previewTextView.setVisibility(INVISIBLE); 745 mFreeKeyPreviewTextViews.add(previewTextView); 746 } 747 // To redraw key top letter. 748 invalidateKey(key); 749 } 750 751 // TODO: Take this method out of this class. 752 @Override 753 public void dismissKeyPreview(final Key key) { 754 final TextView previewTextView = mShowingKeyPreviewTextViews.get(key); 755 if (previewTextView == null) { 756 return; 757 } 758 if (!isHardwareAccelerated()) { 759 // TODO: Implement preference option to control key preview method and duration. 760 mDrawingHandler.dismissKeyPreview(mKeyPreviewLingerTimeout, key); 761 return; 762 } 763 final Object tag = previewTextView.getTag(); 764 if (tag instanceof KeyPreviewAnimations) { 765 final KeyPreviewAnimations animation = (KeyPreviewAnimations)tag; 766 animation.startZoomOut(); 767 } 768 } 769 770 public void setSlidingKeyInputPreviewEnabled(final boolean enabled) { 771 mSlidingKeyInputPreview.setPreviewEnabled(enabled); 772 } 773 774 @Override 775 public void showSlidingKeyInputPreview(final PointerTracker tracker) { 776 locatePreviewPlacerView(); 777 mSlidingKeyInputPreview.setPreviewPosition(tracker); 778 } 779 780 @Override 781 public void dismissSlidingKeyInputPreview() { 782 mSlidingKeyInputPreview.dismissSlidingKeyInputPreview(); 783 } 784 785 private void setGesturePreviewMode(final boolean isGestureTrailEnabled, 786 final boolean isGestureFloatingPreviewTextEnabled) { 787 mGestureFloatingPreviewText.setPreviewEnabled(isGestureFloatingPreviewTextEnabled); 788 mGestureTrailsPreview.setPreviewEnabled(isGestureTrailEnabled); 789 } 790 791 // Implements {@link DrawingHandler.Callbacks} method. 792 @Override 793 public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) { 794 locatePreviewPlacerView(); 795 mGestureFloatingPreviewText.setSuggetedWords(suggestedWords); 796 } 797 798 public void dismissGestureFloatingPreviewText() { 799 locatePreviewPlacerView(); 800 mDrawingHandler.dismissGestureFloatingPreviewText(mGestureFloatingPreviewTextLingerTimeout); 801 } 802 803 @Override 804 public void showGestureTrail(final PointerTracker tracker, 805 final boolean showsFloatingPreviewText) { 806 locatePreviewPlacerView(); 807 if (showsFloatingPreviewText) { 808 mGestureFloatingPreviewText.setPreviewPosition(tracker); 809 } 810 mGestureTrailsPreview.setPreviewPosition(tracker); 811 } 812 813 // Note that this method is called from a non-UI thread. 814 public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) { 815 PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable); 816 } 817 818 public void setGestureHandlingEnabledByUser(final boolean isGestureHandlingEnabledByUser, 819 final boolean isGestureTrailEnabled, 820 final boolean isGestureFloatingPreviewTextEnabled) { 821 PointerTracker.setGestureHandlingEnabledByUser(isGestureHandlingEnabledByUser); 822 setGesturePreviewMode(isGestureHandlingEnabledByUser && isGestureTrailEnabled, 823 isGestureHandlingEnabledByUser && isGestureFloatingPreviewTextEnabled); 824 } 825 826 @Override 827 protected void onAttachedToWindow() { 828 super.onAttachedToWindow(); 829 // Notify the ResearchLogger (development only diagnostics) that the keyboard view has 830 // been attached. This is needed to properly show the splash screen, which requires that 831 // the window token of the KeyboardView be non-null. 832 if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { 833 ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(this); 834 } 835 } 836 837 @Override 838 protected void onDetachedFromWindow() { 839 super.onDetachedFromWindow(); 840 mPreviewPlacerView.removeAllViews(); 841 // Notify the ResearchLogger (development only diagnostics) that the keyboard view has 842 // been detached. This is needed to invalidate the reference of {@link MainKeyboardView} 843 // to null. 844 if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { 845 ResearchLogger.getInstance().mainKeyboardView_onDetachedFromWindow(); 846 } 847 } 848 849 private MoreKeysPanel onCreateMoreKeysPanel(final Key key, final Context context) { 850 if (key.getMoreKeys() == null) { 851 return null; 852 } 853 Keyboard moreKeysKeyboard = mMoreKeysKeyboardCache.get(key); 854 if (moreKeysKeyboard == null) { 855 moreKeysKeyboard = new MoreKeysKeyboard.Builder( 856 context, key, this, mKeyPreviewDrawParams).build(); 857 mMoreKeysKeyboardCache.put(key, moreKeysKeyboard); 858 } 859 860 final View container = mMoreKeysKeyboardContainer; 861 final MoreKeysKeyboardView moreKeysKeyboardView = 862 (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view); 863 moreKeysKeyboardView.setKeyboard(moreKeysKeyboard); 864 container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 865 return moreKeysKeyboardView; 866 } 867 868 // Implements {@link TimerHandler.Callbacks} method. 869 /** 870 * Called when a key is long pressed. 871 * @param tracker the pointer tracker which pressed the parent key 872 */ 873 @Override 874 public void onLongPress(final PointerTracker tracker) { 875 if (isShowingMoreKeysPanel()) { 876 return; 877 } 878 final Key key = tracker.getKey(); 879 if (key == null) { 880 return; 881 } 882 if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { 883 ResearchLogger.mainKeyboardView_onLongPress(); 884 } 885 final KeyboardActionListener listener = mKeyboardActionListener; 886 if (key.hasNoPanelAutoMoreKey()) { 887 final int moreKeyCode = key.getMoreKeys()[0].mCode; 888 tracker.onLongPressed(); 889 listener.onPressKey(moreKeyCode, 0 /* repeatCount */, true /* isSinglePointer */); 890 listener.onCodeInput(moreKeyCode, 891 Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); 892 listener.onReleaseKey(moreKeyCode, false /* withSliding */); 893 return; 894 } 895 final int code = key.getCode(); 896 if (code == Constants.CODE_SPACE || code == Constants.CODE_LANGUAGE_SWITCH) { 897 // Long pressing the space key invokes IME switcher dialog. 898 if (listener.onCustomRequest(Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER)) { 899 tracker.onLongPressed(); 900 listener.onReleaseKey(code, false /* withSliding */); 901 return; 902 } 903 } 904 openMoreKeysPanel(key, tracker); 905 } 906 907 private void openMoreKeysPanel(final Key key, final PointerTracker tracker) { 908 final MoreKeysPanel moreKeysPanel = onCreateMoreKeysPanel(key, getContext()); 909 if (moreKeysPanel == null) { 910 return; 911 } 912 913 final int[] lastCoords = CoordinateUtils.newInstance(); 914 tracker.getLastCoordinates(lastCoords); 915 final boolean keyPreviewEnabled = isKeyPreviewPopupEnabled() && !key.noKeyPreview(); 916 // The more keys keyboard is usually horizontally aligned with the center of the parent key. 917 // If showMoreKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more 918 // keys keyboard is placed at the touch point of the parent key. 919 final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled) 920 ? CoordinateUtils.x(lastCoords) 921 : key.getX() + key.getWidth() / 2; 922 // The more keys keyboard is usually vertically aligned with the top edge of the parent key 923 // (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically 924 // aligned with the bottom edge of the visible part of the key preview. 925 // {@code mPreviewVisibleOffset} has been set appropriately in 926 // {@link KeyboardView#showKeyPreview(PointerTracker)}. 927 final int pointY = key.getY() + mKeyPreviewDrawParams.mPreviewVisibleOffset; 928 moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener); 929 tracker.onShowMoreKeysPanel(moreKeysPanel); 930 // TODO: Implement zoom in animation of more keys panel. 931 dismissKeyPreviewWithoutDelay(key); 932 } 933 934 public boolean isInDraggingFinger() { 935 if (isShowingMoreKeysPanel()) { 936 return true; 937 } 938 return PointerTracker.isAnyInDraggingFinger(); 939 } 940 941 @Override 942 public void onShowMoreKeysPanel(final MoreKeysPanel panel) { 943 locatePreviewPlacerView(); 944 // TODO: Remove this check 945 if (panel.isShowingInParent()) { 946 panel.dismissMoreKeysPanel(); 947 } 948 mPreviewPlacerView.addView(panel.getContainerView()); 949 mMoreKeysPanel = panel; 950 dimEntireKeyboard(true /* dimmed */); 951 } 952 953 public boolean isShowingMoreKeysPanel() { 954 return mMoreKeysPanel != null && mMoreKeysPanel.isShowingInParent(); 955 } 956 957 @Override 958 public void onCancelMoreKeysPanel(final MoreKeysPanel panel) { 959 PointerTracker.dismissAllMoreKeysPanels(); 960 } 961 962 @Override 963 public void onDismissMoreKeysPanel(final MoreKeysPanel panel) { 964 dimEntireKeyboard(false /* dimmed */); 965 if (isShowingMoreKeysPanel()) { 966 mPreviewPlacerView.removeView(mMoreKeysPanel.getContainerView()); 967 mMoreKeysPanel = null; 968 } 969 } 970 971 public void startDoubleTapShiftKeyTimer() { 972 mKeyTimerHandler.startDoubleTapShiftKeyTimer(); 973 } 974 975 public void cancelDoubleTapShiftKeyTimer() { 976 mKeyTimerHandler.cancelDoubleTapShiftKeyTimer(); 977 } 978 979 public boolean isInDoubleTapShiftKeyTimeout() { 980 return mKeyTimerHandler.isInDoubleTapShiftKeyTimeout(); 981 } 982 983 @Override 984 public boolean dispatchTouchEvent(MotionEvent event) { 985 if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { 986 return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event); 987 } 988 return super.dispatchTouchEvent(event); 989 } 990 991 @Override 992 public boolean onTouchEvent(final MotionEvent me) { 993 if (getKeyboard() == null) { 994 return false; 995 } 996 if (mNonDistinctMultitouchHelper != null) { 997 if (me.getPointerCount() > 1 && mKeyTimerHandler.isInKeyRepeat()) { 998 // Key repeating timer will be canceled if 2 or more keys are in action. 999 mKeyTimerHandler.cancelKeyRepeatTimer(); 1000 } 1001 // Non distinct multitouch screen support 1002 mNonDistinctMultitouchHelper.processMotionEvent(me, this); 1003 return true; 1004 } 1005 return processMotionEvent(me); 1006 } 1007 1008 public boolean processMotionEvent(final MotionEvent me) { 1009 if (LatinImeLogger.sUsabilityStudy) { 1010 UsabilityStudyLogUtils.writeMotionEvent(me); 1011 } 1012 // Currently the same "move" event is being logged twice. 1013 if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { 1014 ResearchLogger.mainKeyboardView_processMotionEvent(me); 1015 } 1016 1017 final int index = me.getActionIndex(); 1018 final int id = me.getPointerId(index); 1019 final PointerTracker tracker = PointerTracker.getPointerTracker(id, this); 1020 tracker.processMotionEvent(me, this); 1021 return true; 1022 } 1023 1024 public void cancelAllOngoingEvents() { 1025 mKeyTimerHandler.cancelAllMessages(); 1026 mDrawingHandler.cancelAllMessages(); 1027 dismissAllKeyPreviews(); 1028 dismissGestureFloatingPreviewText(); 1029 dismissSlidingKeyInputPreview(); 1030 PointerTracker.dismissAllMoreKeysPanels(); 1031 PointerTracker.cancelAllPointerTrackers(); 1032 } 1033 1034 public void closing() { 1035 cancelAllOngoingEvents(); 1036 mMoreKeysKeyboardCache.clear(); 1037 } 1038 1039 /** 1040 * Receives hover events from the input framework. 1041 * 1042 * @param event The motion event to be dispatched. 1043 * @return {@code true} if the event was handled by the view, {@code false} 1044 * otherwise 1045 */ 1046 @Override 1047 public boolean dispatchHoverEvent(final MotionEvent event) { 1048 if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { 1049 final PointerTracker tracker = PointerTracker.getPointerTracker(0, this); 1050 return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker); 1051 } 1052 1053 // Reflection doesn't support calling superclass methods. 1054 return false; 1055 } 1056 1057 public void updateShortcutKey(final boolean available) { 1058 final Keyboard keyboard = getKeyboard(); 1059 if (keyboard == null) { 1060 return; 1061 } 1062 final Key shortcutKey = keyboard.getKey(Constants.CODE_SHORTCUT); 1063 if (shortcutKey == null) { 1064 return; 1065 } 1066 shortcutKey.setEnabled(available); 1067 invalidateKey(shortcutKey); 1068 } 1069 1070 public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged, 1071 final boolean needsToDisplayLanguage, final boolean hasMultipleEnabledIMEsOrSubtypes) { 1072 mNeedsToDisplayLanguage = needsToDisplayLanguage; 1073 mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes; 1074 final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator; 1075 if (animator == null) { 1076 mNeedsToDisplayLanguage = false; 1077 } else { 1078 if (subtypeChanged && needsToDisplayLanguage) { 1079 setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE); 1080 if (animator.isStarted()) { 1081 animator.cancel(); 1082 } 1083 animator.start(); 1084 } else { 1085 if (!animator.isStarted()) { 1086 mLanguageOnSpacebarAnimAlpha = mLanguageOnSpacebarFinalAlpha; 1087 } 1088 } 1089 } 1090 invalidateKey(mSpaceKey); 1091 } 1092 1093 public void updateAutoCorrectionState(final boolean isAutoCorrection) { 1094 if (!mAutoCorrectionSpacebarLedEnabled) { 1095 return; 1096 } 1097 mAutoCorrectionSpacebarLedOn = isAutoCorrection; 1098 invalidateKey(mSpaceKey); 1099 } 1100 1101 private void dimEntireKeyboard(final boolean dimmed) { 1102 final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed; 1103 mNeedsToDimEntireKeyboard = dimmed; 1104 if (needsRedrawing) { 1105 invalidateAllKeys(); 1106 } 1107 } 1108 1109 @Override 1110 protected void onDraw(final Canvas canvas) { 1111 super.onDraw(canvas); 1112 1113 // Overlay a dark rectangle to dim. 1114 if (mNeedsToDimEntireKeyboard) { 1115 canvas.drawRect(0.0f, 0.0f, getWidth(), getHeight(), mBackgroundDimAlphaPaint); 1116 } 1117 } 1118 1119 @Override 1120 protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint, 1121 final KeyDrawParams params) { 1122 if (key.altCodeWhileTyping() && key.isEnabled()) { 1123 params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha; 1124 } 1125 // Don't draw key top letter when key preview is showing. 1126 if (FADE_OUT_KEY_TOP_LETTER_WHEN_KEY_IS_PRESSED 1127 && mShowingKeyPreviewTextViews.containsKey(key)) { 1128 // TODO: Fade out animation for the key top letter, and fade in animation for the key 1129 // background color when the user presses the key. 1130 return; 1131 } 1132 final int code = key.getCode(); 1133 if (code == Constants.CODE_SPACE) { 1134 drawSpacebar(key, canvas, paint); 1135 // Whether space key needs to show the "..." popup hint for special purposes 1136 if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) { 1137 drawKeyPopupHint(key, canvas, paint, params); 1138 } 1139 } else if (code == Constants.CODE_LANGUAGE_SWITCH) { 1140 super.onDrawKeyTopVisuals(key, canvas, paint, params); 1141 drawKeyPopupHint(key, canvas, paint, params); 1142 } else { 1143 super.onDrawKeyTopVisuals(key, canvas, paint, params); 1144 } 1145 } 1146 1147 private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) { 1148 final int maxTextWidth = width - mLanguageOnSpacebarHorizontalMargin * 2; 1149 paint.setTextScaleX(1.0f); 1150 final float textWidth = TypefaceUtils.getStringWidth(text, paint); 1151 if (textWidth < width) { 1152 return true; 1153 } 1154 1155 final float scaleX = maxTextWidth / textWidth; 1156 if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) { 1157 return false; 1158 } 1159 1160 paint.setTextScaleX(scaleX); 1161 return TypefaceUtils.getStringWidth(text, paint) < maxTextWidth; 1162 } 1163 1164 // Layout language name on spacebar. 1165 private String layoutLanguageOnSpacebar(final Paint paint, 1166 final InputMethodSubtype subtype, final int width) { 1167 1168 // Choose appropriate language name to fit into the width. 1169 final String fullText = SubtypeLocaleUtils.getFullDisplayName(subtype); 1170 if (fitsTextIntoWidth(width, fullText, paint)) { 1171 return fullText; 1172 } 1173 1174 final String middleText = SubtypeLocaleUtils.getMiddleDisplayName(subtype); 1175 if (fitsTextIntoWidth(width, middleText, paint)) { 1176 return middleText; 1177 } 1178 1179 final String shortText = SubtypeLocaleUtils.getShortDisplayName(subtype); 1180 if (fitsTextIntoWidth(width, shortText, paint)) { 1181 return shortText; 1182 } 1183 1184 return ""; 1185 } 1186 1187 private void drawSpacebar(final Key key, final Canvas canvas, final Paint paint) { 1188 final int width = key.getWidth(); 1189 final int height = key.getHeight(); 1190 1191 // If input language are explicitly selected. 1192 if (mNeedsToDisplayLanguage) { 1193 paint.setTextAlign(Align.CENTER); 1194 paint.setTypeface(Typeface.DEFAULT); 1195 paint.setTextSize(mLanguageOnSpacebarTextSize); 1196 final InputMethodSubtype subtype = getKeyboard().mId.mSubtype; 1197 final String language = layoutLanguageOnSpacebar(paint, subtype, width); 1198 // Draw language text with shadow 1199 final float descent = paint.descent(); 1200 final float textHeight = -paint.ascent() + descent; 1201 final float baseline = height / 2 + textHeight / 2; 1202 paint.setColor(mLanguageOnSpacebarTextShadowColor); 1203 paint.setAlpha(mLanguageOnSpacebarAnimAlpha); 1204 canvas.drawText(language, width / 2, baseline - descent - 1, paint); 1205 paint.setColor(mLanguageOnSpacebarTextColor); 1206 paint.setAlpha(mLanguageOnSpacebarAnimAlpha); 1207 canvas.drawText(language, width / 2, baseline - descent, paint); 1208 } 1209 1210 // Draw the spacebar icon at the bottom 1211 if (mAutoCorrectionSpacebarLedOn) { 1212 final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100; 1213 final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight(); 1214 int x = (width - iconWidth) / 2; 1215 int y = height - iconHeight; 1216 drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight); 1217 } else if (mSpaceIcon != null) { 1218 final int iconWidth = mSpaceIcon.getIntrinsicWidth(); 1219 final int iconHeight = mSpaceIcon.getIntrinsicHeight(); 1220 int x = (width - iconWidth) / 2; 1221 int y = height - iconHeight; 1222 drawIcon(canvas, mSpaceIcon, x, y, iconWidth, iconHeight); 1223 } 1224 } 1225 1226 @Override 1227 public void deallocateMemory() { 1228 super.deallocateMemory(); 1229 mGestureTrailsPreview.deallocateMemory(); 1230 } 1231} 1232