private GradientColor addOpacityStopsToGradientIfNeeded(GradientColor gradientColor, List<Float> array) {
    int startIndex = colorPoints * 4;
    if (array.size() <= startIndex) {
      return gradientColor;
    }

    // When there are opacity stops, we create a merged list of color stops and opacity stops.
    // For a given color stop, we linearly interpolate the opacity for the two opacity stops around it.
    // For a given opacity stop, we linearly interpolate the color for the two color stops around it.
    float[] colorStopPositions = gradientColor.getPositions();
    int[] colorStopColors = gradientColor.getColors();

    int opacityStops = (array.size() - startIndex) / 2;
    float[] opacityStopPositions = new float[opacityStops];
    float[] opacityStopOpacities = new float[opacityStops];

    for (int i = startIndex, j = 0; i < array.size(); i++) {
      if (i % 2 == 0) {
        opacityStopPositions[j] = array.get(i);
      } else {
        opacityStopOpacities[j] = array.get(i);
        j++;
      }
    }

    // Pre-SKIA (Oreo) devices render artifacts when there is two stops in the same position.
    // As a result, we have to de-dupe the merge color and opacity stop positions.
    float[] newPositions = mergeUniqueElements(gradientColor.getPositions(), opacityStopPositions);
    int newColorPoints = newPositions.length;
    int[] newColors = new int[newColorPoints];

    for (int i = 0; i < newColorPoints; i++) {
      float position = newPositions[i];
      int colorStopIndex = Arrays.binarySearch(colorStopPositions, position);
      int opacityIndex = Arrays.binarySearch(opacityStopPositions, position);
      if (colorStopIndex < 0 || opacityIndex > 0) {
        // This is a stop derived from an opacity stop.
        if (opacityIndex < 0) {
          // The formula here is derived from the return value for binarySearch. When an item isn't found, it returns -insertionPoint - 1.
          opacityIndex = -(opacityIndex + 1);
        }
        newColors[i] = getColorInBetweenColorStops(position, opacityStopOpacities[opacityIndex], colorStopPositions, colorStopColors);
      } else {
        // This os a step derived from a color stop.
        newColors[i] = getColorInBetweenOpacityStops(position, colorStopColors[colorStopIndex], opacityStopPositions, opacityStopOpacities);
      }
    }
    return new GradientColor(newPositions, newColors);
  }
This cheats a little bit.
Opacity stops can be at arbitrary intervals independent of color stops.
This uses the existing color stops and modifies the opacity at each existing color stop
based on what the opacity would be.
<p>
This should be a good approximation is nearly all cases. However, if there are many more
opacity stops than color stops, information will be lost.
lottie/src/main/java/com/airbnb/lottie/parser/GradientColorParser.java