/**
   * Posts a pointer event to the dispatch queue.
   *
   * @param event The event to post.
   * @param webKitXOffset X offset to apply to events before dispatching them to web kit.
   * @param webKitYOffset Y offset to apply to events before dispatching them to web kit.
   * @param webKitScale The scale factor to apply to translated events before dispatching them to
   *     web kit.
   * @return True if the dispatcher will handle the event, false if the event is unsupported.
   */
  public boolean postPointerEvent(
      MotionEvent event, int webKitXOffset, int webKitYOffset, float webKitScale) {
    if (event == null) {
      throw new IllegalArgumentException("event cannot be null");
    }

    if (DEBUG) {
      Log.d(TAG, "postPointerEvent: " + event);
    }

    final int action = event.getActionMasked();
    final int eventType;
    switch (action) {
      case MotionEvent.ACTION_DOWN:
      case MotionEvent.ACTION_MOVE:
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_POINTER_DOWN:
      case MotionEvent.ACTION_POINTER_UP:
      case MotionEvent.ACTION_CANCEL:
        eventType = EVENT_TYPE_TOUCH;
        break;
      case MotionEvent.ACTION_SCROLL:
        eventType = EVENT_TYPE_SCROLL;
        break;
      case MotionEvent.ACTION_HOVER_ENTER:
      case MotionEvent.ACTION_HOVER_MOVE:
      case MotionEvent.ACTION_HOVER_EXIT:
        eventType = EVENT_TYPE_HOVER;
        break;
      default:
        return false; // currently unsupported event type
    }

    synchronized (mLock) {
      // Ensure that the event is consistent and should be delivered.
      MotionEvent eventToEnqueue = event;
      if (eventType == EVENT_TYPE_TOUCH) {
        eventToEnqueue = mPostTouchStream.update(event);
        if (eventToEnqueue == null) {
          if (DEBUG) {
            Log.d(TAG, "postPointerEvent: dropped event " + event);
          }
          unscheduleLongPressLocked();
          unscheduleClickLocked();
          hideTapCandidateLocked();
          return false;
        }

        if (action == MotionEvent.ACTION_DOWN && mPostSendTouchEventsToWebKit) {
          if (mUiCallbacks.shouldInterceptTouchEvent(eventToEnqueue)) {
            mPostDoNotSendTouchEventsToWebKitUntilNextGesture = true;
          } else if (mPostDoNotSendTouchEventsToWebKitUntilNextGesture) {
            // Recover from a previous web kit timeout.
            mPostDoNotSendTouchEventsToWebKitUntilNextGesture = false;
          }
        }
      }

      // Copy the event because we need to retain ownership.
      if (eventToEnqueue == event) {
        eventToEnqueue = event.copy();
      }

      DispatchEvent d =
          obtainDispatchEventLocked(
              eventToEnqueue, eventType, 0, webKitXOffset, webKitYOffset, webKitScale);
      updateStateTrackersLocked(d, event);
      enqueueEventLocked(d);
    }
    return true;
  }
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    if (!isEnabled()) return false;

    final View parent = (View) getParent();
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        {

          // only start tracking when in sweet spot
          final boolean acceptDrag;
          final boolean acceptLabel;
          if (mFollowAxis == VERTICAL) {
            acceptDrag = event.getX() > getWidth() - (mSweepPadding.right * 8);
            acceptLabel = mLabelLayout != null ? event.getX() < mLabelLayout.getWidth() : false;
          } else {
            acceptDrag = event.getY() > getHeight() - (mSweepPadding.bottom * 8);
            acceptLabel = mLabelLayout != null ? event.getY() < mLabelLayout.getHeight() : false;
          }

          final MotionEvent eventInParent = event.copy();
          eventInParent.offsetLocation(getLeft(), getTop());

          // ignore event when closer to a neighbor
          for (ChartSweepView neighbor : mNeighbors) {
            if (isTouchCloserTo(eventInParent, neighbor)) {
              return false;
            }
          }

          if (acceptDrag) {
            if (mFollowAxis == VERTICAL) {
              mTrackingStart = getTop() - mMargins.top;
            } else {
              mTrackingStart = getLeft() - mMargins.left;
            }
            mTracking = event.copy();
            mTouchMode = MODE_DRAG;

            // starting drag should activate entire chart
            if (!parent.isActivated()) {
              parent.setActivated(true);
            }

            return true;
          } else if (acceptLabel) {
            mTouchMode = MODE_LABEL;
            return true;
          } else {
            mTouchMode = MODE_NONE;
            return false;
          }
        }
      case MotionEvent.ACTION_MOVE:
        {
          if (mTouchMode == MODE_LABEL) {
            return true;
          }

          getParent().requestDisallowInterceptTouchEvent(true);

          // content area of parent
          final Rect parentContent = getParentContentRect();
          final Rect clampRect = computeClampRect(parentContent);
          if (clampRect.isEmpty()) return true;

          long value;
          if (mFollowAxis == VERTICAL) {
            final float currentTargetY = getTop() - mMargins.top;
            final float requestedTargetY = mTrackingStart + (event.getRawY() - mTracking.getRawY());
            final float clampedTargetY =
                MathUtils.constrain(requestedTargetY, clampRect.top, clampRect.bottom);
            setTranslationY(clampedTargetY - currentTargetY);

            value = mAxis.convertToValue(clampedTargetY - parentContent.top);
          } else {
            final float currentTargetX = getLeft() - mMargins.left;
            final float requestedTargetX = mTrackingStart + (event.getRawX() - mTracking.getRawX());
            final float clampedTargetX =
                MathUtils.constrain(requestedTargetX, clampRect.left, clampRect.right);
            setTranslationX(clampedTargetX - currentTargetX);

            value = mAxis.convertToValue(clampedTargetX - parentContent.left);
          }

          // round value from drag to nearest increment
          value -= value % mDragInterval;
          setValue(value);

          dispatchOnSweep(false);
          return true;
        }
      case MotionEvent.ACTION_UP:
        {
          if (mTouchMode == MODE_LABEL) {
            performClick();
          } else if (mTouchMode == MODE_DRAG) {
            mTrackingStart = 0;
            mTracking = null;
            mValue = mLabelValue;
            dispatchOnSweep(true);
            setTranslationX(0);
            setTranslationY(0);
            requestLayout();
          }

          mTouchMode = MODE_NONE;
          return true;
        }
      default:
        {
          return false;
        }
    }
  }