gt:         thumbAnimation = new AnimatorSet();
context: /*
   * This is the source code of Telegram for Android v. 5.x.x
   * It is licensed under GNU GPL v. 2 or later.
   * You should have received a copy of the license in this archive (see LICENSE).
   *
   * Copyright Nikolai Kudashov, 2013-2018.
   */
  
  package org.telegram.ui.Components;
  
  import android.animation.Animator;
  import android.animation.AnimatorListenerAdapter;
  import android.animation.AnimatorSet;
  import android.animation.ObjectAnimator;
  import android.content.Context;
  import android.graphics.Bitmap;
  import android.graphics.Canvas;
  import android.graphics.Paint;
  import android.graphics.RectF;
  import android.graphics.drawable.Drawable;
  import android.os.Build;
  import android.util.Property;
  import android.view.Gravity;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.animation.OvershootInterpolator;
  import android.widget.FrameLayout;
  
  import org.telegram.messenger.AndroidUtilities;
  import org.telegram.messenger.ImageReceiver;
  import org.telegram.messenger.MediaController;
  import org.telegram.ui.ActionBar.Theme;
  import org.telegram.ui.BubbleActivity;
  import org.telegram.ui.Components.Crop.CropRotationWheel;
  import org.telegram.ui.Components.Crop.CropTransform;
  import org.telegram.ui.Components.Crop.CropView;
  
  public class PhotoCropView extends FrameLayout {
  
      public interface PhotoCropViewDelegate {
          void onChange(boolean reset);
          void onUpdate();
          void onTapUp();
          int getVideoThumbX();
          void onVideoThumbClick();
      }
  
      private PhotoCropViewDelegate delegate;
  
      private CropView cropView;
      private CropRotationWheel wheelView;
  
      private boolean inBubbleMode;
  
      private ImageReceiver thumbImageView;
      private boolean thumbImageVisible;
      private boolean thumbImageVisibleOverride = true;
      private float thumbImageVisibleProgress;
      private float thumbAnimationProgress = 1.0f;
      private AnimatorSet thumbAnimation;
      private AnimatorSet thumbOverrideAnimation;
      private float flashAlpha = 0.0f;
  
      private Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  
      public final Property<PhotoCropView, Float> ANIMATION_VALUE = new AnimationProperties.FloatProperty<PhotoCropView>("thumbAnimationProgress") {
          @Override
          public void setValue(PhotoCropView object, float value) {
              thumbAnimationProgress = value;
              object.invalidate();
          }
  
          @Override
          public Float get(PhotoCropView object) {
              return thumbAnimationProgress;
          }
      };
  
      public final Property<PhotoCropView, Float> PROGRESS_VALUE = new AnimationProperties.FloatProperty<PhotoCropView>("thumbImageVisibleProgress") {
          @Override
          public void setValue(PhotoCropView object, float value) {
              thumbImageVisibleProgress = value;
              object.invalidate();
          }
  
          @Override
          public Float get(PhotoCropView object) {
              return thumbImageVisibleProgress;
          }
      };
  
      public PhotoCropView(Context context) {
          super(context);
  
          inBubbleMode = context instanceof BubbleActivity;
  
          cropView = new CropView(context);
          cropView.setListener(new CropView.CropViewListener() {
              @Override
              public void onChange(boolean reset) {
                  if (delegate != null) {
                      delegate.onChange(reset);
                  }
              }
  
              @Override
              public void onUpdate() {
                  if (delegate != null) {
                      delegate.onUpdate();
                  }
              }
  
              @Override
              public void onAspectLock(boolean enabled) {
                  wheelView.setAspectLock(enabled);
              }
  
              @Override
              public void onTapUp() {
                  if (delegate != null) {
                      delegate.onTapUp();
                  }
              }
          });
          cropView.setBottomPadding(AndroidUtilities.dp(64));
          addView(cropView);
  
          thumbImageView = new ImageReceiver(this);
  
          wheelView = new CropRotationWheel(context);
          wheelView.setListener(new CropRotationWheel.RotationWheelListener() {
              @Override
              public void onStart() {
                  cropView.onRotationBegan();
              }
  
              @Override
              public void onChange(float angle) {
                  cropView.setRotation(angle);
                  if (delegate != null) {
                      delegate.onChange(false);
                  }
              }
  
              @Override
              public void onEnd(float angle) {
                  cropView.onRotationEnded();
              }
  
              @Override
              public void aspectRatioPressed() {
                  cropView.showAspectRatioDialog();
              }
  
              @Override
              public boolean rotate90Pressed() {
                  return rotate();
              }
  
              @Override
              public boolean mirror() {
                  return PhotoCropView.this.mirror();
              }
          });
          addView(wheelView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER | Gravity.BOTTOM, 0, 0, 0, 0));
      }
  
      @Override
      public boolean onInterceptTouchEvent(MotionEvent event) {
          if (thumbImageVisibleOverride && thumbImageVisible && thumbImageView.isInsideImage(event.getX(), event.getY())) {
              if (event.getAction() == MotionEvent.ACTION_UP) {
                  delegate.onVideoThumbClick();
              }
              return true;
          }
          return super.onInterceptTouchEvent(event);
      }
  
      @Override
      public boolean onTouchEvent(MotionEvent event) {
          if (thumbImageVisibleOverride && thumbImageVisible && thumbImageView.isInsideImage(event.getX(), event.getY())) {
              if (event.getAction() == MotionEvent.ACTION_UP) {
                  delegate.onVideoThumbClick();
              }
              return true;
          }
          return super.onTouchEvent(event);
      }
  
      @Override
      protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
          boolean result = super.drawChild(canvas, child, drawingTime);
          if (thumbImageVisible && child == cropView) {
              RectF rect = cropView.getActualRect();
              int targetSize = AndroidUtilities.dp(32);
              int targetX = delegate.getVideoThumbX() - targetSize / 2 + AndroidUtilities.dp(2);
              int targetY = getMeasuredHeight() - AndroidUtilities.dp(156);
              float x = rect.left + (targetX - rect.left) * thumbAnimationProgress;
              float y = rect.top + (targetY - rect.top) * thumbAnimationProgress;
              float size = rect.width() + (targetSize - rect.width()) * thumbAnimationProgress;
              thumbImageView.setRoundRadius((int) (size / 2));
              thumbImageView.setImageCoords(x, y, size, size);
              thumbImageView.setAlpha(thumbImageVisibleProgress);
              thumbImageView.draw(canvas);
  
              if (flashAlpha > 0.0f) {
                  circlePaint.setColor(0xffffffff);
                  circlePaint.setAlpha((int) (flashAlpha * 255));
                  canvas.drawCircle(rect.centerX(), rect.centerY(), rect.width() / 2, circlePaint);
              }
  
              circlePaint.setColor(Theme.getColor(Theme.key_dialogFloatingButton));
              circlePaint.setAlpha(Math.min(255, (int) (255 * thumbAnimationProgress * thumbImageVisibleProgress)));
              canvas.drawCircle(targetX + targetSize / 2, targetY + targetSize + AndroidUtilities.dp(8), AndroidUtilities.dp(3), circlePaint);
          }
          return result;
      }
  
      public boolean rotate() {
          if (wheelView != null) {
              wheelView.reset(false);
          }
          return cropView.rotate90Degrees();
      }
  
      public boolean mirror() {
          return cropView.mirror();
      }
  
      public void setBitmap(Bitmap bitmap, int rotation, boolean freeform, boolean update, PaintingOverlay paintingOverlay, CropTransform cropTransform, VideoEditTextureView videoView, MediaController.CropState state) {
          requestLayout();
  
          thumbImageVisible = false;
          thumbImageView.setImageBitmap((Drawable) null);
          cropView.setBitmap(bitmap, rotation, freeform, update, paintingOverlay, cropTransform, videoView, state);
          wheelView.setFreeform(freeform);
          wheelView.reset(true);
          if (state != null) {
              wheelView.setRotation(state.cropRotate, false);
              wheelView.setRotated(state.transformRotation != 0);
              wheelView.setMirrored(state.mirrored);
          } else {
              wheelView.setRotated(false);
              wheelView.setMirrored(false);
          }
          wheelView.setVisibility(freeform ? VISIBLE : INVISIBLE);
      }
  
      public void setVideoThumbFlashAlpha(float alpha) {
          flashAlpha = alpha;
          invalidate();
      }
  
      public Bitmap getVideoThumb() {
          return thumbImageVisible && thumbImageVisibleOverride ? thumbImageView.getBitmap() : null;
      }
  
      public void setVideoThumb(Bitmap bitmap, int orientation) {
          thumbImageVisible = bitmap != null;
          thumbImageView.setImageBitmap(bitmap);
          thumbImageView.setOrientation(orientation, false);
          if (thumbAnimation != null) {
              thumbAnimation.cancel();
          }
          if (thumbOverrideAnimation != null) {
              thumbOverrideAnimation.cancel();
          }
          thumbImageVisibleOverride = true;
          thumbImageVisibleProgress = 1.0f;
  