/** This function must be called on the UI thread. */
 public void preventedTouchFinished() {
   checkMainThread();
   if (mState == PanZoomState.WAITING_LISTENERS) {
     // if we enter here, we just finished a block of events whose default actions
     // were prevented by touch listeners. Now there are no touch points left, so
     // we need to reset our state and re-bounce because we might be in overscroll
     bounce();
   }
 }
  private boolean onTouchCancel(MotionEvent event) {
    cancelTouch();

    if (mState == PanZoomState.WAITING_LISTENERS) {
      // we might get a cancel event from the TouchEventHandler while in the
      // WAITING_LISTENERS state if the touch listeners prevent-default the
      // block of events. at this point being in WAITING_LISTENERS is equivalent
      // to being in NOTHING with the exception of possibly being in overscroll.
      // so here we don't want to do anything right now; the overscroll will be
      // corrected in preventedTouchFinished().
      return false;
    }

    // ensure we snap back if we're overscrolled
    bounce();
    return false;
  }
  /**
   * Zoom to a specified rect IN CSS PIXELS.
   *
   * <p>While we usually use device pixels, @zoomToRect must be specified in CSS pixels.
   */
  private boolean animatedZoomTo(RectF zoomToRect) {
    setState(PanZoomState.ANIMATED_ZOOM);
    final float startZoom = getMetrics().zoomFactor;

    RectF viewport = getMetrics().getViewport();
    // 1. adjust the aspect ratio of zoomToRect to match that of the current viewport,
    // enlarging as necessary (if it gets too big, it will get shrunk in the next step).
    // while enlarging make sure we enlarge equally on both sides to keep the target rect
    // centered.
    float targetRatio = viewport.width() / viewport.height();
    float rectRatio = zoomToRect.width() / zoomToRect.height();
    if (FloatUtils.fuzzyEquals(targetRatio, rectRatio)) {
      // all good, do nothing
    } else if (targetRatio < rectRatio) {
      // need to increase zoomToRect height
      float newHeight = zoomToRect.width() / targetRatio;
      zoomToRect.top -= (newHeight - zoomToRect.height()) / 2;
      zoomToRect.bottom = zoomToRect.top + newHeight;
    } else { // targetRatio > rectRatio) {
      // need to increase zoomToRect width
      float newWidth = targetRatio * zoomToRect.height();
      zoomToRect.left -= (newWidth - zoomToRect.width()) / 2;
      zoomToRect.right = zoomToRect.left + newWidth;
    }

    float finalZoom = viewport.width() / zoomToRect.width();

    ImmutableViewportMetrics finalMetrics = getMetrics();
    finalMetrics =
        finalMetrics.setViewportOrigin(
            zoomToRect.left * finalMetrics.zoomFactor, zoomToRect.top * finalMetrics.zoomFactor);
    finalMetrics = finalMetrics.scaleTo(finalZoom, new PointF(0.0f, 0.0f));

    // 2. now run getValidViewportMetrics on it, so that the target viewport is
    // clamped down to prevent overscroll, over-zoom, and other bad conditions.
    finalMetrics = getValidViewportMetrics(finalMetrics);

    bounce(finalMetrics);
    return true;
  }
  private boolean onTouchEnd(MotionEvent event) {

    switch (mState) {
      case FLING:
      case BOUNCE:
      case WAITING_LISTENERS:
        // should never happen
        Log.e(LOGTAG, "Received impossible touch end while in " + mState);
        // fall through
      case ANIMATED_ZOOM:
      case NOTHING:
        // may happen if user double-taps and drags without lifting after the
        // second tap. ignore if this happens.
        return false;

      case TOUCHING:
        // the switch into TOUCHING might have happened while the page was
        // snapping back after overscroll. we need to finish the snap if that
        // was the case
        bounce();
        return false;

      case PANNING:
      case PANNING_LOCKED:
      case PANNING_HOLD:
      case PANNING_HOLD_LOCKED:
        setState(PanZoomState.FLING);
        fling();
        return true;

      case PINCHING:
        setState(PanZoomState.NOTHING);
        return true;
    }
    Log.e(LOGTAG, "Unhandled case " + mState + " in onTouchEnd");
    return false;
  }
 /* Performs a bounce-back animation to the nearest valid viewport metrics. */
 private void bounce() {
   setState(PanZoomState.BOUNCE);
   bounce(getValidViewportMetrics());
 }
 /** This function must be called from the UI thread. */
 public void abortPanning() {
   checkMainThread();
   bounce();
 }