[Magnifier-37] Hide handle when overlaps magnifier

In most cases, the magnifier will be displayed above the current line,
so it will not overlap with the handle being dragged. However, when
there is not enough space in the current surface for the magnifier to be
displayed above the current line, the handle can overlap with the
magnifier. Since the handle is implemented as a different window, we
cannot really control the z ordering between them, and we noticed that
the handle will be rendered over the magnifier, which looks bad. This CL
better handles this situation, by hiding the handle when it would
overlap with the magnifier.

Bug: 76459199
Test: manual testing
Test: atest FrameworksCoreTests:android.widget.TextViewActivityTest
Test: atest FrameworksCoreTests:android.widget.TextViewTest
Change-Id: Id5a17fd964360df6094f9e2680e5bcca886c4d2d
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 92f496a8..9946726 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4585,8 +4585,8 @@
             return mContainer.isShowing();
         }
 
-        private boolean isVisible() {
-            // Always show a dragging handle.
+        private boolean shouldShow() {
+            // A dragging handle should always be shown.
             if (mIsDragging) {
                 return true;
             }
@@ -4599,6 +4599,10 @@
                     mPositionX + mHotspotX + getHorizontalOffset(), mPositionY);
         }
 
+        private void setVisible(final boolean visible) {
+            mContainer.getContentView().setVisibility(visible ? VISIBLE : INVISIBLE);
+        }
+
         public abstract int getCurrentCursorOffset();
 
         protected abstract void updateSelection(int offset);
@@ -4692,7 +4696,7 @@
                     onHandleMoved();
                 }
 
-                if (isVisible()) {
+                if (shouldShow()) {
                     // Transform to the window coordinates to follow the view tranformation.
                     final int[] pts = { mPositionX + mHotspotX + getHorizontalOffset(), mPositionY};
                     mTextView.transformFromViewToWindowSpace(pts);
@@ -4745,6 +4749,15 @@
             return 0;
         }
 
+        private boolean tooLargeTextForMagnifier() {
+            final float magnifierContentHeight = Math.round(
+                    mMagnifierAnimator.mMagnifier.getHeight()
+                            / mMagnifierAnimator.mMagnifier.getZoom());
+            final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics();
+            final float glyphHeight = fontMetrics.descent - fontMetrics.ascent;
+            return glyphHeight > magnifierContentHeight;
+        }
+
         /**
          * Computes the position where the magnifier should be shown, relative to
          * {@code mTextView}, and writes them to {@code showPosInView}. Also decides
@@ -4824,13 +4837,12 @@
             return true;
         }
 
-        private boolean tooLargeTextForMagnifier() {
-            final float magnifierContentHeight = Math.round(
-                    mMagnifierAnimator.mMagnifier.getHeight()
-                            / mMagnifierAnimator.mMagnifier.getZoom());
-            final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics();
-            final float glyphHeight = fontMetrics.descent - fontMetrics.ascent;
-            return glyphHeight > magnifierContentHeight;
+        private boolean handleOverlapsMagnifier() {
+            final int handleY = mContainer.getDecorViewLayoutParams().y;
+            final int magnifierBottomWhenAtWindowTop =
+                    mTextView.getRootWindowInsets().getSystemWindowInsetTop()
+                        + mMagnifierAnimator.mMagnifier.getHeight();
+            return handleY <= magnifierBottomWhenAtWindowTop;
         }
 
         protected final void updateMagnifier(@NonNull final MotionEvent event) {
@@ -4846,6 +4858,13 @@
                 mRenderCursorRegardlessTiming = true;
                 mTextView.invalidateCursorPath();
                 suspendBlink();
+                // Hide handle if it overlaps the magnifier.
+                if (handleOverlapsMagnifier()) {
+                    setVisible(false);
+                } else {
+                    setVisible(true);
+                }
+
                 mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
             } else {
                 dismissMagnifier();
@@ -4857,6 +4876,7 @@
                 mMagnifierAnimator.dismiss();
                 mRenderCursorRegardlessTiming = false;
                 resumeBlink();
+                setVisible(true);
             }
         }