content: package com.alexvasilkov.gestures.internal;
  
  import android.content.Context;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.Matrix;
  import android.graphics.Paint;
  import android.graphics.Rect;
  import android.graphics.RectF;
  import android.graphics.Typeface;
  import android.view.View;
  
  import androidx.annotation.NonNull;
  
  import com.alexvasilkov.gestures.GestureController;
  import com.alexvasilkov.gestures.GestureController.StateSource;
  import com.alexvasilkov.gestures.Settings;
  import com.alexvasilkov.gestures.animation.ViewPositionAnimator;
  import com.alexvasilkov.gestures.utils.GravityUtils;
  import com.alexvasilkov.gestures.views.interfaces.AnimatorView;
  import com.alexvasilkov.gestures.views.interfaces.GestureView;
  
  import java.lang.reflect.Field;
  import java.util.Locale;
  
  public class DebugOverlay {
  
      private static final float STROKE_WIDTH = 2f;
      private static final float TEXT_SIZE = 16f;
  
      private static final Paint paint = new Paint();
      private static final RectF rectF = new RectF();
      private static final Rect rect = new Rect();
      private static final Matrix matrix = new Matrix();
  
      private static Field stateSourceField;
  
      private DebugOverlay() {}
  
      public static void drawDebug(@NonNull View view, @NonNull Canvas canvas) {
          final GestureController controller = ((GestureView) view).getController();
          final ViewPositionAnimator animator = ((AnimatorView) view).getPositionAnimator();
          final Settings settings = controller.getSettings();
          final Context context = view.getContext();
          final float stroke = UnitsUtils.toPixels(context, STROKE_WIDTH);
          final float textSize = UnitsUtils.toPixels(context, TEXT_SIZE);
  
          canvas.save();
          canvas.translate(view.getPaddingLeft(), view.getPaddingTop());
  
          // Viewport
          rectF.set(0f, 0f, settings.getViewportW(), settings.getViewportH());
          drawRect(canvas, rectF, Color.GRAY, stroke);
  
          // Movement area
          GravityUtils.getMovementAreaPosition(settings, rect);
          rectF.set(rect);
          drawRect(canvas, rectF, Color.GREEN, stroke);
  
          // Image bounds with rotation
          controller.getState().get(matrix);
          canvas.save();
          canvas.concat(matrix);
          rectF.set(0f, 0f, settings.getImageW(), settings.getImageH());
          drawRect(canvas, rectF, Color.YELLOW, stroke / controller.getState().getZoom());
          canvas.restore();
  
          // Image bounds
          rectF.set(0f, 0f, settings.getImageW(), settings.getImageH());
          controller.getState().get(matrix);
          matrix.mapRect(rectF);
          drawRect(canvas, rectF, Color.RED, stroke);
  
          // State source
          float pos = animator.getPosition();
          if (pos == 1f || (pos == 0f && animator.isLeaving())) {
              final StateSource source = getStateSource(controller);
  
              drawText(canvas, settings, source.name(), Color.CYAN, textSize);
  
              if (source != StateSource.NONE) {
                  view.invalidate();
              }
          } else if (pos > 0f) {
              String direction = animator.isLeaving() ? "EXIT" : "ENTER";
              String text = String.format(Locale.US, "%s %.0f%%", direction, pos * 100f);
              drawText(canvas, settings, text, Color.MAGENTA, textSize);
          }
  
          canvas.restore();
      }
  
      @SuppressWarnings("SameParameterValue")
      private static void drawRect(Canvas canvas, RectF rect, int color, float stroke) {
          paint.setStyle(Paint.Style.STROKE);
          paint.setStrokeWidth(stroke);
          rectF.inset(0.5f * stroke, 0.5f * stroke);
          paint.setColor(color);
          canvas.drawRect(rect, paint);
      }
  
      private static void drawText(Canvas canvas, Settings settings, String text,
              int color, float textSize) {
          // Text settings
          paint.setTextSize(textSize);
          paint.setTypeface(Typeface.MONOSPACE);
          paint.setTextAlign(Paint.Align.CENTER);
  
          final float halfSize = 0.5f * textSize;
  
          // Computing text background
          paint.getTextBounds(text, 0, text.length(), rect);
          rectF.set(rect);
          rectF.offset(-rectF.centerX(), -rectF.centerY());
          GravityUtils.getMovementAreaPosition(settings, rect);
          rectF.offset(rect.centerX(), rect.centerY());
          rectF.inset(-halfSize, -halfSize);
  
          // Drawing background
          paint.setStyle(Paint.Style.FILL);
          paint.setColor(Color.WHITE);
          canvas.drawRoundRect(rectF, halfSize, halfSize, paint);
          paint.setStyle(Paint.Style.STROKE);
          paint.setColor(Color.GRAY);
          canvas.drawRoundRect(rectF, halfSize, halfSize, paint);
  
          // Drawing text
          paint.setStyle(Paint.Style.FILL);
          paint.setColor(color);
          canvas.drawText(text, rectF.centerX(), rectF.bottom - halfSize, paint);
      }
  
  
      private static StateSource getStateSource(GestureController controller) {
          // We can't have public API for state source in GestureController,
          // since it is only make sense to get it through corresponding listener.
          // Getting field through reflection for debug purpose does not seem to be very bad.
          if (stateSourceField == null) {
              try {
                  stateSourceField = GestureController.class.getDeclaredField("stateSource");
                  stateSourceField.setAccessible(true);
              } catch (Exception ignored) {
              }
          }
  
          if (stateSourceField != null) {
              try {
                  return (StateSource) stateSourceField.get(controller);
              } catch (Exception ignored) {
              }
          }
  
          return StateSource.NONE;
      }
  
  }
  