content: package com.github.alexjlockwood.kyrie;
  
  import android.content.res.Resources;
  import android.content.res.TypedArray;
  import android.graphics.LinearGradient;
  import android.graphics.RadialGradient;
  import android.graphics.Shader;
  import android.graphics.SweepGradient;
  import android.util.AttributeSet;
  
  import androidx.annotation.ColorInt;
  import androidx.annotation.IntDef;
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  
  import org.xmlpull.v1.XmlPullParser;
  import org.xmlpull.v1.XmlPullParserException;
  
  import java.io.IOException;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.util.ArrayList;
  import java.util.List;
  
  import static android.graphics.Color.TRANSPARENT;
  import static android.graphics.drawable.GradientDrawable.LINEAR_GRADIENT;
  import static android.graphics.drawable.GradientDrawable.RADIAL_GRADIENT;
  import static android.graphics.drawable.GradientDrawable.SWEEP_GRADIENT;
  
  final class GradientColorInflater {
  
    @IntDef({TILE_MODE_CLAMP, TILE_MODE_REPEAT, TILE_MODE_MIRROR})
    @Retention(RetentionPolicy.SOURCE)
    private @interface GradientTileMode {}
  
    private static final int TILE_MODE_CLAMP = 0;
    private static final int TILE_MODE_REPEAT = 1;
    private static final int TILE_MODE_MIRROR = 2;
  
    private GradientColorInflater() {}
  
    static Shader createFromXmlInner(
        @NonNull Resources resources,
        @NonNull XmlPullParser parser,
        @NonNull AttributeSet attrs,
        @Nullable Resources.Theme theme)
        throws IOException, XmlPullParserException {
      final String name = parser.getName();
      if (!name.equals("gradient")) {
        throw new XmlPullParserException(
            parser.getPositionDescription() + ": invalid gradient color tag " + name);
      }
  
      final TypedArray a =
          TypedArrayUtils.obtainAttributes(resources, theme, attrs, R.styleable.GradientColor);
      final float startX =
          TypedArrayUtils.getNamedFloat(
              a, parser, "startX", R.styleable.GradientColor_android_startX, 0f);
      final float startY =
          TypedArrayUtils.getNamedFloat(
              a, parser, "startY", R.styleable.GradientColor_android_startY, 0f);
      final float endX =
          TypedArrayUtils.getNamedFloat(
              a, parser, "endX", R.styleable.GradientColor_android_endX, 0f);
      final float endY =
          TypedArrayUtils.getNamedFloat(
              a, parser, "endY", R.styleable.GradientColor_android_endY, 0f);
      final float centerX =
          TypedArrayUtils.getNamedFloat(
              a, parser, "centerX", R.styleable.GradientColor_android_centerX, 0f);
      final float centerY =
          TypedArrayUtils.getNamedFloat(
              a, parser, "centerY", R.styleable.GradientColor_android_centerY, 0f);
      final int type =
          TypedArrayUtils.getNamedInt(
              a, parser, "type", R.styleable.GradientColor_android_type, LINEAR_GRADIENT);
      final int startColor =
          TypedArrayUtils.getNamedColor(
              a, parser, "startColor", R.styleable.GradientColor_android_startColor, TRANSPARENT);
      final boolean hasCenterColor = TypedArrayUtils.hasAttribute(parser, "centerColor");
      final int centerColor =
          TypedArrayUtils.getNamedColor(
              a, parser, "centerColor", R.styleable.GradientColor_android_centerColor, TRANSPARENT);
      final int endColor =
          TypedArrayUtils.getNamedColor(
              a, parser, "endColor", R.styleable.GradientColor_android_endColor, TRANSPARENT);
      final int tileMode =
          TypedArrayUtils.getNamedInt(
              a, parser, "tileMode", R.styleable.GradientColor_android_tileMode, TILE_MODE_CLAMP);
      final float gradientRadius =
          TypedArrayUtils.getNamedFloat(
              a, parser, "gradientRadius", R.styleable.GradientColor_android_gradientRadius, 0f);
      a.recycle();
  
      ColorStops colorStops = inflateChildElements(resources, parser, attrs, theme);
      colorStops = checkColors(colorStops, startColor, endColor, hasCenterColor, centerColor);
  
      switch (type) {
        case RADIAL_GRADIENT:
          if (gradientRadius <= 0f) {
            throw new XmlPullParserException(
                "<gradient> tag requires 'gradientRadius' attribute with radial type");
          }
          return new RadialGradient(
              centerX,
              centerY,
              gradientRadius,
              colorStops.mColors,
              colorStops.mOffsets,
              parseTileMode(tileMode));
        case SWEEP_GRADIENT:
          return new SweepGradient(centerX, centerY, colorStops.mColors, colorStops.mOffsets);
        case LINEAR_GRADIENT:
        default:
          return new LinearGradient(
              startX,
              startY,
              endX,
              endY,
              colorStops.mColors,
              colorStops.mOffsets,
              parseTileMode(tileMode));
      }
    }
  
    private static ColorStops inflateChildElements(
        @NonNull Resources resources,
        @NonNull XmlPullParser parser,
        @NonNull AttributeSet attrs,
        @Nullable Resources.Theme theme)
        throws XmlPullParserException, IOException {
      final int innerDepth = parser.getDepth() + 1;
      int type;
      int depth;
  
      List<Float> offsets = new ArrayList<>(20);
      List<Integer> colors = new ArrayList<>(20);
  
      while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
          && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
        if (type != XmlPullParser.START_TAG) {
          continue;
        }
        if (depth > innerDepth || !parser.getName().equals("item")) {
          continue;
        }
  
        final TypedArray a =
            TypedArrayUtils.obtainAttributes(resources, theme, attrs, R.styleable.GradientColorItem);
        final boolean hasColor = a.hasValue(R.styleable.GradientColorItem_android_color);
        final boolean hasOffset = a.hasValue(R.styleable.GradientColorItem_android_offset);
        if (!hasColor || !hasOffset) {
          throw new XmlPullParserException(
              parser.getPositionDescription()
                  + ": <item> tag requires a 'color' attribute and a 'offset' "
                  + "attribute!");
        }
  
        final int color = a.getColor(R.styleable.GradientColorItem_android_color, TRANSPARENT);
        final float offset = a.getFloat(R.styleable.GradientColorItem_android_offset, 0f);
        a.recycle();
  
        colors.add(color);
        offsets.add(offset);
      }
      if (colors.size() > 0) return new ColorStops(colors, offsets);
      return null;
    }
  
    private static ColorStops checkColors(
        @Nullable ColorStops colorItems,
        @ColorInt int startColor,
        @ColorInt int endColor,
        boolean hasCenterColor,
        @ColorInt int centerColor) {
      // prefer child color items if any, otherwise use the start, (center), end colors
      if (colorItems != null) {
        return colorItems;
      } else if (hasCenterColor) {
        return new ColorStops(startColor, centerColor, endColor);
      } else {
        return new ColorStops(startColor, endColor);
      }
    }
  
    private static Shader.TileMode parseTileMode(@GradientTileMode int tileMode) {
      switch (tileMode) {
        case TILE_MODE_REPEAT:
          return Shader.TileMode.REPEAT;
        case TILE_MODE_MIRROR:
          return Shader.TileMode.MIRROR;
        case TILE_MODE_CLAMP:
        default:
          return Shader.TileMode.CLAMP;
      }
    }
  
    static final class ColorStops {
      final int[] mColors;
      final float[] mOffsets;
  
      ColorStops(@NonNull List<Integer> colorsList, @NonNull List<Float> offsetsList) {
        final int size = colorsList.size();
        mColors = new int[size];
        mOffsets = new float[size];
        for (int i = 0; i < size; i++) {
          mColors[i] = colorsList.get(i);
          mOffsets[i] = offsetsList.get(i);
        }
      }
  
      ColorStops(@ColorInt int startColor, @ColorInt int endColor) {
        mColors = new int[] {startColor, endColor};
        mOffsets = new float[] {0f, 1f};
      }
  
      ColorStops(@ColorInt int startColor, @ColorInt int centerColor, @ColorInt int endColor) {
        mColors = new int[] {startColor, centerColor, endColor};
        mOffsets = new float[] {0f, 0.5f, 1f};
      }
    }
  }
  