@Override
 public void onDraw(Canvas canvas) {
   final boolean keyboardChanged = mKeyboardChanged;
   super.onDraw(canvas);
   // switching animation
   if (mAnimationLevel != AnimationsLevel.None && keyboardChanged && (mInAnimation != null)) {
     startAnimation(mInAnimation);
     mInAnimation = null;
   }
   // text pop out animation
   if (mPopOutText != null && mAnimationLevel != AnimationsLevel.None) {
     final int maxVerticalTravel = getHeight() / 2;
     final long animationDuration = 1200;
     final long currentAnimationTime = SystemClock.elapsedRealtime() - mPopOutTime;
     if (currentAnimationTime > animationDuration) {
       mPopOutText = null;
     } else {
       final float animationProgress =
           ((float) currentAnimationTime) / ((float) animationDuration);
       final float animationFactoredProgress = getPopOutAnimationInterpolator(animationProgress);
       final int y = mPopOutStartPoint.y - (int) (maxVerticalTravel * animationFactoredProgress);
       final int x = mPopOutStartPoint.x;
       final int alpha = 255 - (int) (255 * animationProgress);
       if (FeaturesSet.DEBUG_LOG)
         Log.d(
             TAG,
             "Drawing text popout '"
                 + mPopOutText
                 + "' at "
                 + x
                 + ","
                 + y
                 + " with alpha "
                 + alpha
                 + ". Animation progress is "
                 + animationProgress
                 + ", and factor progress is "
                 + animationFactoredProgress);
       // drawing
       setPaintToKeyText(mPaint);
       // will disappear over time
       mPaint.setAlpha(alpha);
       mPaint.setShadowLayer(5, 0, 0, Color.BLACK);
       // will grow over time
       mPaint.setTextSize(mPaint.getTextSize() * (1.0f + animationFactoredProgress));
       canvas.translate(x, y);
       canvas.drawText(mPopOutText, 0, mPopOutText.length(), 0, 0, mPaint);
       canvas.translate(-x, -y);
       // next frame
       postInvalidateDelayed(1000 / 50); // doing 50 frames per second;
     }
   }
 }
 public void openUtilityKeyboard() {
   dismissAllKeyPreviews();
   if (mUtilityKey == null) {
     mUtilityKey = new AnyKey(new Row(getKeyboard()), getThemedKeyboardDimens());
     mUtilityKey.edgeFlags = Keyboard.EDGE_BOTTOM;
     mUtilityKey.height = 0;
     mUtilityKey.width = 0;
     mUtilityKey.popupResId = R.xml.ext_kbd_utility_utility;
     mUtilityKey.externalResourcePopupLayout = false;
     mUtilityKey.x = getWidth() / 2;
     mUtilityKey.y = getHeight() - getThemedKeyboardDimens().getSmallKeyHeight();
   }
   super.onLongPress(mDefaultAddOn, mUtilityKey, true, false);
   mMiniKeyboard.setPreviewEnabled(true);
 }
  @Override
  public void setKeyboard(AnyKeyboard newKeyboard, float verticalCorrection) {
    mExtensionKey = null;
    mExtensionVisible = false;

    mUtilityKey = null;
    super.setKeyboard(newKeyboard, verticalCorrection);
    if (newKeyboard != null
        && newKeyboard instanceof GenericKeyboard
        && ((GenericKeyboard) newKeyboard).disableKeyPreviews()) {
      // Phone keyboard never shows popup preview (except language
      // switch).
      setPreviewEnabled(false);
    } else {
      setPreviewEnabled(AnyApplication.getConfig().getShowKeyPreview());
    }
    // TODO: For now! should be a calculated value
    // lots of key : true
    // some keys: false
    setProximityCorrectionEnabled(true);
    // One-seventh of the keyboard width seems like a reasonable threshold
    // mJumpThresholdSquare = newKeyboard.getMinWidth() / 7;
    // mJumpThresholdSquare *= mJumpThresholdSquare;
    // Assuming there are 4 rows, this is the coordinate of the last row
    // mLastRowY = (newKeyboard.getHeight() * 3) / 4;
    // setKeyboardLocal(newKeyboard);

    // looking for the space-bar, so I'll be able to detect swipes starting
    // at it
    mSpaceBarKey = null;
    if (newKeyboard != null) {
      for (Key aKey : newKeyboard.getKeys()) {
        if (aKey.getPrimaryCode() == KeyCodes.SPACE) {
          mSpaceBarKey = aKey;
          break;
        }
      }
    }
  }
 protected void onCancelEvent(PointerTracker tracker, int x, int y, long eventTime) {
   super.onCancelEvent(tracker, x, y, eventTime);
   mIsFirstDownEventInsideSpaceBar = false;
 }
  @Override
  public boolean onTouchEvent(@NonNull MotionEvent me) {
    if (getKeyboard()
        == null) // I mean, if there isn't any keyboard I'm handling, what's the point?
    return false;

    if (areTouchesDisabled()) return super.onTouchEvent(me);

    if (me.getAction() == MotionEvent.ACTION_DOWN) {
      mFirstTouchPoint.x = (int) me.getX();
      mFirstTouchPoint.y = (int) me.getY();
      mIsFirstDownEventInsideSpaceBar =
          mSpaceBarKey != null && mSpaceBarKey.isInside(mFirstTouchPoint.x, mFirstTouchPoint.y);
    } else if (mIsFirstDownEventInsideSpaceBar) {
      if (me.getAction() == MotionEvent.ACTION_MOVE) {
        setGesturePreviewText(mSwitcher, me);
        return true;
      } else if (me.getAction() == MotionEvent.ACTION_UP) {
        final int slide = getSlideDistance(me);
        final int distance = slide & 0x00FF; // removing direction
        if (distance > SLIDE_RATIO_FOR_GESTURE) {
          // cancelling the touch (since we handle this)
          disableTouchesTillFingersAreUp();
          // handling the gesture
          switch (slide & 0xFF00) {
            case DIRECTION_DOWN:
              mKeyboardActionListener.onSwipeDown(true);
              break;
            case DIRECTION_UP:
              mKeyboardActionListener.onSwipeUp(true);
              break;
            case DIRECTION_LEFT:
              mKeyboardActionListener.onSwipeLeft(true, isAtTwoFingersState());
              break;
            case DIRECTION_RIGHT:
              mKeyboardActionListener.onSwipeRight(true, isAtTwoFingersState());
              break;
          }
        }
        super.onTouchEvent(me);
        return true; // handled
      }
    }
    // If the motion event is above the keyboard and it's a MOVE event
    // coming even before the first MOVE event into the extension area
    if (!mIsFirstDownEventInsideSpaceBar
        && me.getY() < mExtensionKeyboardYActivationPoint
        && !mMiniKeyboardPopup.isShowing()
        && !mExtensionVisible
        && me.getAction() == MotionEvent.ACTION_MOVE) {
      if (mExtensionKeyboardAreaEntranceTime <= 0)
        mExtensionKeyboardAreaEntranceTime = System.currentTimeMillis();

      if (System.currentTimeMillis() - mExtensionKeyboardAreaEntranceTime
          > DELAY_BEFORE_POPPING_UP_EXTENSION_KBD) {
        KeyboardExtension extKbd = ((ExternalAnyKeyboard) getKeyboard()).getExtensionLayout();
        if (extKbd == null || extKbd.getKeyboardResId() == -1) {
          return super.onTouchEvent(me);
        } else {
          // telling the main keyboard that the last touch was
          // canceled
          MotionEvent cancel =
              MotionEvent.obtain(
                  me.getDownTime(),
                  me.getEventTime(),
                  MotionEvent.ACTION_CANCEL,
                  me.getX(),
                  me.getY(),
                  0);
          super.onTouchEvent(cancel);
          cancel.recycle();

          mExtensionVisible = true;
          dismissAllKeyPreviews();
          if (mExtensionKey == null) {
            mExtensionKey = new AnyKey(new Row(getKeyboard()), getThemedKeyboardDimens());
            mExtensionKey.edgeFlags = 0;
            mExtensionKey.height = 1;
            mExtensionKey.width = 1;
            mExtensionKey.popupResId = extKbd.getKeyboardResId();
            mExtensionKey.externalResourcePopupLayout = mExtensionKey.popupResId != 0;
            mExtensionKey.x = getWidth() / 2;
            mExtensionKey.y = mExtensionKeyboardPopupOffset;
          }
          // so the popup will be right above your finger.
          mExtensionKey.x = (int) me.getX();

          onLongPress(
              extKbd,
              mExtensionKey,
              AnyApplication.getConfig().isStickyExtensionKeyboard(),
              !AnyApplication.getConfig().isStickyExtensionKeyboard());
          // it is an extension..
          mMiniKeyboard.setPreviewEnabled(true);
          return true;
        }
      } else {
        return super.onTouchEvent(me);
      }
    } else if (mExtensionVisible && me.getY() > mExtensionKeyboardYDismissPoint) {
      // closing the popup
      dismissPopupKeyboard();
      return true;
    } else {
      return super.onTouchEvent(me);
    }
  }