// Path: indicator/src/main/java/com/fuzz/indicator/style/MigratoryRange.java
// public final class MigratoryRange<T extends Number & Comparable<T>> {
// 
//     public static abstract class Operator<T> {
//         @NonNull
//         public abstract T add(T lower, T offset);
//     }
// 
//     public static final Operator<Float> FLOAT_OPERATOR
//             = new Operator<Float>() {
//         @NonNull
//         @Override
//         public Float add(Float lower, Float offset) {
//             return lower + offset;
//         }
//     };
// 
//     public static final Operator<Integer> INTEGER_OPERATOR
//             = new Operator<Integer>() {
//         @NonNull
//         @Override
//         public Integer add(Integer lower, Integer offset) {
//             return lower + offset;
//         }
//     };
// 
//     public static MigratoryRange<Float> from(float lower, float upper) {
//         return new MigratoryRange<>(lower, upper, FLOAT_OPERATOR);
//     }
// 
//     public static MigratoryRange<Integer> from(int lower, int upper) {
//         return new MigratoryRange<>(lower, upper, INTEGER_OPERATOR);
//     }
// 
//     @NonNull
//     private final T lower;
//     @NonNull
//     private final T upper;
//     @NonNull
//     private final Operator<T> operator;
// 
//     public MigratoryRange(@NonNull T lower, @NonNull T upper, @NonNull Operator<T> operator) {
//         this.lower = lower;
//         this.upper = upper;
//         this.operator = operator;
// 
//         if (lower.compareTo(upper) > 0) {
//             throw new IllegalArgumentException("Migratory ranges may not have an upper bound below their lower bound.");
//         }
//     }
// 
//     @NonNull
//     public T getLower() {
//         return lower;
//     }
// 
//     @NonNull
//     public T getUpper() {
//         return upper;
//     }
// 
//     /**
//      * If the parameter is within this range it will be
//      * returned unaltered. If it isn't, the closest value
//      * within the range (e.g. {@link #getLower()} or
//      * {@link #getUpper()}) will be returned.
//      *
//      * @param value any non-null value of type T
//      * @return a value guaranteed to be within this range
//      */
//     @NonNull
//     public T clamp(@NonNull T value) {
//         if (value.compareTo(lower) < 0) {
//             return lower;
//         } else if (value.compareTo(upper) > 0) {
//             return upper;
//         } else {
//             return value;
//         }
//     }
// 
//     public double diff() {
//         return upper.doubleValue() - lower.doubleValue();
//     }
// 
//     public MigratoryRange<T> translate(@NonNull T offset) {
//         T newLower = operator.add(lower, offset);
//         T newUpper = operator.add(upper, offset);
//         return new MigratoryRange<>(newLower, newUpper, operator);
//     }
// }
// 
// Path: indicator/src/main/java/com/fuzz/indicator/style/MigratorySpan.java
// public interface MigratorySpan {
// 
//     /**
//      * Consider a Spannable sequence which has a defined start and end.
//      * The MigratoryRange returned by this method represents some portion
//      * of indices within that sequence. Implementations are asked to
//      * <i>NOT</i> modify the parameter within this method.
//      * <p>
//      *     Sample caller code may be as follows:
//      *     <pre>
//      * MigratorySpan ms;
//      * Spannable target;
//      *
//      * //...
//      *
//      * MigratoryRange&lt;Integer&gt; bounds = ms.getCoverage(target);
//      *
//      * int start = bounds.getLower();
//      * int end = bounds.getUpper();
//      *
//      * target.setSpan(ms, start, end, ms.preferredFlags(0));
//      *     </pre>
//      * </p>
//      *
//      * @return a range which can be used to figure out what
//      * characters in the Spannable are covered.
//      * @param enclosingSequence    the sequence of characters wherein this span
//      */
//     @NonNull
//     MigratoryRange<Integer> getCoverage(Spannable enclosingSequence);
// 
//     /**
//      * Whatever is returned here should be a valid argument into
//      * {@link android.text.Spannable#setSpan(Object, int, int, int)}'s
//      * {@code flags} argument. Feel free to return the parameter directly
//      * if they don't need to change.
//      *
//      * @return a combination of valid span-laying-out flags
//      * @param previousFlags    flags used for the previous layout - will
//      *                         be 0 if not currently attached to a
//      *                         {@link android.text.Spannable Spannable}
//      */
//     int preferredFlags(int previousFlags);
// }


