/**
   * Rotate the buttons.
   *
   * @param degrees The degrees, the menu items should get rotated.
   */
  private void rotateButtons(float degrees) {
    int left, top, childCount = getChildCount();
    float angleDelay = 360 / childCount;
    angle += degrees;

    if (angle > 360) {
      angle -= 360;
    } else {
      if (angle < 0) {
        angle += 360;
      }
    }

    for (int i = 0; i < childCount; i++) {
      if (angle > 360) {
        angle -= 360;
      } else {
        if (angle < 0) {
          angle += 360;
        }
      }

      final CircleImageView child = (CircleImageView) getChildAt(i);
      if (child.getVisibility() == GONE) {
        continue;
      }
      left =
          Math.round(
              (float)
                  (((circleWidth / 2) - childWidth / 2)
                      + radius * Math.cos(Math.toRadians(angle))));
      top =
          Math.round(
              (float)
                  (((circleHeight / 2) - childHeight / 2)
                      + radius * Math.sin(Math.toRadians(angle))));

      child.setAngle(angle);

      if (Math.abs(angle - firstChildPos) < (angleDelay / 2) && selected != child.getPosition()) {
        selected = child.getPosition();

        if (mOnItemSelectedListener != null && rotateToCenter) {
          mOnItemSelectedListener.onItemSelected(child, selected, child.getId(), child.getName());
        }
      }

      child.layout(left, top, left + childWidth, top + childHeight);
      angle += angleDelay;
    }
  }
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    int layoutWidth = r - l;
    int layoutHeight = b - t;

    // Laying out the child views
    final int childCount = getChildCount();
    int left, top;
    radius = (layoutWidth <= layoutHeight) ? layoutWidth / 3 : layoutHeight / 3;

    childWidth = (int) (radius / 1.5);
    childHeight = (int) (radius / 1.5);

    float angleDelay = 360 / getChildCount();

    for (int i = 0; i < childCount; i++) {
      final CircleImageView child = (CircleImageView) getChildAt(i);
      if (child.getVisibility() == GONE) {
        continue;
      }

      if (angle > 360) {
        angle -= 360;
      } else {
        if (angle < 0) {
          angle += 360;
        }
      }

      child.setAngle(angle);
      child.setPosition(i);

      left =
          Math.round(
              (float)
                  (((layoutWidth / 2) - childWidth / 2)
                      + radius * Math.cos(Math.toRadians(angle))));
      top =
          Math.round(
              (float)
                  (((layoutHeight / 2) - childHeight / 2)
                      + radius * Math.sin(Math.toRadians(angle))));

      child.layout(left, top, left + childWidth, top + childHeight);
      angle += angleDelay;
    }
  }
  @Override
  public boolean onTouchEvent(MotionEvent ev) {
    final int action = MotionEventCompat.getActionMasked(ev);

    if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
      mReturningToStart = false;
    }

    if (!isEnabled() || mReturningToStart || canChildScrollUp()) {
      // Fail fast if we're not in a state where a swipe is possible
      return false;
    }

    switch (action) {
      case MotionEvent.ACTION_DOWN:
        mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
        mIsBeingDragged = false;
        break;

      case MotionEvent.ACTION_MOVE:
        {
          final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
          if (pointerIndex < 0) {
            Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id.");
            return false;
          }

          final float y = MotionEventCompat.getY(ev, pointerIndex);
          final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE;
          if (mIsBeingDragged) {
            mProgress.showArrow(true);
            float originalDragPercent = overscrollTop / mTotalDragDistance;
            if (originalDragPercent < 0) {
              return false;
            }
            float dragPercent = Math.min(1f, Math.abs(originalDragPercent));
            float adjustedPercent = (float) Math.max(dragPercent - .4, 0) * 5 / 3;
            float extraOS = Math.abs(overscrollTop) - mTotalDragDistance;
            float slingshotDist =
                mUsingCustomStart ? mSpinnerFinalOffset - mOriginalOffsetTop : mSpinnerFinalOffset;
            float tensionSlingshotPercent =
                Math.max(0, Math.min(extraOS, slingshotDist * 2) / slingshotDist);
            float tensionPercent =
                (float) ((tensionSlingshotPercent / 4) - Math.pow((tensionSlingshotPercent / 4), 2))
                    * 2f;
            float extraMove = (slingshotDist) * tensionPercent * 2;

            int targetY = mOriginalOffsetTop + (int) ((slingshotDist * dragPercent) + extraMove);
            // where 1.0f is a full circle
            if (mCircleView.getVisibility() != View.VISIBLE) {
              mCircleView.setVisibility(View.VISIBLE);
            }
            if (!mScale) {
              ViewCompat.setScaleX(mCircleView, 1f);
              ViewCompat.setScaleY(mCircleView, 1f);
            }
            if (overscrollTop < mTotalDragDistance) {
              if (mScale) {
                setAnimationProgress(overscrollTop / mTotalDragDistance);
              }
              if (mProgress.getAlpha() > STARTING_PROGRESS_ALPHA
                  && !isAnimationRunning(mAlphaStartAnimation)) {
                // Animate the alpha
                startProgressAlphaStartAnimation();
              }
              float strokeStart = adjustedPercent * .8f;
              mProgress.setStartEndTrim(0f, Math.min(MAX_PROGRESS_ANGLE, strokeStart));
              mProgress.setArrowScale(Math.min(1f, adjustedPercent));
            } else {
              if (mProgress.getAlpha() < MAX_ALPHA && !isAnimationRunning(mAlphaMaxAnimation)) {
                // Animate the alpha
                startProgressAlphaMaxAnimation();
              }
            }
            float rotation = (-0.25f + .4f * adjustedPercent + tensionPercent * 2) * .5f;
            mProgress.setProgressRotation(rotation);
            setTargetOffsetTopAndBottom(
                targetY - mCurrentTargetOffsetTop, true /* requires update */);
          }
          break;
        }
      case MotionEventCompat.ACTION_POINTER_DOWN:
        {
          final int index = MotionEventCompat.getActionIndex(ev);
          mActivePointerId = MotionEventCompat.getPointerId(ev, index);
          break;
        }

      case MotionEventCompat.ACTION_POINTER_UP:
        onSecondaryPointerUp(ev);
        break;

      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:
        {
          if (mActivePointerId == INVALID_POINTER) {
            if (action == MotionEvent.ACTION_UP) {
              Log.e(LOG_TAG, "Got ACTION_UP event but don't have an active pointer id.");
            }
            return false;
          }
          final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
          final float y = MotionEventCompat.getY(ev, pointerIndex);
          final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE;
          mIsBeingDragged = false;
          if (overscrollTop > mTotalDragDistance) {
            setRefreshing(true, true /* notify */);
          } else {
            // cancel refresh
            mRefreshing = false;
            mProgress.setStartEndTrim(0f, 0f);
            Animation.AnimationListener listener = null;
            if (!mScale) {
              listener =
                  new Animation.AnimationListener() {

                    @Override
                    public void onAnimationStart(Animation animation) {}

                    @Override
                    public void onAnimationEnd(Animation animation) {
                      if (!mScale) {
                        startScaleDownAnimation(null);
                      }
                    }

                    @Override
                    public void onAnimationRepeat(Animation animation) {}
                  };
            }
            animateOffsetToStartPosition(mCurrentTargetOffsetTop, listener);
            mProgress.showArrow(false);
          }
          mActivePointerId = INVALID_POINTER;
          return false;
        }
    }

    return true;
  }