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