package com.example.boidelov3; import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.os.VibrationEffect; import android.os.Vibrator; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; import androidx.core.content.ContextCompat; /** * Classe utilitaire pour les animations et effets visuels */ public class BoideloAnimationUtils { private static final Interpolator OVERSHOOT = new OvershootInterpolator(); /** * Anime le changement de couleur de fond d'une vue * * @param view La vue à animer * @param targetColor La couleur cible * @param duration La durée de l'animation en ms */ public static void animateBackgroundColor(View view, int targetColor, int duration) { if (view == null) return; int currentColor = getBackgroundColor(view); if (currentColor == targetColor) return; ValueAnimator anim = ValueAnimator.ofObject(new ArgbEvaluator(), currentColor, targetColor); anim.setDuration(duration); anim.addUpdateListener(animation -> { int color = (int) animation.getAnimatedValue(); view.setBackgroundColor(color); }); anim.start(); } /** * Anime le changement de couleur de fond avec la durée par défaut (500ms) */ public static void animateBackgroundColor(View view, int targetColor) { animateBackgroundColor(view, targetColor, 500); } /** * Obtient la couleur de fond actuelle d'une vue */ private static int getBackgroundColor(View view) { if (view.getBackground() instanceof ColorDrawable) { return ((ColorDrawable) view.getBackground()).getColor(); } return 0xFFFFFFFF; // Blanc par défaut } /** * Déclenche une vibration haptique * * @param context Le contexte * @param duration Durée de la vibration en ms */ public static void triggerHapticFeedback(Context context, int duration) { if (context == null) return; Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); if (vibrator != null && vibrator.hasVibrator()) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { VibrationEffect effect = VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE); vibrator.vibrate(effect); } else { vibrator.vibrate(duration); } } } /** * Déclenche une vibration haptique courte (100ms) */ public static void triggerHapticFeedback(Context context) { triggerHapticFeedback(context, 100); } /** * Déclenche une vibration haptique de succès */ public static void triggerSuccessHaptic(Context context) { if (context == null) return; Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); if (vibrator != null && vibrator.hasVibrator() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { long[] pattern = {0, 50, 50, 50}; VibrationEffect effect = VibrationEffect.createWaveform(pattern, -1); vibrator.vibrate(effect); } } /** * Déclenche une vibration haptique d'erreur */ public static void triggerErrorHaptic(Context context) { if (context == null) return; Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); if (vibrator != null && vibrator.hasVibrator() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { long[] pattern = {0, 100, 50, 100}; VibrationEffect effect = VibrationEffect.createWaveform(pattern, -1); vibrator.vibrate(effect); } } /** * Anime l'apparition d'une vue avec un effet de fade-in * * @param view La vue à animer * @param duration Durée de l'animation */ public static void fadeIn(View view, int duration) { if (view == null) return; view.setAlpha(0f); view.setVisibility(View.VISIBLE); view.animate() .alpha(1f) .setDuration(duration) .start(); } /** * Anime la disparition d'une vue avec un effet de fade-out * * @param view La vue à animer * @param duration Durée de l'animation */ public static void fadeOut(View view, int duration) { if (view == null) return; view.animate() .alpha(0f) .setDuration(duration) .withEndAction(() -> view.setVisibility(View.GONE)) .start(); } /** * Anime une vue avec un effet de scale * * @param view La vue à animer * @param scale Échelle cible (1.0 = normal, 0.5 = moitié) * @param duration Durée de l'animation */ public static void scale(View view, float scale, int duration) { if (view == null) return; view.animate() .scaleX(scale) .scaleY(scale) .setDuration(duration) .setInterpolator(OVERSHOOT) .start(); } /** * Anime une vue avec un effet de slide depuis le bas * * @param view La vue à animer * @param duration Durée de l'animation */ public static void slideUp(View view, int duration) { if (view == null) return; view.setTranslationY(view.getHeight()); view.setVisibility(View.VISIBLE); view.animate() .translationY(0f) .setDuration(duration) .setInterpolator(new android.view.animation.DecelerateInterpolator()) .start(); } /** * Applique une animation de pression à un bouton * * @param view La vue (bouton) à animer */ public static void applyButtonPressAnimation(View view) { if (view == null) return; view.setOnTouchListener((v, event) -> { switch (event.getAction()) { case android.view.MotionEvent.ACTION_DOWN: v.animate() .scaleX(0.95f) .scaleY(0.95f) .setDuration(100) .start(); return true; case android.view.MotionEvent.ACTION_UP: case android.view.MotionEvent.ACTION_CANCEL: v.animate() .scaleX(1f) .scaleY(1f) .setDuration(100) .start(); v.performClick(); return true; } return false; }); } /** * Anime une vue avec un effet de pulsation * * @param view La vue à animer * @param duration Durée d'un cycle de pulsation */ public static void pulse(View view, int duration) { if (view == null) return; view.animate() .scaleX(1.05f) .scaleY(1.05f) .setDuration(duration / 2) .withEndAction(() -> { view.animate() .scaleX(1f) .scaleY(1f) .setDuration(duration / 2) .start(); }) .start(); } /** * Anime une vue avec un effet de shake (tremblement) * * @param view La vue à animer */ public static void shake(View view) { if (view == null) return; Animation shake = AnimationUtils.loadAnimation(view.getContext(), R.anim.button_press); view.startAnimation(shake); } /** * Obtient la couleur depuis une ressource de couleur */ public static int getColorFromResource(Context context, int colorResId) { return ContextCompat.getColor(context, colorResId); } /** * Anime une vue avec un effet de bounce (rebond) * * @param view La vue à animer * @param duration Durée de l'animation */ public static void bounce(View view, int duration) { if (view == null) return; view.animate() .scaleY(0.8f) .scaleX(0.8f) .setDuration(duration / 2) .setInterpolator(new android.view.animation.DecelerateInterpolator()) .withEndAction(() -> { view.animate() .scaleY(1f) .scaleX(1f) .setDuration(duration / 2) .setInterpolator(OVERSHOOT) .start(); }) .start(); } /** * Anime une vue pour la supprimer (slide out + fade out) * * @param view La vue à animer * @param duration Durée de l'animation * @param endAction Action à exécuter après l'animation */ public static void slideOutToRemove(View view, int duration, Runnable endAction) { if (view == null) return; view.animate() .translationX(-view.getWidth()) .alpha(0f) .setDuration(duration) .setInterpolator(new android.view.animation.AccelerateInterpolator()) .withEndAction(endAction) .start(); } /** * Anime l'apparition d'une vue (slide in + fade in) * * @param view La vue à animer * @param duration Durée de l'animation */ public static void slideIn(View view, int duration) { if (view == null) return; view.setTranslationX(view.getWidth()); view.setAlpha(0f); view.setVisibility(View.VISIBLE); view.animate() .translationX(0f) .alpha(1f) .setDuration(duration) .setInterpolator(new android.view.animation.DecelerateInterpolator()) .start(); } /** * Anime une vue avec un effet de rotation * * @param view La vue à animer * @param degrees Angle de rotation en degrés * @param duration Durée de l'animation */ public static void rotate(View view, float degrees, int duration) { if (view == null) return; view.animate() .rotation(degrees) .setDuration(duration) .setInterpolator(new android.view.animation.DecelerateInterpolator()) .start(); } /** * Anime une vue avec un effet de wiggle (gauche-droite) * * @param view La vue à animer */ public static void wiggle(View view) { if (view == null) return; view.animate() .rotation(5f) .setDuration(50) .withEndAction(() -> { view.animate() .rotation(-5f) .setDuration(50) .withEndAction(() -> { view.animate() .rotation(3f) .setDuration(50) .withEndAction(() -> { view.animate() .rotation(0f) .setDuration(50) .start(); }) .start(); }) .start(); }) .start(); } /** * Anime une vue avec un effet de pop-in (apparition avec scale) * * @param view La vue à animer * @param duration Durée de l'animation */ public static void popIn(View view, int duration) { if (view == null) return; view.setScaleX(0f); view.setScaleY(0f); view.setAlpha(0f); view.setVisibility(View.VISIBLE); view.animate() .scaleX(1f) .scaleY(1f) .alpha(1f) .setDuration(duration) .setInterpolator(OVERSHOOT) .start(); } /** * Anime une vue avec un effet de pop-out (disparition avec scale) * * @param view La vue à animer * @param duration Durée de l'animation * @param endAction Action à exécuter après l'animation */ public static void popOut(View view, int duration, Runnable endAction) { if (view == null) return; view.animate() .scaleX(0f) .scaleY(0f) .alpha(0f) .setDuration(duration) .setInterpolator(new android.view.animation.AccelerateInterpolator()) .withEndAction(endAction) .start(); } }