/** Ensures correct size of the widget. */
  @Override
  protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = 200;
    if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) {
      width = MeasureSpec.getSize(widthMeasureSpec);
    }

    int height =
        thumbImage.getHeight()
            + (!mShowTextAboveThumbs ? 0 : PixelUtil.dpToPx(getContext(), HEIGHT_IN_DP));
    if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) {
      height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
    }
    setMeasuredDimension(width, height);
  }
  /** Draws the widget on the given canvas. */
  @Override
  protected synchronized void onDraw(@NonNull Canvas canvas) {
    super.onDraw(canvas);

    paint.setTextSize(mTextSize);
    paint.setStyle(Style.FILL);
    paint.setColor(mDefaultColor);
    paint.setAntiAlias(true);
    float minMaxLabelSize = 0;

    if (mShowLabels) {
      // draw min and max labels
      String minLabel = getContext().getString(R.string.demo_min_label);
      String maxLabel = getContext().getString(R.string.demo_max_label);
      minMaxLabelSize = Math.max(paint.measureText(minLabel), paint.measureText(maxLabel));
      float minMaxHeight = mTextOffset + mThumbHalfHeight + mTextSize / 3;
      canvas.drawText(minLabel, 0, minMaxHeight, paint);
      canvas.drawText(maxLabel, getWidth() - minMaxLabelSize, minMaxHeight, paint);
    }
    padding = mInternalPad + minMaxLabelSize + mThumbHalfWidth;

    // draw seek bar background line
    mRect.left = padding;
    mRect.right = getWidth() - padding;
    canvas.drawRoundRect(mRect, mRoundedCorners, mRoundedCorners, paint);

    boolean selectedValuesAreDefault =
        (getSelectedMinValue().equals(getAbsoluteMinValue())
            && getSelectedMaxValue().equals(getAbsoluteMaxValue()));

    int colorToUseForButtonsAndHighlightedLine =
        !mAlwaysActive && selectedValuesAreDefault
            ? mDefaultColor
            : // default values
            mActiveColor; // non default, filter is active

    // draw seek bar active range line
    mRect.left = normalizedToScreen(normalizedMinValue);
    mRect.right = normalizedToScreen(normalizedMaxValue);

    paint.setColor(colorToUseForButtonsAndHighlightedLine);
    canvas.drawRoundRect(mRect, mRoundedCorners, mRoundedCorners, paint);

    // draw minimum thumb if not a single thumb control
    if (!mSingleThumb) {
      drawThumb(
          normalizedToScreen(normalizedMinValue),
          Thumb.MIN.equals(pressedThumb),
          canvas,
          selectedValuesAreDefault);
    }

    // draw maximum thumb
    drawThumb(
        normalizedToScreen(normalizedMaxValue),
        Thumb.MAX.equals(pressedThumb),
        canvas,
        selectedValuesAreDefault);

    // draw the text if sliders have moved from default edges
    if (mShowTextAboveThumbs) {
      paint.setTextSize(mTextSize);
      paint.setColor(mTextAboveThumbsColor);
      // give text a bit more space here so it doesn't get cut off
      int offset = PixelUtil.dpToPx(getContext(), TEXT_LATERAL_PADDING_IN_DP);

      String minText = String.valueOf(getSelectedMinValue());
      String maxText = String.valueOf(getSelectedMaxValue());

      if (mValueProcessor != null) {
        minText = mValueProcessor.getProcessedValue(getSelectedMinValue());
        maxText = mValueProcessor.getProcessedValue(getSelectedMaxValue());
      }

      float minTextWidth = paint.measureText(minText) + offset;
      float maxTextWidth = paint.measureText(maxText) + offset;

      if (!mSingleThumb) {
        canvas.drawText(
            minText,
            normalizedToScreen(normalizedMinValue) - minTextWidth * 0.5f,
            mDistanceToTop + mTextSize,
            paint);
      }

      canvas.drawText(
          maxText,
          normalizedToScreen(normalizedMaxValue) - maxTextWidth * 0.5f,
          mDistanceToTop + mTextSize,
          paint);
    }
  }
  private void init(Context context, AttributeSet attrs) {
    float barHeight;
    int thumbNormal = R.drawable.seek_thumb_normal;
    int thumbPressed = R.drawable.seek_thumb_pressed;
    int thumbDisabled = R.drawable.seek_thumb_disabled;

    if (attrs == null) {
      setRangeToDefaultValues();
      mInternalPad = PixelUtil.dpToPx(context, INITIAL_PADDING_IN_DP);
      barHeight = PixelUtil.dpToPx(context, LINE_HEIGHT_IN_DP);
      mActiveColor = ACTIVE_COLOR;
      mDefaultColor = Color.GRAY;
      mTextAboveThumbsColor = Color.WHITE;
      mAlwaysActive = false;
      mShowTextAboveThumbs = true;
    } else {
      TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.RangeSeekBar, 0, 0);
      try {
        setRangeValues(
            extractNumericValueFromAttributes(
                a, R.styleable.RangeSeekBar_absoluteMinValue, DEFAULT_MINIMUM),
            extractNumericValueFromAttributes(
                a, R.styleable.RangeSeekBar_absoluteMaxValue, DEFAULT_MAXIMUM));
        mShowTextAboveThumbs = a.getBoolean(R.styleable.RangeSeekBar_valuesAboveThumbs, true);
        mSingleThumb = a.getBoolean(R.styleable.RangeSeekBar_singleThumb, false);
        mShowLabels = a.getBoolean(R.styleable.RangeSeekBar_showLabels, true);
        mInternalPad =
            a.getDimensionPixelSize(
                R.styleable.RangeSeekBar_internalPadding, INITIAL_PADDING_IN_DP);
        barHeight = a.getDimensionPixelSize(R.styleable.RangeSeekBar_barHeight, LINE_HEIGHT_IN_DP);
        mRoundedCorners = a.getDimensionPixelSize(R.styleable.RangeSeekBar_roundedCorners, 0);
        mActiveColor = a.getColor(R.styleable.RangeSeekBar_activeColor, ACTIVE_COLOR);
        mDefaultColor = a.getColor(R.styleable.RangeSeekBar_defaultColor, Color.GRAY);
        mTextAboveThumbsColor =
            a.getColor(R.styleable.RangeSeekBar_textAboveThumbsColor, Color.WHITE);
        mAlwaysActive = a.getBoolean(R.styleable.RangeSeekBar_alwaysActive, false);
        Drawable normalDrawable = a.getDrawable(R.styleable.RangeSeekBar_thumbNormal);
        if (normalDrawable != null) {
          thumbImage = BitmapUtil.drawableToBitmap(normalDrawable);
        }
        Drawable disabledDrawable = a.getDrawable(R.styleable.RangeSeekBar_thumbDisabled);
        if (disabledDrawable != null) {
          thumbDisabledImage = BitmapUtil.drawableToBitmap(disabledDrawable);
        }
        Drawable pressedDrawable = a.getDrawable(R.styleable.RangeSeekBar_thumbPressed);
        if (pressedDrawable != null) {
          thumbPressedImage = BitmapUtil.drawableToBitmap(pressedDrawable);
        }
      } finally {
        a.recycle();
      }
    }

    if (thumbImage == null) {
      thumbImage = BitmapFactory.decodeResource(getResources(), thumbNormal);
    }
    if (thumbPressedImage == null) {
      thumbPressedImage = BitmapFactory.decodeResource(getResources(), thumbPressed);
    }
    if (thumbDisabledImage == null) {
      thumbDisabledImage = BitmapFactory.decodeResource(getResources(), thumbDisabled);
    }

    mThumbHalfWidth = 0.5f * thumbImage.getWidth();
    mThumbHalfHeight = 0.5f * thumbImage.getHeight();

    setValuePrimAndNumberType();

    mTextSize = PixelUtil.dpToPx(context, DEFAULT_TEXT_SIZE_IN_DP);
    mDistanceToTop = PixelUtil.dpToPx(context, DEFAULT_TEXT_DISTANCE_TO_TOP_IN_DP);
    mTextOffset =
        !mShowTextAboveThumbs
            ? 0
            : this.mTextSize
                + PixelUtil.dpToPx(context, DEFAULT_TEXT_DISTANCE_TO_BUTTON_IN_DP)
                + this.mDistanceToTop;

    mRect =
        new RectF(
            padding,
            mTextOffset + mThumbHalfHeight - barHeight / 2,
            getWidth() - padding,
            mTextOffset + mThumbHalfHeight + barHeight / 2);

    // make RangeSeekBar focusable. This solves focus handling issues in case EditText widgets are
    // being used along with the RangeSeekBar within ScrollViews.
    setFocusable(true);
    setFocusableInTouchMode(true);
    mScaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();

    mValueProcessor = new RangeSeekBarValueProcessor();
  }