// Path: demo/src/main/java/additive_animations/helper/DpConverter.java
// public class DpConverter {
// 
//     public static int converDpToPx(float dp) {
//         float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, AAApplication.getContext().getResources().getDisplayMetrics());
//         return Math.round(px);
//     }
// }
// 
// Path: additive_animations/src/main/java/at/wirecube/additiveanimations/additive_animator/AdditiveAnimator.java
// public final class AdditiveAnimator extends SubclassableAdditiveViewAnimator<AdditiveAnimator> {
// 
//     /**
//      * This is just a convenience method when you need to animate a single view.
//      * No state is kept in individual AdditiveAnimator instances, so you don't need to keep a reference to it.
//      * @param view The view to animate.
//      * @return A new instance of AdditiveAnimator with `target` set to `view`.
//      */
//     public static AdditiveAnimator animate(View view) {
//         return new AdditiveAnimator(view);
//     }
// 
//     public static AdditiveAnimator animate(View... views) {
//         return new AdditiveAnimator().targets(views);
//     }
// 
//     public static AdditiveAnimator animate(List<View> views, long stagger) {
//         return new AdditiveAnimator().targets(views, stagger);
//     }
// 
//     public static AdditiveAnimator animate(List<View> views, long duration, long stagger) {
//         return new AdditiveAnimator(duration).targets(views, stagger);
//     }
// 
//     /**
//      * This is just a convenience method when you need to animate a single view.
//      * No state is kept in individual AdditiveAnimator instances, so you don't need to keep a reference to it.
//      * @param view The view to animate.
//      * @param duration The animation duration.
//      * @return A new instance of AdditiveAnimator with the animation target set to `view` and the animationDuration set to `duration`.
//      */
//     public static AdditiveAnimator animate(View view, long duration) {
//         return new AdditiveAnimator(view).setDuration(duration);
//     }
// 
//     @Override
//     protected AdditiveAnimator newInstance() {
//         return new AdditiveAnimator();
//     }
// 
//     protected AdditiveAnimator(View view) {
//         target(view);
//     }
// 
//     /**
//      * Creates a new AdditiveAnimator instance without a target view.
//      * You **must** call `target(View v)` before calling one of the animation methods.
//      */
//     public AdditiveAnimator() {}
// 
//     /**
//      * Creates a new AdditiveAnimator instance with the specified animation duration for more convenience.
//      * You **must** call `target(View v)` before calling one of the animation methods.
//      */
//     public AdditiveAnimator(long duration) {
//         setDuration(duration);
//     }
// 
// }
// 
// Path: demo/src/main/java/additive_animations/subclass/AdditiveAnimatorSubclassDemo.java
// public class AdditiveAnimatorSubclassDemo extends SubclassableAdditiveViewAnimator<AdditiveAnimatorSubclassDemo> {
// 
//     public static AdditiveAnimatorSubclassDemo animate(View v) {
//         return new AdditiveAnimatorSubclassDemo().target(v);
//     }
// 
//     public AdditiveAnimatorSubclassDemo() { super(); }
// 
//     @Override
//     protected AdditiveAnimatorSubclassDemo newInstance() {
//         return new AdditiveAnimatorSubclassDemo();
//     }
// 
//     static boolean isPulsing = false;
// 
//     public void startPulsing() {
//         setDuration(3000);
//         getCurrentTarget().setScaleX(0);
//         getCurrentTarget().setScaleY(0);
//         getCurrentTarget().setAlpha(1f);
//         addEndAction(new AnimationEndListener() {
//             @Override
//             public void onAnimationEnd(boolean wasCancelled) {
//                 if (isPulsing) {
//                     AdditiveAnimatorSubclassDemo.animate(getCurrentTarget()).startPulsing();
//                 }
//             }
//         });
//         alpha(0);
//         scale(1);
//         start();
//         isPulsing = true;
//     }
// 
//     public void stopPulsing() {
//         isPulsing = false;
//     }
// 
// 
//     public AdditiveAnimatorSubclassDemo thenBounceBeforeEnd(int millis, long duration) {
//         return thenBeforeEnd(millis).scale(1.2f).setDuration(duration)
//                .thenBeforeEnd(100).scale(0.8f)
//                .thenBeforeEnd(100).scale(1f);
//     }
// }


