/**
   * Show this button at the specific location. If this button isn't attached to any parent view
   * yet, it will be add to activity's root view. If not, it will just update the location.
   *
   * @param parent The parent view. Should be {@link FrameLayout} or {@link RelativeLayout}
   * @param x The x value of anchor point.
   * @param y The y value of anchor point.
   * @param gravity The gravity apply with this button.
   * @see Gravity
   */
  public void show(ViewGroup parent, int x, int y, int gravity) {
    if (getParent() == null) {
      ViewGroup.LayoutParams params = parent.generateLayoutParams(null);
      params.width = mBackground.getIntrinsicWidth();
      params.height = mBackground.getIntrinsicHeight();
      updateParams(x, y, gravity, params);

      parent.addView(this, params);
    } else updateLocation(x, y, gravity);
  }
  /**
   * Show this button at the specific location. If this button isn't attached to any parent view
   * yet, it will be add to activity's root view. If not, it will just update the location.
   *
   * @param activity The activity that this button will be attached to.
   * @param x The x value of anchor point.
   * @param y The y value of anchor point.
   * @param gravity The gravity apply with this button.
   * @see Gravity
   */
  public void show(Activity activity, int x, int y, int gravity) {
    if (getParent() == null) {
      FrameLayout.LayoutParams params =
          new FrameLayout.LayoutParams(
              mBackground.getIntrinsicWidth(), mBackground.getIntrinsicHeight());
      updateParams(x, y, gravity, params);

      activity.getWindow().addContentView(this, params);
    } else updateLocation(x, y, gravity);
  }
 @Override
 public void draw(@NonNull Canvas canvas) {
   mBackground.draw(canvas);
   super.draw(canvas);
   if (mPrevIcon != null) mPrevIcon.draw(canvas);
   if (mIcon != null) mIcon.draw(canvas);
 }
 @Override
 protected void drawableStateChanged() {
   super.drawableStateChanged();
   if (mBackground != null) mBackground.setState(getDrawableState());
   if (mIcon != null) mIcon.setState(getDrawableState());
   if (mPrevIcon != null) mPrevIcon.setState(getDrawableState());
 }
  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  @Override
  public float getElevation() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) return super.getElevation();

    return mBackground.getShadowSize();
  }
  @Override
  public boolean onTouchEvent(@NonNull MotionEvent event) {
    int action = event.getActionMasked();

    if (action == MotionEvent.ACTION_DOWN && !mBackground.isPointerOver(event.getX(), event.getY()))
      return false;

    boolean result = super.onTouchEvent(event);
    return getRippleManager().onTouchEvent(event) || result;
  }
  private void updateParams(int x, int y, int gravity, ViewGroup.LayoutParams params) {
    int horizontalGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;

    switch (horizontalGravity) {
      case Gravity.LEFT:
        setLeftMargin(params, (int) (x - mBackground.getPaddingLeft()));
        break;
      case Gravity.CENTER_HORIZONTAL:
        setLeftMargin(params, (int) (x - mBackground.getCenterX()));
        break;
      case Gravity.RIGHT:
        setLeftMargin(
            params, (int) (x - mBackground.getPaddingLeft() - mBackground.getRadius() * 2));
        break;
      default:
        setLeftMargin(params, (int) (x - mBackground.getPaddingLeft()));
        break;
    }

    int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;

    switch (verticalGravity) {
      case Gravity.TOP:
        setTopMargin(params, (int) (y - mBackground.getPaddingTop()));
        break;
      case Gravity.CENTER_VERTICAL:
        setTopMargin(params, (int) (y - mBackground.getCenterY()));
        break;
      case Gravity.BOTTOM:
        setTopMargin(params, (int) (y - mBackground.getPaddingTop() - mBackground.getRadius() * 2));
        break;
      default:
        setTopMargin(params, (int) (y - mBackground.getPaddingTop()));
        break;
    }

    setLayoutParams(params);
  }
  /**
   * Set the drawable that is used as this button's icon.
   *
   * @param icon The drawable.
   * @param animation Indicate should show animation when switch drawable or not.
   */
  public void setIcon(Drawable icon, boolean animation) {
    if (icon == null) return;

    if (animation) {
      mSwitchIconAnimator.startAnimation(icon);
      invalidate();
    } else {
      if (mIcon != null) {
        mIcon.setCallback(null);
        unscheduleDrawable(mIcon);
      }

      mIcon = icon;
      float half = mIconSize / 2f;
      mIcon.setBounds(
          (int) (mBackground.getCenterX() - half),
          (int) (mBackground.getCenterY() - half),
          (int) (mBackground.getCenterX() + half),
          (int) (mBackground.getCenterY() + half));
      mIcon.setCallback(this);
      invalidate();
    }
  }
  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    mBackground.setBounds(0, 0, w, h);

    if (mIcon != null) {
      float half = mIconSize / 2f;
      mIcon.setBounds(
          (int) (mBackground.getCenterX() - half),
          (int) (mBackground.getCenterY() - half),
          (int) (mBackground.getCenterX() + half),
          (int) (mBackground.getCenterY() + half));
    }

    if (mPrevIcon != null) {
      float half = mIconSize / 2f;
      mPrevIcon.setBounds(
          (int) (mBackground.getCenterX() - half),
          (int) (mBackground.getCenterY() - half),
          (int) (mBackground.getCenterX() + half),
          (int) (mBackground.getCenterY() + half));
    }
  }
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   setMeasuredDimension(mBackground.getIntrinsicWidth(), mBackground.getIntrinsicHeight());
 }
 /**
  * Set radius of the button.
  *
  * @param radius The radius in pixel.
  */
 public void setRadius(int radius) {
   if (mBackground.setRadius(radius)) requestLayout();
 }
 @Override
 public void setBackgroundColor(int color) {
   mBackground.setColor(color);
   invalidate();
 }
 public void setBackgroundColor(ColorStateList color) {
   mBackground.setColor(color);
   invalidate();
 }
 /** @return The background color of this button. */
 public ColorStateList getBackgroundColor() {
   return mBackground.getColor();
 }
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 @Override
 public void setElevation(float elevation) {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) super.setElevation(elevation);
   else if (mBackground.setShadow(elevation, elevation)) requestLayout();
 }
  protected void applyStyle(
      Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    TypedArray a =
        context.obtainStyledAttributes(
            attrs, R.styleable.FloatingActionButton, defStyleAttr, defStyleRes);

    int radius = -1;
    int elevation = -1;
    ColorStateList bgColor = null;
    int bgAnimDuration = -1;
    int iconSrc = 0;
    int iconLineMorphing = 0;

    for (int i = 0, count = a.getIndexCount(); i < count; i++) {
      int attr = a.getIndex(i);

      if (attr == R.styleable.FloatingActionButton_fab_radius)
        radius = a.getDimensionPixelSize(attr, 0);
      else if (attr == R.styleable.FloatingActionButton_fab_elevation)
        elevation = a.getDimensionPixelSize(attr, 0);
      else if (attr == R.styleable.FloatingActionButton_fab_backgroundColor)
        bgColor = a.getColorStateList(attr);
      else if (attr == R.styleable.FloatingActionButton_fab_backgroundAnimDuration)
        bgAnimDuration = a.getInteger(attr, 0);
      else if (attr == R.styleable.FloatingActionButton_fab_iconSrc)
        iconSrc = a.getResourceId(attr, 0);
      else if (attr == R.styleable.FloatingActionButton_fab_iconLineMorphing)
        iconLineMorphing = a.getResourceId(attr, 0);
      else if (attr == R.styleable.FloatingActionButton_fab_iconSize)
        mIconSize = a.getDimensionPixelSize(attr, 0);
      else if (attr == R.styleable.FloatingActionButton_fab_animDuration)
        mAnimDuration = a.getInteger(attr, 0);
      else if (attr == R.styleable.FloatingActionButton_fab_interpolator) {
        int resId = a.getResourceId(R.styleable.FloatingActionButton_fab_interpolator, 0);
        if (resId != 0) mInterpolator = AnimationUtils.loadInterpolator(context, resId);
      }
    }

    a.recycle();

    if (mIconSize < 0) mIconSize = ThemeUtil.dpToPx(context, 24);

    if (mAnimDuration < 0)
      mAnimDuration = context.getResources().getInteger(android.R.integer.config_mediumAnimTime);

    if (mInterpolator == null) mInterpolator = new DecelerateInterpolator();

    if (mBackground == null) {
      if (radius < 0) radius = ThemeUtil.dpToPx(context, 28);

      if (elevation < 0) elevation = ThemeUtil.dpToPx(context, 4);

      if (bgColor == null) bgColor = ColorStateList.valueOf(ThemeUtil.colorAccent(context, 0));

      if (bgAnimDuration < 0) bgAnimDuration = 0;

      mBackground = new OvalShadowDrawable(radius, bgColor, elevation, elevation, bgAnimDuration);
      mBackground.setInEditMode(isInEditMode());
      mBackground.setBounds(0, 0, getWidth(), getHeight());
      mBackground.setCallback(this);
    } else {
      if (radius >= 0) mBackground.setRadius(radius);

      if (bgColor != null) mBackground.setColor(bgColor);

      if (elevation >= 0) mBackground.setShadow(elevation, elevation);

      if (bgAnimDuration >= 0) mBackground.setAnimationDuration(bgAnimDuration);
    }

    if (iconLineMorphing != 0)
      setIcon(new LineMorphingDrawable.Builder(context, iconLineMorphing).build(), false);
    else if (iconSrc != 0) setIcon(context.getResources().getDrawable(iconSrc), false);

    getRippleManager().onCreate(this, context, attrs, defStyleAttr, defStyleRes);
    Drawable background = getBackground();
    if (background != null && background instanceof RippleDrawable) {
      RippleDrawable drawable = (RippleDrawable) background;
      drawable.setBackgroundDrawable(null);
      drawable.setMask(
          RippleDrawable.Mask.TYPE_OVAL,
          0,
          0,
          0,
          0,
          (int) mBackground.getPaddingLeft(),
          (int) mBackground.getPaddingTop(),
          (int) mBackground.getPaddingRight(),
          (int) mBackground.getPaddingBottom());
    }
  }
 /** @return The radius of the button. */
 public int getRadius() {
   return mBackground.getRadius();
 }