From 9d2f606aa8df37de7c38c26b37afb4496ee0e2fc Mon Sep 17 00:00:00 2001
From: Yohei Yukawa <yukawa@google.com>
Date: Fri, 19 Sep 2014 17:48:50 +0900
Subject: [PATCH] Avoid the add-to-dictionary indicator from being clipped

With this CL, the display bounds is taken into consideration
when the location of add-to-dictionary indicator is determined.

BUG: 17578360
Change-Id: I31e458fa7bbc33e539578b331fe1c51ff7f8668e
---
 .../inputmethod/keyboard/TextDecorator.java   | 22 +++-------
 .../inputmethod/keyboard/TextDecoratorUi.java | 42 +++++++++++++++----
 .../keyboard/TextDecoratorUiOperator.java     |  6 +--
 3 files changed, 42 insertions(+), 28 deletions(-)

diff --git a/java/src/com/android/inputmethod/keyboard/TextDecorator.java b/java/src/com/android/inputmethod/keyboard/TextDecorator.java
index 315d36313..6e4e3281e 100644
--- a/java/src/com/android/inputmethod/keyboard/TextDecorator.java
+++ b/java/src/com/android/inputmethod/keyboard/TextDecorator.java
@@ -49,7 +49,7 @@ public class TextDecorator {
     private int mMode = MODE_MONITOR;
 
     private String mLastComposingText = null;
-    private RectF mIndicatorBoundsForLastComposingText = new RectF();
+    private boolean mHasRtlCharsInLastComposingText = false;
     private RectF mComposingTextBoundsForLastComposingText = new RectF();
 
     private boolean mIsFullScreenMode = false;
@@ -241,20 +241,8 @@ public class TextDecorator {
                 right = Math.max(characterBounds.right, right);
             }
             mLastComposingText = composingTextString;
+            mHasRtlCharsInLastComposingText = useRtlLayout;
             mComposingTextBoundsForLastComposingText.set(left, top, right, bottom);
-            // The height and width of the indicator is the same as the height of the composing
-            // text.
-            final float indicatorSize = bottom - top;
-            mIndicatorBoundsForLastComposingText.set(0.0f, 0.0f, indicatorSize, indicatorSize);
-            // The horizontal position of the indicator depends on the text direction.
-            final float indicatorTop = top;
-            final float indicatorLeft;
-            if (useRtlLayout) {
-                indicatorLeft = left - indicatorSize;
-            } else {
-                indicatorLeft = right;
-            }
-            mIndicatorBoundsForLastComposingText.offset(indicatorLeft, indicatorTop);
         }
 
         final int selectionStart = info.getSelectionStart();
@@ -295,8 +283,8 @@ public class TextDecorator {
             return;
         }
 
