// Path: htextview-library/src/main/java/com/hanks/htextview/util/CharacterUtils.java
// public class CharacterUtils {
// 
//     public static List<CharacterDiffResult> diff(CharSequence oldText, CharSequence newText) {
// 
//         List<CharacterDiffResult> differentList = new ArrayList<>();
//         Set<Integer> skip = new HashSet<>();
// 
//         for (int i = 0; i < oldText.length(); i++) {
//             char c = oldText.charAt(i);
//             for (int j = 0; j < newText.length(); j++) {
//                 if (!skip.contains(j) && c == newText.charAt(j)) {
//                     skip.add(j);
//                     CharacterDiffResult different = new CharacterDiffResult();
//                     different.c = c;
//                     different.fromIndex = i;
//                     different.moveIndex = j;
//                     differentList.add(different);
//                     break;
//                 }
//             }
//         }
//         return differentList;
//     }
// 
//     public static int needMove(int index, List<CharacterDiffResult> differentList) {
//         for (CharacterDiffResult different : differentList) {
//             if (different.fromIndex == index) {
//                 return different.moveIndex;
//             }
//         }
//         return -1;
//     }
// 
//     public static boolean stayHere(int index, List<CharacterDiffResult> differentList) {
//         for (CharacterDiffResult different : differentList) {
//             if (different.moveIndex == index) {
//                 return true;
//             }
//         }
//         return false;
//     }
// 
//     /**
//      * @return
//      */
//     public static float getOffset(int from, int move, float progress, float startX, float oldStartX, float[] gaps, float[] oldGaps) {
// 
//         float dist = startX;
//         for (int i = 0; i < move; i++) {
//             dist += gaps[i];
//         }
// 
//         float cur = oldStartX;
//         for (int i = 0; i < from; i++) {
//             cur += oldGaps[i];
//         }
// 
//         return cur + (dist - cur) * progress;
// 
//     }
// 
// }


import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.view.animation.AccelerateDecelerateInterpolator;
import com.hanks.htextview.util.CharacterUtils;
        n = n <= 0 ? 1 : n;
        duration = (long) (charTime + charTime / mostCount * (n - 1));

        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, duration).setDuration(duration);
        valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override public void onAnimationUpdate(ValueAnimator animation) {
                progress = (float) animation.getAnimatedValue();
                mHTextView.invalidate();
            }
        });
        valueAnimator.start();
    }

    @Override protected void animatePrepare(CharSequence text) {

    }

    @Override public void drawFrame(Canvas canvas) {
        float offset = startX;
        float oldOffset = oldStartX;

        int maxLength = Math.max(mText.length(), mOldText.length());

        for (int i = 0; i < maxLength; i++) {

            // draw old text
            if (i < mOldText.length()) {

                float percent = progress / duration;
// Path: htextview-library/src/main/java/com/hanks/htextview/util/CharacterUtils.java
// public class CharacterUtils {
// 
//     public static List<CharacterDiffResult> diff(CharSequence oldText, CharSequence newText) {
// 
//         List<CharacterDiffResult> differentList = new ArrayList<>();
//         Set<Integer> skip = new HashSet<>();
// 
//         for (int i = 0; i < oldText.length(); i++) {
//             char c = oldText.charAt(i);
//             for (int j = 0; j < newText.length(); j++) {
//                 if (!skip.contains(j) && c == newText.charAt(j)) {
//                     skip.add(j);
//                     CharacterDiffResult different = new CharacterDiffResult();
//                     different.c = c;
//                     different.fromIndex = i;
//                     different.moveIndex = j;
//                     differentList.add(different);
//                     break;
//                 }
//             }
//         }
//         return differentList;
//     }
// 
//     public static int needMove(int index, List<CharacterDiffResult> differentList) {
//         for (CharacterDiffResult different : differentList) {
//             if (different.fromIndex == index) {
//                 return different.moveIndex;
//             }
//         }
//         return -1;
//     }
// 
//     public static boolean stayHere(int index, List<CharacterDiffResult> differentList) {
//         for (CharacterDiffResult different : differentList) {
//             if (different.moveIndex == index) {
//                 return true;
//             }
//         }
//         return false;
//     }
// 
//     /**
//      * @return
//      */
//     public static float getOffset(int from, int move, float progress, float startX, float oldStartX, float[] gaps, float[] oldGaps) {
// 
//         float dist = startX;
//         for (int i = 0; i < move; i++) {
//             dist += gaps[i];
//         }
// 
//         float cur = oldStartX;
//         for (int i = 0; i < from; i++) {
//             cur += oldGaps[i];
//         }
// 
//         return cur + (dist - cur) * progress;
// 
//     }
// 
// }


// Path: htextview-library/src/main/java/com/hanks/htextview/animatetext/ScaleText.java
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.view.animation.AccelerateDecelerateInterpolator;
import com.hanks.htextview.util.CharacterUtils;

        n = n <= 0 ? 1 : n;
        duration = (long) (charTime + charTime / mostCount * (n - 1));

        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, duration).setDuration(duration);
        valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override public void onAnimationUpdate(ValueAnimator animation) {
                progress = (float) animation.getAnimatedValue();
                mHTextView.invalidate();
            }
        });
        valueAnimator.start();
    }

    @Override protected void animatePrepare(CharSequence text) {

    }

    @Override public void drawFrame(Canvas canvas) {
        float offset = startX;
        float oldOffset = oldStartX;

        int maxLength = Math.max(mText.length(), mOldText.length());

        for (int i = 0; i < maxLength; i++) {

            // draw old text
            if (i < mOldText.length()) {

                float percent = progress / duration;
                int move = CharacterUtils.needMove(i, differentList);