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