gt:         ViewParent parent = getParent();
context: package org.telegram.ui.Components.voip;
  
  import android.animation.Animator;
  import android.animation.AnimatorListenerAdapter;
  import android.animation.ValueAnimator;
  import android.annotation.TargetApi;
  import android.content.Context;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.Outline;
  import android.graphics.Paint;
  import android.graphics.Path;
  import android.graphics.RectF;
  import android.graphics.drawable.Drawable;
  import android.os.Build;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.ViewConfiguration;
  import android.view.ViewOutlineProvider;
  import android.view.ViewParent;
  import android.view.ViewPropertyAnimator;
  import android.view.ViewTreeObserver;
  import android.view.WindowInsets;
  import android.widget.FrameLayout;
  
  import androidx.annotation.NonNull;
  import androidx.core.content.ContextCompat;
  import androidx.core.graphics.ColorUtils;
  
  import org.telegram.messenger.AndroidUtilities;
  import org.telegram.messenger.R;
  import org.telegram.ui.Components.CubicBezierInterpolator;
  
  public class VoIPFloatingLayout extends FrameLayout {
  
      private final float FLOATING_MODE_SCALE = 0.23f;
      float starX;
      float starY;
      float startMovingFromX;
      float startMovingFromY;
      boolean moving;
  
      int lastH;
      int lastW;
  
      WindowInsets lastInsets;
      float touchSlop;
  
      final Path path = new Path();
      final RectF rectF = new RectF();
      final Paint xRefPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
      Paint mutedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
      Drawable mutedDrawable;
  
      public float relativePositionToSetX = -1f;
      float relativePositionToSetY = -1f;
  
      float leftPadding;
      float rightPadding;
      float topPadding;
      float bottomPadding;
  
      float toFloatingModeProgress = 0;
      float mutedProgress = 0;
  
      private boolean uiVisible;
      private boolean floatingMode;
      private boolean setedFloatingMode;
      private boolean switchingToFloatingMode;
      private boolean measuredAsFloatingMode;
  
      private float overrideCornerRadius = -1f;
      private boolean active = true;
      public boolean alwaysFloating;
      public int bottomOffset;
      public float savedRelativePositionX;
      public float savedRelativePositionY;
      public float updatePositionFromX;
      public float updatePositionFromY;
      public boolean switchingToPip;
      Drawable outerShadow;
  
      ValueAnimator switchToFloatingModeAnimator;
      private ValueAnimator.AnimatorUpdateListener progressUpdateListener = valueAnimator -> {
          toFloatingModeProgress = (float) valueAnimator.getAnimatedValue();
          invalidate();
      };
  
      ValueAnimator mutedAnimator;
      private ValueAnimator.AnimatorUpdateListener mutedUpdateListener = valueAnimator -> {
          mutedProgress = (float) valueAnimator.getAnimatedValue();
          invalidate();
      };
  
      OnClickListener tapListener;
  
  
      public VoIPFloatingLayout(@NonNull Context context) {
          super(context);
          touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
              setOutlineProvider(new ViewOutlineProvider() {
                  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
                  @Override
                  public void getOutline(View view, Outline outline) {
                      if (overrideCornerRadius >= 0) {
                          if (overrideCornerRadius < 1) {
                              outline.setRect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
                          } else {
                              outline.setRoundRect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight(), overrideCornerRadius);
                          }
                      } else {
                          if (!floatingMode) {
                              outline.setRect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
                          } else {
                              outline.setRoundRect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight(), floatingMode ? AndroidUtilities.dp(4) : 0);
                          }
                      }
                  }
              });
              setClipToOutline(true);
          }
          mutedPaint.setColor(ColorUtils.setAlphaComponent(Color.BLACK, (int) (255 * 0.4f)));
          mutedDrawable = ContextCompat.getDrawable(context, R.drawable.calls_mute_mini);
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          int width = MeasureSpec.getSize(widthMeasureSpec);
          int height = MeasureSpec.getSize(heightMeasureSpec);
          measuredAsFloatingMode = false;
          if (floatingMode) {
              width *= FLOATING_MODE_SCALE;
              height *= FLOATING_MODE_SCALE;
              measuredAsFloatingMode = true;
          } else {
              if (!switchingToPip) {
                  setTranslationX(0);
                  setTranslationY(0);
              }
          }
  
          super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
  
          if (getMeasuredHeight() != lastH && getMeasuredWidth() != lastW) {
              path.reset();
              rectF.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
              path.addRoundRect(rectF, AndroidUtilities.dp(4), AndroidUtilities.dp(4), Path.Direction.CW);
              path.toggleInverseFillType();
          }
          lastH = getMeasuredHeight();
          lastW = getMeasuredWidth();
  
          updatePadding();
      }
  
      private void updatePadding() {
          leftPadding = AndroidUtilities.dp(16);
          rightPadding = AndroidUtilities.dp(16);
          topPadding = uiVisible ? AndroidUtilities.dp(60) : AndroidUtilities.dp(16);
          bottomPadding = (uiVisible ? AndroidUtilities.dp(100) : AndroidUtilities.dp(16)) + bottomOffset;
      }
  
      @Override
      public boolean onInterceptTouchEvent(MotionEvent ev) {
          return true;
      }
  
      long startTime;
  
      @Override
      public boolean onTouchEvent(MotionEvent event) {
          ViewParent parent = getParent();
          if (!floatingMode || switchingToFloatingMode || !active) {
              return false;
          }
          switch (event.getAction()) {
              case MotionEvent.ACTION_DOWN:
                  if (floatingMode && !switchingToFloatingMode) {
                      startTime = System.currentTimeMillis();
                      starX = event.getX() + getX();
                      starY = event.getY() + getY();
                      animate().setListener(null).cancel();
                      animate().scaleY(1.05f).scaleX(1.05f).alpha(1f).setStartDelay(0).start();
                  }
                  break;
              case MotionEvent.ACTION_MOVE:
                  float dx = event.getX() + getX() - starX;
                  float dy = event.getY() + getY() - starY;
                  if (!moving && (dx * dx + dy * dy) > touchSlop * touchSlop) {
                      if (parent != null) {
                          parent.requestDisallowInterceptTouchEvent(true);
                      }
                      moving = true;
                      starX = event.getX() + getX();
                      starY = event.getY() + getY();
                      startMovingFromX = getTranslationX();
                      startMovingFromY = getTranslationY();
                      dx = 0;
                      dy = 0;
                  }
                  if (moving) {
                      setTranslationX(startMovingFromX + dx);
                      setTranslationY(startMovingFromY + dy);
                  }
                  break;
              case MotionEvent.ACTION_CANCEL:
              case MotionEvent.ACTION_UP:
                  if (parent != null && floatingMode && !switchingToFloatingMode) {
                      parent.requestDisallowInterceptTouchEvent(false);
                      animate().setListener(null).cancel();
                      ViewPropertyAnimator animator = animate().scaleX(1f).scaleY(1f).alpha(1f).setStartDelay(0);
  
                      if (tapListener != null && !moving && System.currentTimeMillis() - startTime < 200) {
                          tapListener.onClick(this);
                      }
  
                      int parentWidth = ((View) getParent()).getMeasuredWidth();
                      int parentHeight = ((View) getParent()).getMeasuredHeight();
  
                      float maxTop = topPadding;
                      float maxBottom = bottomPadding;
                      if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH && lastInsets != null) {
                          maxTop += lastInsets.getSystemWindowInsetTop();
                          maxBottom += lastInsets.getSystemWindowInsetBottom();
                      }
  
                      if (getX() < leftPadding) {
                          animator.translationX(leftPadding);
                      } else if (getX() + getMeasuredWidth() > parentWidth - rightPadding) {
                          animator.translationX(parentWidth - getMeasuredWidth() - rightPadding);
                      }
  
                      if (getY() < maxTop) {
                          animator.translationY(maxTop);
                      } else if (getY() + getMeasuredHeight() > parentHeight - maxBottom) {
                          animator.translationY(parentHeight - getMeasuredHeight() - maxBottom);
                      }
                      animator.setDuration(150).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
                  }
                  moving = false;
                  break;
          }
          return true;
      }
  
      @Override
      protected void dispatchDraw(Canvas canvas) {
          boolean animated = false;
          if (updatePositionFromX >= 0) {
              animate().setListener(null).cancel();
              setTranslationX(updatePositionFromX);
              setTranslationY(updatePositionFromY);
              setScaleX(1f);
              setScaleY(1f);
              setAlpha(1f);
              updatePositionFromX = -1f;
              updatePositionFromY = -1f;
          }
  
          if (relativePositionToSetX >= 0 && floatingMode && getMeasuredWidth() > 0) {
              setRelativePositionInternal(relativePositionToSetX, relativePositionToSetY, getMeasuredWidth(), getMeasuredHeight(), animated);
              relativePositionToSetX = -1f;
              relativePositionToSetY = -1f;
          }
  
  
          super.dispatchDraw(canvas);
  
  
          if (!switchingToFloatingMode && floatingMode != setedFloatingMode) {
              setFloatingMode(setedFloatingMode, true);
          }
  
          int cX = getMeasuredWidth() >> 1;
          int cY = getMeasuredHeight() - (int) (AndroidUtilities.dp(18) * 1f / getScaleY());
          canvas.save();
          float scaleX = 1f / getScaleX() * toFloatingModeProgress * mutedProgress;
          float scaleY = 1f / getScaleY() * toFloatingModeProgress * mutedProgress;
          canvas.scale(scaleX, scaleY, cX, cY);
  
          canvas.drawCircle(cX, cY, AndroidUtilities.dp(14), mutedPaint);
          mutedDrawable.setBounds(
                  cX - mutedDrawable.getIntrinsicWidth() / 2, cY - mutedDrawable.getIntrinsicHeight() / 2,
                  cX + mutedDrawable.getIntrinsicWidth() / 2, cY + mutedDrawable.getIntrinsicHeight() / 2
          );
          mutedDrawable.draw(canvas);
          canvas.restore();
          if (switchingToFloatingMode) {
              invalidate();
          }
      }
  
      public void setInsets(WindowInsets lastInsets) {
          this.lastInsets = lastInsets;
      }
  
      public void setRelativePosition(float x, float y) {
          ViewParent parent = getParent();
          if (!floatingMode || parent == null || ((View) parent).getMeasuredWidth() > 0 || getMeasuredWidth() == 0 || getMeasuredHeight() == 0) {
              relativePositionToSetX = x;
              relativePositionToSetY = y;
          } else {
              setRelativePositionInternal(x, y, getMeasuredWidth(), getMeasuredHeight(), true);
          }
      }
  
      public void setUiVisible(boolean uiVisible) {
          ViewParent parent = getParent();
          if (parent == null) {
              this.uiVisible = uiVisible;
              return;
          }
          this.uiVisible = uiVisible;
      }
  
      public void setBottomOffset(int bottomOffset, boolean animated) {
  