private int getRailSlideOffset(TouchEvent event) {
    int offset = event.getX() - lastX;

    lastX = event.getX();
    lastY = event.getY();

    offset = adjustCalculatedOffsetToStayWithinBounds(offset);

    return offset;
  }
  @Override
  public void update(float portionOfSecond, List<TouchEvent> touchEvents) {
    if (railState != RailState.LOCKED) {
      if (railState == RailState.ADJUSTING) {
        boolean doneAdjusting = true;
        for (int i = 0; i < tilesToAdjust.length; i++) {
          if (tilesToAdjust[i] && !letterTiles[i].progressTowardDesiredPosition(portionOfSecond)) {
            doneAdjusting = false;
          }
        }

        if (doneAdjusting) {
          Arrays.fill(tilesToAdjust, false);
          railState = targetRailStateAfterAdjustment;
        }
      }

      if (railState == RailState.FLUNG) {
        int offset = (int) (flingVelocity * portionOfSecond);
        offset = adjustCalculatedOffsetToStayWithinBounds(offset);
        updateTilesWithOffset(offset);
        flingVelocity = flingVelocity * FLING_FRICTION;

        if (flingVelocity < 5 && flingVelocity > -5) {
          railState = RailState.RESTING;
        }
      }

      timeSinceLastTouchEvent += portionOfSecond;

      if (timeSinceLastTouchEvent > LONG_TOUCH_THRESHOLD
          && (railState == RailState.TOUCH_INITIATED || railState == RailState.SLIDING)) {
        tryToPickUpLetter();
      }

      int lastTouchOffset = 0;
      for (TouchEvent event : touchEvents) {
        if (event.getType() == TouchEvent.TOUCH_DOWN
            && railState == RailState.RESTING
            && touchIsInsideRail(event)) {
          railState = RailState.TOUCH_INITIATED;

          lastX = event.getX();
          lastY = event.getY();

          timeSinceLastTouchEvent = 0;
        }

        if (event.getType() == TouchEvent.TOUCH_DRAGGED) {
          if (railState == RailState.TOUCH_INITIATED || railState == RailState.SLIDING) {
            if (railState != RailState.SLIDING) {
              railState = RailState.SLIDING;
            }

            lastTouchOffset = getRailSlideOffset(event);
            updateTilesWithOffset(lastTouchOffset);
          }

          // some devices register drags even if there is no change. Others don't register a drag
          // without change
          if (lastTouchOffset != 0) {
            timeSinceLastTouchEvent = 0;
          }
        }

        if (event.getType() == TouchEvent.TOUCH_UP) {
          if (railState == RailState.SLIDING) {
            // Generally an up is accompanied by a drag first. But what it if isn't?
            if (lastTouchOffset == 0) {
              lastTouchOffset = getRailSlideOffset(event);
            }
            if (lastTouchOffset != 0) {
              railState = RailState.FLUNG;
              updateTilesWithOffset(lastTouchOffset);
              flingVelocity = lastTouchOffset / portionOfSecond;
            } else {
              railState = RailState.RESTING;
            }
          } else if (railState != RailState.LETTER_SELECTED && railState != RailState.ADJUSTING) {
            railState = RailState.RESTING;
          }

          timeSinceLastTouchEvent = 0;
        }
      }
    }
  }
 private boolean touchIsInsideRail(TouchEvent event) {
   return event.getX() > sectionLeft
       && event.getX() < sectionLeft + sectionWidth
       && event.getY() > sectionTop + Assets.padding
       && event.getY() < sectionTop + sectionHeight - Assets.padding;
 }