-        mUiOperator.layoutUi(matrix, mIndicatorBoundsForLastComposingText,
-                mComposingTextBoundsForLastComposingText);
+        mUiOperator.layoutUi(matrix, mComposingTextBoundsForLastComposingText,
+                mHasRtlCharsInLastComposingText);
     }
 
     private void onClickIndicator() {
@@ -374,7 +362,7 @@ public class TextDecorator {
         public void setOnClickListener(Runnable listener) {
         }
         @Override
-        public void layoutUi(Matrix matrix, RectF indicatorBounds, RectF composingTextBounds) {
+        public void layoutUi(Matrix matrix, RectF composingTextBounds, boolean useRtlLayout) {
         }
     };
 }
diff --git a/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java b/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java
index b67d17789..d87dc1bfa 100644
--- a/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java
+++ b/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java
@@ -26,6 +26,7 @@ import android.graphics.Path;
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
 import android.inputmethodservice.InputMethodService;
+import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.View;
@@ -50,6 +51,7 @@ public final class TextDecoratorUi implements TextDecoratorUiOperator {
     private final PopupWindow mTouchEventWindow;
     private final View mTouchEventWindowClickListenerView;
     private final float mHitAreaMarginInPixels;
+    private final RectF mDisplayRect;
 
     /**
      * This constructor is designed to be called from {@link InputMethodService#setInputView(View)}.
@@ -64,6 +66,9 @@ public final class TextDecoratorUi implements TextDecoratorUiOperator {
                 R.integer.text_decorator_hit_area_margin_in_dp);
         mHitAreaMarginInPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                 hitAreaMarginInDP, resources.getDisplayMetrics());
+        final DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+        mDisplayRect = new RectF(0.0f, 0.0f, displayMetrics.widthPixels,
+                displayMetrics.heightPixels);
 
         mLocalRootView = new RelativeLayout(context);
         mLocalRootView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
@@ -111,17 +116,40 @@ public final class TextDecoratorUi implements TextDecoratorUiOperator {
         mTouchEventWindow.dismiss();
     }
 
+    private static final RectF getIndicatorBoundsInScreenCoordinates(final Matrix matrix,
+            final RectF composingTextBounds, final boolean showAtLeftSide) {
+        final float indicatorSize = composingTextBounds.height();
+        final RectF indicatorBounds;
+        if (showAtLeftSide) {
+            indicatorBounds = new RectF(composingTextBounds.left - indicatorSize,
+                    composingTextBounds.top, composingTextBounds.left,
+                    composingTextBounds.top + indicatorSize);
+        } else {
+            indicatorBounds = new RectF(composingTextBounds.right, composingTextBounds.top,
+                    composingTextBounds.right + indicatorSize,
+                    composingTextBounds.top + indicatorSize);
+        }
+        matrix.mapRect(indicatorBounds);
+        return indicatorBounds;
+    }
+
     @Override
-    public void layoutUi(final Matrix matrix, final RectF indicatorBounds,
-            final RectF composingTextBounds) {
-        final RectF indicatorBoundsInScreenCoordinates = new RectF();
-        matrix.mapRect(indicatorBoundsInScreenCoordinates, indicatorBounds);
+    public void layoutUi(final Matrix matrix, final RectF composingTextBounds,
+            final boolean useRtlLayout) {
+        RectF indicatorBoundsInScreenCoordinates = getIndicatorBoundsInScreenCoordinates(matrix,
+                composingTextBounds, useRtlLayout /* showAtLeftSide */);
+        if (indicatorBoundsInScreenCoordinates.left < mDisplayRect.left ||
+                mDisplayRect.right < indicatorBoundsInScreenCoordinates.right) {
+            // The indicator is clipped by the screen. Show the indicator at the opposite side.
+            indicatorBoundsInScreenCoordinates = getIndicatorBoundsInScreenCoordinates(matrix,
+                    composingTextBounds, !useRtlLayout /* showAtLeftSide */);
+        }
+
         mAddToDictionaryIndicatorView.setBounds(indicatorBoundsInScreenCoordinates);
 
-        final RectF hitAreaBounds = new RectF(composingTextBounds);
-        hitAreaBounds.union(indicatorBounds);
         final RectF hitAreaBoundsInScreenCoordinates = new RectF();
-        matrix.mapRect(hitAreaBoundsInScreenCoordinates, hitAreaBounds);
+        matrix.mapRect(hitAreaBoundsInScreenCoordinates, composingTextBounds);
+        hitAreaBoundsInScreenCoordinates.union(indicatorBoundsInScreenCoordinates);
         hitAreaBoundsInScreenCoordinates.inset(-mHitAreaMarginInPixels, -mHitAreaMarginInPixels);
 
         final int[] originScreen = new int[2];
diff --git a/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java b/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java
index 9c0b64ad4..9e30e417e 100644
--- a/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java
+++ b/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java
@@ -17,7 +17,6 @@
 package com.android.inputmethod.keyboard;
 
 import android.graphics.Matrix;
-import android.graphics.PointF;
 import android.graphics.RectF;
 
 /**
@@ -45,9 +44,8 @@ public interface TextDecoratorUiOperator {
     /**
      * Called when the layout should be updated.
      * @param matrix The matrix that transforms the local coordinates into the screen coordinates.
-     * @param indicatorBounds The bounding box of the indicator, in local coordinates.
      * @param composingTextBounds The bounding box of the composing text, in local coordinates.
+     * @param useRtlLayout {@code true} if the indicator should be optimized for RTL layout.
      */
-    void layoutUi(final Matrix matrix, final RectF indicatorBounds,
-            final RectF composingTextBounds);
+    void layoutUi(final Matrix matrix, final RectF composingTextBounds, final boolean useRtlLayout);
 }