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