code: package io.virtualapp.widgets;
  
  import android.animation.ValueAnimator;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.ColorFilter;
  import android.graphics.Paint;
  import android.graphics.PixelFormat;
  import android.graphics.Rect;
  import android.graphics.drawable.Animatable;
  import android.graphics.drawable.Drawable;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  
  
  public abstract class Indicator extends Drawable implements Animatable {
  
      private static final Rect ZERO_BOUNDS_RECT = new Rect();
      protected Rect drawBounds = ZERO_BOUNDS_RECT;
      private HashMap<ValueAnimator, ValueAnimator.AnimatorUpdateListener> mUpdateListeners = new HashMap<>();
      private ArrayList<ValueAnimator> mAnimators;
      private int alpha = 255;
      private boolean mHasAnimators;
  
      private Paint mPaint = new Paint();
  
      public Indicator() {
          mPaint.setColor(Color.WHITE);
          mPaint.setStyle(Paint.Style.FILL);
          mPaint.setAntiAlias(true);
      }
  
      public int getColor() {
          return mPaint.getColor();
      }
  
      public void setColor(int color) {
          mPaint.setColor(color);
      }
  
      @Override
      public int getAlpha() {
          return alpha;
      }
  
      @Override
      public void setAlpha(int alpha) {
          this.alpha = alpha;
      }
  
      @Override
      public int getOpacity() {
          return PixelFormat.OPAQUE;
      }
  
      @Override
      public void setColorFilter(ColorFilter colorFilter) {
  
      }
  
      @Override
      public void draw(Canvas canvas) {
          draw(canvas, mPaint);
      }
  
      public abstract void draw(Canvas canvas, Paint paint);
  
      public abstract ArrayList<ValueAnimator> onCreateAnimators();
  
      @Override
      public void start() {
          ensureAnimators();
  
          if (mAnimators == null) {
              return;
          }
  
          // If the animators has not ended, do nothing.
          if (isStarted()) {
              return;
          }
          startAnimators();
          invalidateSelf();
      }
  
      private void startAnimators() {
          for (int i = 0; i < mAnimators.size(); i++) {
              ValueAnimator animator = mAnimators.get(i);
  
              //when the animator restart , add the updateListener again because they
              // was removed by animator stop .
              ValueAnimator.AnimatorUpdateListener updateListener = mUpdateListeners.get(animator);
              if (updateListener != null) {
                  animator.addUpdateListener(updateListener);
              }
  
              animator.start();
          }
      }
  
      private void stopAnimators() {
          if (mAnimators != null) {
              for (ValueAnimator animator : mAnimators) {
                  if (animator != null && animator.isStarted()) {
                      animator.removeAllUpdateListeners();
                      animator.end();
                  }
              }
          }
      }
  
      private void ensureAnimators() {
          if (!mHasAnimators) {
              mAnimators = onCreateAnimators();
              mHasAnimators = true;
          }
      }
  
      @Override
      public void stop() {
          stopAnimators();
      }
  
      private boolean isStarted() {
          for (ValueAnimator animator : mAnimators) {
              return animator.isStarted();
          }
          return false;
      }
  
      @Override
      public boolean isRunning() {
          for (ValueAnimator animator : mAnimators) {
              return animator.isRunning();
          }
          return false;
      }
  
      /**
       * Your should use this to add AnimatorUpdateListener when
       * create animator , otherwise , animator doesn't work when
       * the animation restart .
       *
       * @param updateListener
       */
      public void addUpdateListener(ValueAnimator animator, ValueAnimator.AnimatorUpdateListener updateListener) {
          mUpdateListeners.put(animator, updateListener);
      }
  
      @Override
      protected void onBoundsChange(Rect bounds) {
          super.onBoundsChange(bounds);
          setDrawBounds(bounds);
      }
  
      public void setDrawBounds(int left, int top, int right, int bottom) {
          this.drawBounds = new Rect(left, top, right, bottom);
      }
  
      public void postInvalidate() {
          invalidateSelf();
      }
  
      public Rect getDrawBounds() {
          return drawBounds;
      }
  
      public void setDrawBounds(Rect drawBounds) {
          setDrawBounds(drawBounds.left, drawBounds.top, drawBounds.right, drawBounds.bottom);
      }
  
      public int getWidth() {
          return drawBounds.width();
      }
  
      public int getHeight() {
          return drawBounds.height();
      }
  
      public int centerX() {
          return drawBounds.centerX();
      }
  
      public int centerY() {
          return drawBounds.centerY();
      }
  
      public float exactCenterX() {
          return drawBounds.exactCenterX();
      }
  
      public float exactCenterY() {
          return drawBounds.exactCenterY();
      }
  
  }