import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import additive_animations.helper.DpConverter;
import at.wirecube.additiveanimations.additive_animator.AdditiveAnimator;
import additive_animations.subclass.AdditiveAnimatorSubclassDemo;
import at.wirecube.additiveanimations.additiveanimationsdemo.R;
                getResources().getColor(R.color.niceOrange),
                getResources().getColor(R.color.niceBlue),
                getResources().getColor(R.color.niceGreen),
                getResources().getColor(R.color.nicePink)
        };
        // TODO: don't use hardcoded px values
        AdditiveAnimatorSubclassDemo.animate(animatedView)
                .x(px(50)).y(px(100)).backgroundColor(colors[1]).rotation(0)
                .thenBounceBeforeEnd(800, 300)
                .thenBeforeEnd(400).x(px(250)).backgroundColor(colors[2]).rotationBy(45).setDuration(1000)
                .thenBounceBeforeEnd(800, 300)
                .thenBeforeEnd(400).y(px(500)).backgroundColor(colors[3]).rotationBy(45).setDuration(1000)
                .thenBounceBeforeEnd(800, 300)
                .thenBeforeEnd(400).x(px(50)).backgroundColor(colors[0]).rotationBy(90).setDuration(1000)
                .thenBounceBeforeEnd(800, 300)
                .addEndAction(wasCancelled -> {
                    if (getActivity() != null) {
                        animate();
                    }
                })
                .start();
    }

    private int px(int dp) {
        return DpConverter.converDpToPx(dp);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
// Path: demo/src/main/java/additive_animations/helper/DpConverter.java
// public class DpConverter {
// 
//     public static int converDpToPx(float dp) {
//         float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, AAApplication.getContext().getResources().getDisplayMetrics());
//         return Math.round(px);
//     }
// }
// 
// Path: additive_animations/src/main/java/at/wirecube/additiveanimations/additive_animator/AdditiveAnimator.java
// public final class AdditiveAnimator extends SubclassableAdditiveViewAnimator<AdditiveAnimator> {
// 
//     /**
//      * This is just a convenience method when you need to animate a single view.
//      * No state is kept in individual AdditiveAnimator instances, so you don't need to keep a reference to it.
//      * @param view The view to animate.
//      * @return A new instance of AdditiveAnimator with `target` set to `view`.
//      */
//     public static AdditiveAnimator animate(View view) {
//         return new AdditiveAnimator(view);
//     }
// 
//     public static AdditiveAnimator animate(View... views) {
//         return new AdditiveAnimator().targets(views);
//     }
// 
//     public static AdditiveAnimator animate(List<View> views, long stagger) {
//         return new AdditiveAnimator().targets(views, stagger);
//     }
// 
//     public static AdditiveAnimator animate(List<View> views, long duration, long stagger) {
//         return new AdditiveAnimator(duration).targets(views, stagger);
//     }
// 
//     /**
//      * This is just a convenience method when you need to animate a single view.
//      * No state is kept in individual AdditiveAnimator instances, so you don't need to keep a reference to it.
//      * @param view The view to animate.
//      * @param duration The animation duration.
//      * @return A new instance of AdditiveAnimator with the animation target set to `view` and the animationDuration set to `duration`.
//      */
//     public static AdditiveAnimator animate(View view, long duration) {
//         return new AdditiveAnimator(view).setDuration(duration);
//     }
// 
//     @Override
//     protected AdditiveAnimator newInstance() {
//         return new AdditiveAnimator();
//     }
// 
//     protected AdditiveAnimator(View view) {
//         target(view);
//     }
// 
//     /**
//      * Creates a new AdditiveAnimator instance without a target view.
//      * You **must** call `target(View v)` before calling one of the animation methods.
//      */
//     public AdditiveAnimator() {}
// 
//     /**
//      * Creates a new AdditiveAnimator instance with the specified animation duration for more convenience.
//      * You **must** call `target(View v)` before calling one of the animation methods.
//      */
//     public AdditiveAnimator(long duration) {
//         setDuration(duration);
//     }
// 
// }
// 
// Path: demo/src/main/java/additive_animations/subclass/AdditiveAnimatorSubclassDemo.java
// public class AdditiveAnimatorSubclassDemo extends SubclassableAdditiveViewAnimator<AdditiveAnimatorSubclassDemo> {
// 
//     public static AdditiveAnimatorSubclassDemo animate(View v) {
//         return new AdditiveAnimatorSubclassDemo().target(v);
//     }
// 
//     public AdditiveAnimatorSubclassDemo() { super(); }
// 
//     @Override
//     protected AdditiveAnimatorSubclassDemo newInstance() {
//         return new AdditiveAnimatorSubclassDemo();
//     }
// 
//     static boolean isPulsing = false;
// 
//     public void startPulsing() {
//         setDuration(3000);
//         getCurrentTarget().setScaleX(0);
//         getCurrentTarget().setScaleY(0);
//         getCurrentTarget().setAlpha(1f);
//         addEndAction(new AnimationEndListener() {
//             @Override
//             public void onAnimationEnd(boolean wasCancelled) {
//                 if (isPulsing) {
//                     AdditiveAnimatorSubclassDemo.animate(getCurrentTarget()).startPulsing();
//                 }
//             }
//         });
//         alpha(0);
//         scale(1);
//         start();
//         isPulsing = true;
//     }
// 
//     public void stopPulsing() {
//         isPulsing = false;
//     }
// 
// 
//     public AdditiveAnimatorSubclassDemo thenBounceBeforeEnd(int millis, long duration) {
//         return thenBeforeEnd(millis).scale(1.2f).setDuration(duration)
//                .thenBeforeEnd(100).scale(0.8f)
//                .thenBeforeEnd(100).scale(1f);
//     }
// }


// Path: demo/src/main/java/additive_animations/fragments/RepeatingChainedAnimationsDemoFragment.java
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import additive_animations.helper.DpConverter;
import at.wirecube.additiveanimations.additive_animator.AdditiveAnimator;
import additive_animations.subclass.AdditiveAnimatorSubclassDemo;
import at.wirecube.additiveanimations.additiveanimationsdemo.R;

                getResources().getColor(R.color.niceOrange),
                getResources().getColor(R.color.niceBlue),
                getResources().getColor(R.color.niceGreen),
                getResources().getColor(R.color.nicePink)
        };
        // TODO: don't use hardcoded px values
        AdditiveAnimatorSubclassDemo.animate(animatedView)
                .x(px(50)).y(px(100)).backgroundColor(colors[1]).rotation(0)
                .thenBounceBeforeEnd(800, 300)
                .thenBeforeEnd(400).x(px(250)).backgroundColor(colors[2]).rotationBy(45).setDuration(1000)
                .thenBounceBeforeEnd(800, 300)
                .thenBeforeEnd(400).y(px(500)).backgroundColor(colors[3]).rotationBy(45).setDuration(1000)
                .thenBounceBeforeEnd(800, 300)
                .thenBeforeEnd(400).x(px(50)).backgroundColor(colors[0]).rotationBy(90).setDuration(1000)
                .thenBounceBeforeEnd(800, 300)
                .addEndAction(wasCancelled -> {
                    if (getActivity() != null) {
                        animate();
                    }
                })
                .start();
    }

    private int px(int dp) {
        return DpConverter.converDpToPx(dp);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        AdditiveAnimator.cancelAnimationsForObject(animatedView);