import android.graphics.Matrix;
import android.os.Build;
import android.support.annotation.NonNull;
import android.text.Spannable;
import android.util.Property;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.fuzz.indicator.style.MigratoryRange;
import com.fuzz.indicator.style.MigratorySpan;
import static android.os.Build.VERSION_CODES.KITKAT;
        setAlpha.set(imageView, fraction);
    }

    public static void offsetImageScaleBy(@NonNull ImageView imageView, float fraction) {
        Matrix matrix = new Matrix(imageView.getImageMatrix());
        matrix.preScale(fraction, fraction);
    }

    public static void offsetScaleBy(@NonNull View imageView, float fraction) {
        imageView.setScaleX(fraction);
        imageView.setScaleY(fraction);
    }

    /**
     * Ensures that all spans of type {@link MigratorySpan} within {@code spannable} are
     * translated a proportional quantity of characters from their baseline position. Each
     * span's implementation of {@link MigratorySpan#getCoverage(Spannable)} is responsible
     * for reporting the correct baseline coverage prior to translation.
     *
     * @param spannable      the text to which the spans are attached
     * @param fraction       what proportion of the spannable should be considered offset.
     *                       values outside the range of 0..1 will be clamped into that
     *                       range.
     */
    public static void offsetSpansBy(@NonNull Spannable spannable, float fraction) {
        int length = spannable.length();
        int offset = (int) (fraction * length);

        MigratorySpan[] knownSpans = spannable.getSpans(0, length, MigratorySpan.class);
        if (knownSpans.length > 0) {
// Path: indicator/src/main/java/com/fuzz/indicator/style/MigratoryRange.java
// public final class MigratoryRange<T extends Number & Comparable<T>> {
// 
//     public static abstract class Operator<T> {
//         @NonNull
//         public abstract T add(T lower, T offset);
//     }
// 
//     public static final Operator<Float> FLOAT_OPERATOR
//             = new Operator<Float>() {
//         @NonNull
//         @Override
//         public Float add(Float lower, Float offset) {
//             return lower + offset;
//         }
//     };
// 
//     public static final Operator<Integer> INTEGER_OPERATOR
//             = new Operator<Integer>() {
//         @NonNull
//         @Override
//         public Integer add(Integer lower, Integer offset) {
//             return lower + offset;
//         }
//     };
// 
//     public static MigratoryRange<Float> from(float lower, float upper) {
//         return new MigratoryRange<>(lower, upper, FLOAT_OPERATOR);
//     }
// 
//     public static MigratoryRange<Integer> from(int lower, int upper) {
//         return new MigratoryRange<>(lower, upper, INTEGER_OPERATOR);
//     }
// 
//     @NonNull
//     private final T lower;
//     @NonNull
//     private final T upper;
//     @NonNull
//     private final Operator<T> operator;
// 
//     public MigratoryRange(@NonNull T lower, @NonNull T upper, @NonNull Operator<T> operator) {
//         this.lower = lower;
//         this.upper = upper;
//         this.operator = operator;
// 
//         if (lower.compareTo(upper) > 0) {
//             throw new IllegalArgumentException("Migratory ranges may not have an upper bound below their lower bound.");
//         }
//     }
// 
//     @NonNull
//     public T getLower() {
//         return lower;
//     }
// 
//     @NonNull
//     public T getUpper() {
//         return upper;
//     }
// 
//     /**
//      * If the parameter is within this range it will be
//      * returned unaltered. If it isn't, the closest value
//      * within the range (e.g. {@link #getLower()} or
//      * {@link #getUpper()}) will be returned.
//      *
//      * @param value any non-null value of type T
//      * @return a value guaranteed to be within this range
//      */
//     @NonNull
//     public T clamp(@NonNull T value) {
//         if (value.compareTo(lower) < 0) {
//             return lower;
//         } else if (value.compareTo(upper) > 0) {
//             return upper;
//         } else {
//             return value;
//         }
//     }
// 
//     public double diff() {
//         return upper.doubleValue() - lower.doubleValue();
//     }
// 
//     public MigratoryRange<T> translate(@NonNull T offset) {
//         T newLower = operator.add(lower, offset);
//         T newUpper = operator.add(upper, offset);
//         return new MigratoryRange<>(newLower, newUpper, operator);
//     }
// }
// 
// Path: indicator/src/main/java/com/fuzz/indicator/style/MigratorySpan.java
// public interface MigratorySpan {
// 
//     /**
//      * Consider a Spannable sequence which has a defined start and end.
//      * The MigratoryRange returned by this method represents some portion
//      * of indices within that sequence. Implementations are asked to
//      * <i>NOT</i> modify the parameter within this method.
//      * <p>
//      *     Sample caller code may be as follows:
//      *     <pre>
//      * MigratorySpan ms;
//      * Spannable target;
//      *
//      * //...
//      *
//      * MigratoryRange&lt;Integer&gt; bounds = ms.getCoverage(target);
//      *
//      * int start = bounds.getLower();
//      * int end = bounds.getUpper();
//      *
//      * target.setSpan(ms, start, end, ms.preferredFlags(0));
//      *     </pre>
//      * </p>
//      *
//      * @return a range which can be used to figure out what
//      * characters in the Spannable are covered.
//      * @param enclosingSequence    the sequence of characters wherein this span
//      */
//     @NonNull
//     MigratoryRange<Integer> getCoverage(Spannable enclosingSequence);
// 
//     /**
//      * Whatever is returned here should be a valid argument into
//      * {@link android.text.Spannable#setSpan(Object, int, int, int)}'s
//      * {@code flags} argument. Feel free to return the parameter directly
//      * if they don't need to change.
//      *
//      * @return a combination of valid span-laying-out flags
//      * @param previousFlags    flags used for the previous layout - will
//      *                         be 0 if not currently attached to a
//      *                         {@link android.text.Spannable Spannable}
//      */
//     int preferredFlags(int previousFlags);
// }


// Path: indicator/src/main/java/com/fuzz/indicator/cell/OffSetters.java
import android.graphics.Matrix;
import android.os.Build;
import android.support.annotation.NonNull;
import android.text.Spannable;
import android.util.Property;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.fuzz.indicator.style.MigratoryRange;
import com.fuzz.indicator.style.MigratorySpan;
import static android.os.Build.VERSION_CODES.KITKAT;

        setAlpha.set(imageView, fraction);
    }

    public static void offsetImageScaleBy(@NonNull ImageView imageView, float fraction) {
        Matrix matrix = new Matrix(imageView.getImageMatrix());
        matrix.preScale(fraction, fraction);
    }

    public static void offsetScaleBy(@NonNull View imageView, float fraction) {
        imageView.setScaleX(fraction);
        imageView.setScaleY(fraction);
    }

    /**
     * Ensures that all spans of type {@link MigratorySpan} within {@code spannable} are
     * translated a proportional quantity of characters from their baseline position. Each
     * span's implementation of {@link MigratorySpan#getCoverage(Spannable)} is responsible
     * for reporting the correct baseline coverage prior to translation.
     *
     * @param spannable      the text to which the spans are attached
     * @param fraction       what proportion of the spannable should be considered offset.
     *                       values outside the range of 0..1 will be clamped into that
     *                       range.
     */
    public static void offsetSpansBy(@NonNull Spannable spannable, float fraction) {
        int length = spannable.length();
        int offset = (int) (fraction * length);

        MigratorySpan[] knownSpans = spannable.getSpans(0, length, MigratorySpan.class);
        if (knownSpans.length > 0) {
            MigratoryRange<Integer> fullSize = MigratoryRange.from(0, length);