From 91df3f9e7c95d43d645e158b7d8fd34acc3385d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petar=20=C5=A0egina?= Date: Tue, 15 Aug 2017 16:20:43 +0100 Subject: Expand the animation from the user's last touch point The Smart Select animation now expands from the spot the user last lifted their finger. In order to achieve this, the last up event coordinates need to be tracked in Editor. Since it's possible to trigger Smart Select by having the second of the two taps outside any of the rectangles, the touch point gets moved into the nearest rectangle and the animation starts from that point. Test: manual - try out Smart Select by touching different words at different points Test: manual - try to trigger Smart Select with a double tap where the second tap is outside of the word Test: bit FrameworksCoreTests:android.widget.SelectionActionModeHelperTest Test: bit CtsViewTestCases:android.view.textclassifier.cts.TextClassificationManagerTest Test: bit FrameworksCoreTests:android.widget.TextViewActivityTest Test: bit CtsAccessibilityServiceTestCases:android.accessibilityservice.cts.AccessibilityTextTraversalTest Change-Id: I96844e8307554b010b476673820f98dae09c0cc3 --- .../android/widget/SelectionActionModeHelper.java | 50 +++++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) (limited to 'core/java/android/widget/SelectionActionModeHelper.java') diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index 5e70ef0b973f..2561ffe572ab 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiThread; import android.annotation.WorkerThread; +import android.graphics.PointF; import android.graphics.RectF; import android.os.AsyncTask; import android.os.LocaleList; @@ -27,13 +28,13 @@ import android.text.Layout; import android.text.Selection; import android.text.Spannable; import android.text.TextUtils; -import android.util.Pair; import android.view.ActionMode; import android.view.textclassifier.TextClassification; import android.view.textclassifier.TextClassifier; import android.view.textclassifier.TextSelection; import android.widget.Editor.SelectionModifierCursorController; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.util.ArrayList; @@ -45,8 +46,10 @@ import java.util.function.Supplier; /** * Helper class for starting selection action mode * (synchronously without the TextClassifier, asynchronously with the TextClassifier). + * @hide */ @UiThread +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) final class SelectionActionModeHelper { /** @@ -224,15 +227,15 @@ final class SelectionActionModeHelper { rectangle.bottom += textView.getPaddingTop(); } - final RectF firstRectangle = selectionRectangles.get(0); + final PointF touchPoint = new PointF( + mEditor.getLastUpPositionX(), + mEditor.getLastUpPositionY()); - // TODO use the original touch point instead of the hardcoded point generated here - final Pair halfPoint = new Pair<>( - firstRectangle.centerX(), - firstRectangle.centerY()); + final PointF animationStartPoint = + movePointInsideNearestRectangle(touchPoint, selectionRectangles); mSmartSelectSprite.startAnimation( - halfPoint, + animationStartPoint, selectionRectangles, onAnimationEndCallback); } @@ -248,6 +251,39 @@ final class SelectionActionModeHelper { return result; } + /** @hide */ + @VisibleForTesting + public static PointF movePointInsideNearestRectangle(final PointF point, + final List rectangles) { + float bestX = -1; + float bestY = -1; + double bestDistance = Double.MAX_VALUE; + + for (final RectF rectangle : rectangles) { + final float candidateY = rectangle.centerY(); + final float candidateX; + + if (point.x > rectangle.right) { + candidateX = rectangle.right; + } else if (point.x < rectangle.left) { + candidateX = rectangle.left; + } else { + candidateX = point.x; + } + + final double candidateDistance = Math.pow(point.x - candidateX, 2) + + Math.pow(point.y - candidateY, 2); + + if (candidateDistance < bestDistance) { + bestX = candidateX; + bestY = candidateY; + bestDistance = candidateDistance; + } + } + + return new PointF(bestX, bestY); + } + private void invalidateActionMode(@Nullable SelectionResult result) { cancelSmartSelectAnimation(); mTextClassification = result != null ? result.mClassification : null; -- cgit v1.2.3