content: /*
   * Copyright (c) Meta Platforms, Inc. and affiliates.
   * All rights reserved.
   *
   * This source code is licensed under the BSD-style license found in the
   * LICENSE file in the root directory of this source tree.
   */
  
  package me.yifeiyuan.flap.skeleton;
  
  import android.animation.ValueAnimator;
  import android.content.Context;
  import android.content.res.TypedArray;
  import android.graphics.Color;
  import android.graphics.RectF;
  import android.util.AttributeSet;
  
  import androidx.annotation.ColorInt;
  import androidx.annotation.FloatRange;
  import androidx.annotation.IntDef;
  import androidx.annotation.Px;
  
  
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  
  import me.yifeiyuan.flap.R;
  
  /**
   * Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
   * Copy from https://github.com/facebook/shimmer-android
   *
   * @since 3.0.1
   */
  public class Shimmer {
    private static final int COMPONENT_COUNT = 4;
  
    /** The shape of the shimmer's highlight. By default LINEAR is used. */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({Shape.LINEAR, Shape.RADIAL})
    public @interface Shape {
      /** Linear gives a ray reflection effect. */
      int LINEAR = 0;
      /** Radial gives a spotlight effect. */
      int RADIAL = 1;
    }
  
    /** Direction of the shimmer's sweep. */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({
      Direction.LEFT_TO_RIGHT,
      Direction.TOP_TO_BOTTOM,
      Direction.RIGHT_TO_LEFT,
      Direction.BOTTOM_TO_TOP
    })
    public @interface Direction {
      int LEFT_TO_RIGHT = 0;
      int TOP_TO_BOTTOM = 1;
      int RIGHT_TO_LEFT = 2;
      int BOTTOM_TO_TOP = 3;
    }
  
    final float[] positions = new float[COMPONENT_COUNT];
    final int[] colors = new int[COMPONENT_COUNT];
    final RectF bounds = new RectF();
  
    @Direction int direction = Direction.LEFT_TO_RIGHT;
    @ColorInt
    int highlightColor = Color.WHITE;
    @ColorInt
    int baseColor = 0x4cffffff;
    @Shape int shape = Shape.LINEAR;
    int fixedWidth = 0;
    int fixedHeight = 0;
  
    float widthRatio = 1f;
    float heightRatio = 1f;
    float intensity = 0f;
    float dropoff = 0.5f;
    float tilt = 20f;
  
    boolean clipToChildren = true;
    boolean autoStart = true;
    boolean alphaShimmer = true;
  
    int repeatCount = ValueAnimator.INFINITE;
    int repeatMode = ValueAnimator.RESTART;
    long animationDuration = 1000L;
    long repeatDelay;
    long startDelay;
  
    Shimmer() {}
  
    int width(int width) {
      return fixedWidth > 0 ? fixedWidth : Math.round(widthRatio * width);
    }
  
    int height(int height) {
      return fixedHeight > 0 ? fixedHeight : Math.round(heightRatio * height);
    }
  
    void updateColors() {
      switch (shape) {
        default:
        case Shape.LINEAR:
          colors[0] = baseColor;
          colors[1] = highlightColor;
          colors[2] = highlightColor;
          colors[3] = baseColor;
          break;
        case Shape.RADIAL:
          colors[0] = highlightColor;
          colors[1] = highlightColor;
          colors[2] = baseColor;
          colors[3] = baseColor;
          break;
      }
    }
  
    void updatePositions() {
      switch (shape) {
        default:
        case Shape.LINEAR:
          positions[0] = Math.max((1f - intensity - dropoff) / 2f, 0f);
          positions[1] = Math.max((1f - intensity - 0.001f) / 2f, 0f);
          positions[2] = Math.min((1f + intensity + 0.001f) / 2f, 1f);
          positions[3] = Math.min((1f + intensity + dropoff) / 2f, 1f);
          break;
        case Shape.RADIAL:
          positions[0] = 0f;
          positions[1] = Math.min(intensity, 1f);
          positions[2] = Math.min(intensity + dropoff, 1f);
          positions[3] = 1f;
          break;
      }
    }
  
    void updateBounds(int viewWidth, int viewHeight) {
      int magnitude = Math.max(viewWidth, viewHeight);
      double rad = Math.PI / 2f - Math.toRadians(tilt % 90f);
      double hyp = magnitude / Math.sin(rad);
      int padding = 3 * Math.round((float) (hyp - magnitude) / 2f);
      bounds.set(-padding, -padding, width(viewWidth) + padding, height(viewHeight) + padding);
    }
  
    @SuppressWarnings({"SameParameterValue", "UnusedReturnValue"})
    public abstract static class Builder<T extends Builder<T>> {
      final Shimmer mShimmer = new Shimmer();
  
      // Gets around unchecked cast
      protected abstract T getThis();
  
      /** Applies all specified options from the {@link AttributeSet}. */
      public T consumeAttributes(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ShimmerFrameLayout, 0, 0);
        return consumeAttributes(a);
      }
  
      T consumeAttributes(TypedArray a) {
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_clip_to_children)) {
          setClipToChildren(
              a.getBoolean(
                  R.styleable.ShimmerFrameLayout_flap_shimmer_clip_to_children, mShimmer.clipToChildren));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_auto_start)) {
          setAutoStart(
              a.getBoolean(R.styleable.ShimmerFrameLayout_flap_shimmer_auto_start, mShimmer.autoStart));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_base_alpha)) {
          setBaseAlpha(a.getFloat(R.styleable.ShimmerFrameLayout_flap_shimmer_base_alpha, 0.3f));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_highlight_alpha)) {
          setHighlightAlpha(a.getFloat(R.styleable.ShimmerFrameLayout_flap_shimmer_highlight_alpha, 1f));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_duration)) {
          setDuration(
              a.getInt(
                  R.styleable.ShimmerFrameLayout_flap_shimmer_duration, (int) mShimmer.animationDuration));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_repeat_count)) {
          setRepeatCount(
              a.getInt(R.styleable.ShimmerFrameLayout_flap_shimmer_repeat_count, mShimmer.repeatCount));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_repeat_delay)) {
          setRepeatDelay(
              a.getInt(
                  R.styleable.ShimmerFrameLayout_flap_shimmer_repeat_delay, (int) mShimmer.repeatDelay));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_repeat_mode)) {
          setRepeatMode(
              a.getInt(R.styleable.ShimmerFrameLayout_flap_shimmer_repeat_mode, mShimmer.repeatMode));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_start_delay)) {
          setStartDelay(
              a.getInt(
                  R.styleable.ShimmerFrameLayout_flap_shimmer_start_delay, (int) mShimmer.startDelay));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_direction)) {
          int direction =
              a.getInt(R.styleable.ShimmerFrameLayout_flap_shimmer_direction, mShimmer.direction);
          switch (direction) {
            default:
            case Direction.LEFT_TO_RIGHT:
              setDirection(Direction.LEFT_TO_RIGHT);
              break;
            case Direction.TOP_TO_BOTTOM:
              setDirection(Direction.TOP_TO_BOTTOM);
              break;
            case Direction.RIGHT_TO_LEFT:
              setDirection(Direction.RIGHT_TO_LEFT);
              break;
            case Direction.BOTTOM_TO_TOP:
              setDirection(Direction.BOTTOM_TO_TOP);
              break;
          }
        }
  
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_shape)) {
          int shape = a.getInt(R.styleable.ShimmerFrameLayout_flap_shimmer_shape, mShimmer.shape);
          switch (shape) {
            default:
            case Shape.LINEAR:
              setShape(Shape.LINEAR);
              break;
            case Shape.RADIAL:
              setShape(Shape.RADIAL);
              break;
          }
        }
  
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_dropoff)) {
          setDropoff(a.getFloat(R.styleable.ShimmerFrameLayout_flap_shimmer_dropoff, mShimmer.dropoff));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_fixed_width)) {
          setFixedWidth(
              a.getDimensionPixelSize(
                  R.styleable.ShimmerFrameLayout_flap_shimmer_fixed_width, mShimmer.fixedWidth));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_fixed_height)) {
          setFixedHeight(
              a.getDimensionPixelSize(
                  R.styleable.ShimmerFrameLayout_flap_shimmer_fixed_height, mShimmer.fixedHeight));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_intensity)) {
          setIntensity(
              a.getFloat(R.styleable.ShimmerFrameLayout_flap_shimmer_intensity, mShimmer.intensity));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_width_ratio)) {
          setWidthRatio(
              a.getFloat(R.styleable.ShimmerFrameLayout_flap_shimmer_width_ratio, mShimmer.widthRatio));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_height_ratio)) {
          setHeightRatio(
              a.getFloat(R.styleable.ShimmerFrameLayout_flap_shimmer_height_ratio, mShimmer.heightRatio));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_tilt)) {
          setTilt(a.getFloat(R.styleable.ShimmerFrameLayout_flap_shimmer_tilt, mShimmer.tilt));
        }
        return getThis();
      }
  
      /** Copies the configuration of an already built Shimmer to this builder */
      public T copyFrom(Shimmer other) {
        setDirection(other.direction);
        setShape(other.shape);
        setFixedWidth(other.fixedWidth);
        setFixedHeight(other.fixedHeight);
        setWidthRatio(other.widthRatio);
        setHeightRatio(other.heightRatio);
        setIntensity(other.intensity);
        setDropoff(other.dropoff);
        setTilt(other.tilt);
        setClipToChildren(other.clipToChildren);
        setAutoStart(other.autoStart);
        setRepeatCount(other.repeatCount);
        setRepeatMode(other.repeatMode);
        setRepeatDelay(other.repeatDelay);
        setStartDelay(other.startDelay);
        setDuration(other.animationDuration);
        mShimmer.baseColor = other.baseColor;
        mShimmer.highlightColor = other.highlightColor;
        return getThis();
      }
  
      /** Sets the direction of the shimmer's sweep. See {@link Direction}. */
      public T setDirection(@Direction int direction) {
        mShimmer.direction = direction;
        return getThis();
      }
  
      /** Sets the shape of the shimmer. See {@link Shape}. */
      public T setShape(@Shape int shape) {
        mShimmer.shape = shape;
        return getThis();
      }
  
      /** Sets the fixed width of the shimmer, in pixels. */
      public T setFixedWidth(@Px int fixedWidth) {
        if (fixedWidth < 0) {
          throw new IllegalArgumentException("Given invalid width: " + fixedWidth);
        }
        mShimmer.fixedWidth = fixedWidth;
        return getThis();
      }
  
      /** Sets the fixed height of the shimmer, in pixels. */
      public T setFixedHeight(@Px int fixedHeight) {
        if (fixedHeight < 0) {
          throw new IllegalArgumentException("Given invalid height: " + fixedHeight);
        }
        mShimmer.fixedHeight = fixedHeight;
        return getThis();
      }
  
      /** Sets the width ratio of the shimmer, multiplied against the total width of the layout. */
      public T setWidthRatio(float widthRatio) {
        if (widthRatio < 0f) {
          throw new IllegalArgumentException("Given invalid width ratio: " + widthRatio);
        }
        mShimmer.widthRatio = widthRatio;
        return getThis();
      }
  
      /** Sets the height ratio of the shimmer, multiplied against the total height of the layout. */
      public T setHeightRatio(float heightRatio) {
        if (heightRatio < 0f) {
          throw new IllegalArgumentException("Given invalid height ratio: " + heightRatio);
        }
        mShimmer.heightRatio = heightRatio;
        return getThis();
      }
  
      /** Sets the intensity of the shimmer. A larger value causes the shimmer to be larger. */
      public T setIntensity(float intensity) {
        if (intensity < 0f) {
          throw new IllegalArgumentException("Given invalid intensity value: " + intensity);
        }
        mShimmer.intensity = intensity;
        return getThis();
      }
  
      /**
       * Sets how quickly the shimmer's gradient drops-off. A larger value causes a sharper drop-off.
       */
      public T setDropoff(float dropoff) {
        if (dropoff < 0f) {
          throw new IllegalArgumentException("Given invalid dropoff value: " + dropoff);
        }
        mShimmer.dropoff = dropoff;
        return getThis();
      }
  
      /** Sets the tilt angle of the shimmer in degrees. */
      public T setTilt(float tilt) {
        mShimmer.tilt = tilt;
        return getThis();
      }
  
      /**
       * Sets the base alpha, which is the alpha of the underlying children, amount in the range [0,
       * 1].
       */
      public T setBaseAlpha(@FloatRange(from = 0, to = 1) float alpha) {
        int intAlpha = (int) (clamp(0f, 1f, alpha) * 255f);
        mShimmer.baseColor = intAlpha << 24 | (mShimmer.baseColor & 0x00FFFFFF);
        return getThis();
      }
  
      /** Sets the shimmer alpha amount in the range [0, 1]. */
      public T setHighlightAlpha(@FloatRange(from = 0, to = 1) float alpha) {
        int intAlpha = (int) (clamp(0f, 1f, alpha) * 255f);
        mShimmer.highlightColor = intAlpha << 24 | (mShimmer.highlightColor & 0x00FFFFFF);
        return getThis();
      }
  
      /**
       * Sets whether the shimmer will clip to the childrens' contents, or if it will opaquely draw on
       * top of the children.
       */
      public T setClipToChildren(boolean status) {
        mShimmer.clipToChildren = status;
        return getThis();
      }
  
      /** Sets whether the shimmering animation will start automatically. */
      public T setAutoStart(boolean status) {
        mShimmer.autoStart = status;
        return getThis();
      }
  
      /**
       * Sets how often the shimmering animation will repeat. See {@link
       * ValueAnimator#setRepeatCount(int)}.
       */
      public T setRepeatCount(int repeatCount) {
        mShimmer.repeatCount = repeatCount;
        return getThis();
      }
  
      /**
       * Sets how the shimmering animation will repeat. See {@link
       * ValueAnimator#setRepeatMode(int)}.
       */
      public T setRepeatMode(int mode) {
        mShimmer.repeatMode = mode;
        return getThis();
      }
  
      /** Sets how long to wait in between repeats of the shimmering animation. */
      public T setRepeatDelay(long millis) {
        if (millis < 0) {
          throw new IllegalArgumentException("Given a negative repeat delay: " + millis);
        }
        mShimmer.repeatDelay = millis;
        return getThis();
      }
  
      /** Sets how long to wait for starting the shimmering animation. */
      public T setStartDelay(long millis) {
        if (millis < 0) {
          throw new IllegalArgumentException("Given a negative start delay: " + millis);
        }
        mShimmer.startDelay = millis;
        return getThis();
      }
  
      /** Sets how long the shimmering animation takes to do one full sweep. */
      public T setDuration(long millis) {
        if (millis < 0) {
          throw new IllegalArgumentException("Given a negative duration: " + millis);
        }
        mShimmer.animationDuration = millis;
        return getThis();
      }
  
      public Shimmer build() {
        mShimmer.updateColors();
        mShimmer.updatePositions();
        return mShimmer;
      }
  
      private static float clamp(float min, float max, float value) {
        return Math.min(max, Math.max(min, value));
      }
    }
  
    public static class AlphaHighlightBuilder extends Builder<AlphaHighlightBuilder> {
      public AlphaHighlightBuilder() {
        mShimmer.alphaShimmer = true;
      }
  
      @Override
      protected AlphaHighlightBuilder getThis() {
        return this;
      }
    }
  
    @SuppressWarnings("UnusedReturnValue")
    public static class ColorHighlightBuilder extends Builder<ColorHighlightBuilder> {
      public ColorHighlightBuilder() {
        mShimmer.alphaShimmer = false;
      }
  
      /** Sets the highlight color for the shimmer. */
      public ColorHighlightBuilder setHighlightColor(@ColorInt int color) {
        mShimmer.highlightColor = color;
        return getThis();
      }
  
      /** Sets the base color for the shimmer. */
      public ColorHighlightBuilder setBaseColor(@ColorInt int color) {
        mShimmer.baseColor = (mShimmer.baseColor & 0xFF000000) | (color & 0x00FFFFFF);
        return getThis();
      }
  
      @Override
      ColorHighlightBuilder consumeAttributes(TypedArray a) {
        super.consumeAttributes(a);
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_base_color)) {
          setBaseColor(
              a.getColor(R.styleable.ShimmerFrameLayout_flap_shimmer_base_color, mShimmer.baseColor));
        }
        if (a.hasValue(R.styleable.ShimmerFrameLayout_flap_shimmer_highlight_color)) {
          setHighlightColor(
              a.getColor(
                  R.styleable.ShimmerFrameLayout_flap_shimmer_highlight_color, mShimmer.highlightColor));
        }
        return getThis();
      }
  
      @Override
      protected ColorHighlightBuilder getThis() {
        return this;
      }
    }
  }
  