Loading core/java/android/widget/Editor.java +14 −0 Original line number Diff line number Diff line Loading @@ -260,6 +260,7 @@ public class Editor { private PositionListener mPositionListener; private float mLastDownPositionX, mLastDownPositionY; private float mLastUpPositionX, mLastUpPositionY; private float mContextMenuAnchorX, mContextMenuAnchorY; Callback mCustomSelectionActionModeCallback; Callback mCustomInsertionActionModeCallback; Loading Loading @@ -1130,6 +1131,14 @@ public class Editor { return handled; } float getLastUpPositionX() { return mLastUpPositionX; } float getLastUpPositionY() { return mLastUpPositionY; } private long getLastTouchOffsets() { SelectionModifierCursorController selectionController = getSelectionController(); final int minOffset = selectionController.getMinTouchOffset(); Loading Loading @@ -1371,6 +1380,11 @@ public class Editor { mShowSuggestionRunnable = null; } if (event.getActionMasked() == MotionEvent.ACTION_UP) { mLastUpPositionX = event.getX(); mLastUpPositionY = event.getY(); } if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { mLastDownPositionX = event.getX(); mLastDownPositionY = event.getY(); Loading core/java/android/widget/SelectionActionModeHelper.java +43 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 { /** Loading Loading @@ -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<Float, Float> halfPoint = new Pair<>( firstRectangle.centerX(), firstRectangle.centerY()); final PointF animationStartPoint = movePointInsideNearestRectangle(touchPoint, selectionRectangles); mSmartSelectSprite.startAnimation( halfPoint, animationStartPoint, selectionRectangles, onAnimationEndCallback); } Loading @@ -248,6 +251,39 @@ final class SelectionActionModeHelper { return result; } /** @hide */ @VisibleForTesting public static PointF movePointInsideNearestRectangle(final PointF point, final List<RectF> 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; Loading core/java/android/widget/SmartSelectSprite.java +35 −20 Original line number Diff line number Diff line Loading @@ -30,11 +30,11 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PointF; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.Shape; import android.util.Pair; import android.util.TypedValue; import android.view.View; import android.view.ViewOverlay; Loading Loading @@ -82,16 +82,16 @@ final class SmartSelectSprite { private final float[] mLineCoordinates; private PolygonShape(final List<Pair<Float, Float>> points) { private PolygonShape(final List<PointF> points) { mLineCoordinates = new float[points.size() * POINTS_PER_LINE]; int index = 0; Pair<Float, Float> currentPoint = points.get(0); for (final Pair<Float, Float> nextPoint : points) { mLineCoordinates[index] = currentPoint.first; mLineCoordinates[index + 1] = currentPoint.second; mLineCoordinates[index + 2] = nextPoint.first; mLineCoordinates[index + 3] = nextPoint.second; PointF currentPoint = points.get(0); for (final PointF nextPoint : points) { mLineCoordinates[index] = currentPoint.x; mLineCoordinates[index + 1] = currentPoint.y; mLineCoordinates[index + 2] = nextPoint.x; mLineCoordinates[index + 3] = nextPoint.y; index += POINTS_PER_LINE; currentPoint = nextPoint; Loading Loading @@ -342,9 +342,9 @@ final class SmartSelectSprite { final List<RectF> rectangles, final int color) { final List<Drawable> drawables = new LinkedList<>(); final Set<List<Pair<Float, Float>>> mergedPaths = calculateMergedPolygonPoints(rectangles); final Set<List<PointF>> mergedPaths = calculateMergedPolygonPoints(rectangles); for (List<Pair<Float, Float>> path : mergedPaths) { for (List<PointF> path : mergedPaths) { // Add the starting point to the end of the polygon so that it ends up closed. path.add(path.get(0)); Loading @@ -361,7 +361,7 @@ final class SmartSelectSprite { return drawables; } private static Set<List<Pair<Float, Float>>> calculateMergedPolygonPoints( private static Set<List<PointF>> calculateMergedPolygonPoints( List<RectF> rectangles) { final Set<List<RectF>> partitions = new HashSet<>(); final LinkedList<RectF> listOfRects = new LinkedList<>(rectangles); Loading Loading @@ -389,20 +389,20 @@ final class SmartSelectSprite { partitions.add(partition); } final Set<List<Pair<Float, Float>>> result = new HashSet<>(); final Set<List<PointF>> result = new HashSet<>(); for (List<RectF> partition : partitions) { final List<Pair<Float, Float>> points = new LinkedList<>(); final List<PointF> points = new LinkedList<>(); final Stack<RectF> rects = new Stack<>(); for (RectF rect : partition) { points.add(new Pair<>(rect.right, rect.top)); points.add(new Pair<>(rect.right, rect.bottom)); points.add(new PointF(rect.right, rect.top)); points.add(new PointF(rect.right, rect.bottom)); rects.add(rect); } while (!rects.isEmpty()) { final RectF rect = rects.pop(); points.add(new Pair<>(rect.left, rect.bottom)); points.add(new Pair<>(rect.left, rect.top)); points.add(new PointF(rect.left, rect.bottom)); points.add(new PointF(rect.left, rect.top)); } result.add(points); Loading @@ -426,7 +426,7 @@ final class SmartSelectSprite { * @see #cancelAnimation() */ public void startAnimation( final Pair<Float, Float> start, final PointF start, final List<RectF> destinationRectangles, final Runnable onAnimationEnd) throws IllegalArgumentException { cancelAnimation(); Loading @@ -439,7 +439,7 @@ final class SmartSelectSprite { final RectF centerRectangle = destinationRectangles .stream() .filter((r) -> r.contains(start.first, start.second)) .filter((r) -> contains(r, start)) .findFirst() .orElseThrow(() -> new IllegalArgumentException( "Center point is not inside any of the rectangles!")); Loading @@ -452,7 +452,7 @@ final class SmartSelectSprite { startingOffset += rectangle.width(); } startingOffset += start.first - centerRectangle.left; startingOffset += start.x - centerRectangle.left; final float centerRectangleHalfHeight = centerRectangle.height() / 2; final float startingOffsetLeft = startingOffset - centerRectangleHalfHeight; Loading Loading @@ -632,6 +632,21 @@ final class SmartSelectSprite { return result; } /** * A variant of {@link RectF#contains(float, float)} that also allows the point to reside on * the right boundary of the rectangle. * * @param rectangle the rectangle inside which the point should be to be considered "contained" * @param point the point which will be tested * @return whether the point is inside the rectangle (or on it's right boundary) */ private static boolean contains(final RectF rectangle, final PointF point) { final float x = point.x; final float y = point.y; return x >= rectangle.left && x <= rectangle.right && y >= rectangle.top && y <= rectangle.bottom; } private void addToOverlay(final Drawable drawable) { mView.getOverlay().add(drawable); mExistingAnimationDrawables.add(drawable); Loading core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java 0 → 100644 +113 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.widget; import static org.junit.Assert.assertEquals; import android.graphics.PointF; import android.graphics.RectF; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.Arrays; import java.util.List; @RunWith(JUnit4.class) public final class SelectionActionModeHelperTest { /* * The test rectangle set is composed of three 1x1 rectangles as illustrated below. * * (0, 0) ____________ (100001, 0) * |█ █| * |_█________| * (0, 2) (100001, 2) */ private final List<RectF> mRectFList = Arrays.asList( new RectF(0, 0, 1, 1), new RectF(100000, 0, 100001, 1), new RectF(1, 1, 2, 2)); @Test public void testMovePointInsideNearestRectangle_pointIsInsideRectangle() { testMovePointInsideNearestRectangle( 0.1f /* pointX */, 0.1f /* pointY */, 0.1f /* expectedPointX */, 0.5f /* expectedPointY */); } @Test public void testMovePointInsideNearestRectangle_pointIsAboveRectangle() { testMovePointInsideNearestRectangle( 0.1f /* pointX */, -1.0f /* pointY */, 0.1f /* expectedPointX */, 0.5f /* expectedPointY */); } @Test public void testMovePointInsideNearestRectangle_pointIsLeftOfRectangle() { testMovePointInsideNearestRectangle( -1.0f /* pointX */, 0.4f /* pointY */, 0.0f /* expectedPointX */, 0.5f /* expectedPointY */); } @Test public void testMovePointInsideNearestRectangle_pointIsRightOfRectangle() { testMovePointInsideNearestRectangle( 1.1f /* pointX */, 0.0f /* pointY */, 1.0f /* expectedPointX */, 0.5f /* expectedPointY */); } @Test public void testMovePointInsideNearestRectangle_pointIsBelowRectangle() { testMovePointInsideNearestRectangle( 0.1f /* pointX */, 1.1f /* pointY */, 0.1f /* expectedPointX */, 0.5f /* expectedPointY */); } @Test public void testMovePointInsideNearestRectangle_pointIsToRightOfTheRightmostRectangle() { testMovePointInsideNearestRectangle( 200000.0f /* pointX */, 0.1f /* pointY */, 100001.0f /* expectedPointX */, 0.5f /* expectedPointY */); } private void testMovePointInsideNearestRectangle(final float pointX, final float pointY, final float expectedPointX, final float expectedPointY) { final PointF point = new PointF(pointX, pointY); final PointF adjustedPoint = SelectionActionModeHelper.movePointInsideNearestRectangle(point, mRectFList); assertEquals(expectedPointX, adjustedPoint.x, 0.0f); assertEquals(expectedPointY, adjustedPoint.y, 0.0f); } } Loading
core/java/android/widget/Editor.java +14 −0 Original line number Diff line number Diff line Loading @@ -260,6 +260,7 @@ public class Editor { private PositionListener mPositionListener; private float mLastDownPositionX, mLastDownPositionY; private float mLastUpPositionX, mLastUpPositionY; private float mContextMenuAnchorX, mContextMenuAnchorY; Callback mCustomSelectionActionModeCallback; Callback mCustomInsertionActionModeCallback; Loading Loading @@ -1130,6 +1131,14 @@ public class Editor { return handled; } float getLastUpPositionX() { return mLastUpPositionX; } float getLastUpPositionY() { return mLastUpPositionY; } private long getLastTouchOffsets() { SelectionModifierCursorController selectionController = getSelectionController(); final int minOffset = selectionController.getMinTouchOffset(); Loading Loading @@ -1371,6 +1380,11 @@ public class Editor { mShowSuggestionRunnable = null; } if (event.getActionMasked() == MotionEvent.ACTION_UP) { mLastUpPositionX = event.getX(); mLastUpPositionY = event.getY(); } if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { mLastDownPositionX = event.getX(); mLastDownPositionY = event.getY(); Loading
core/java/android/widget/SelectionActionModeHelper.java +43 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 { /** Loading Loading @@ -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<Float, Float> halfPoint = new Pair<>( firstRectangle.centerX(), firstRectangle.centerY()); final PointF animationStartPoint = movePointInsideNearestRectangle(touchPoint, selectionRectangles); mSmartSelectSprite.startAnimation( halfPoint, animationStartPoint, selectionRectangles, onAnimationEndCallback); } Loading @@ -248,6 +251,39 @@ final class SelectionActionModeHelper { return result; } /** @hide */ @VisibleForTesting public static PointF movePointInsideNearestRectangle(final PointF point, final List<RectF> 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; Loading
core/java/android/widget/SmartSelectSprite.java +35 −20 Original line number Diff line number Diff line Loading @@ -30,11 +30,11 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PointF; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.Shape; import android.util.Pair; import android.util.TypedValue; import android.view.View; import android.view.ViewOverlay; Loading Loading @@ -82,16 +82,16 @@ final class SmartSelectSprite { private final float[] mLineCoordinates; private PolygonShape(final List<Pair<Float, Float>> points) { private PolygonShape(final List<PointF> points) { mLineCoordinates = new float[points.size() * POINTS_PER_LINE]; int index = 0; Pair<Float, Float> currentPoint = points.get(0); for (final Pair<Float, Float> nextPoint : points) { mLineCoordinates[index] = currentPoint.first; mLineCoordinates[index + 1] = currentPoint.second; mLineCoordinates[index + 2] = nextPoint.first; mLineCoordinates[index + 3] = nextPoint.second; PointF currentPoint = points.get(0); for (final PointF nextPoint : points) { mLineCoordinates[index] = currentPoint.x; mLineCoordinates[index + 1] = currentPoint.y; mLineCoordinates[index + 2] = nextPoint.x; mLineCoordinates[index + 3] = nextPoint.y; index += POINTS_PER_LINE; currentPoint = nextPoint; Loading Loading @@ -342,9 +342,9 @@ final class SmartSelectSprite { final List<RectF> rectangles, final int color) { final List<Drawable> drawables = new LinkedList<>(); final Set<List<Pair<Float, Float>>> mergedPaths = calculateMergedPolygonPoints(rectangles); final Set<List<PointF>> mergedPaths = calculateMergedPolygonPoints(rectangles); for (List<Pair<Float, Float>> path : mergedPaths) { for (List<PointF> path : mergedPaths) { // Add the starting point to the end of the polygon so that it ends up closed. path.add(path.get(0)); Loading @@ -361,7 +361,7 @@ final class SmartSelectSprite { return drawables; } private static Set<List<Pair<Float, Float>>> calculateMergedPolygonPoints( private static Set<List<PointF>> calculateMergedPolygonPoints( List<RectF> rectangles) { final Set<List<RectF>> partitions = new HashSet<>(); final LinkedList<RectF> listOfRects = new LinkedList<>(rectangles); Loading Loading @@ -389,20 +389,20 @@ final class SmartSelectSprite { partitions.add(partition); } final Set<List<Pair<Float, Float>>> result = new HashSet<>(); final Set<List<PointF>> result = new HashSet<>(); for (List<RectF> partition : partitions) { final List<Pair<Float, Float>> points = new LinkedList<>(); final List<PointF> points = new LinkedList<>(); final Stack<RectF> rects = new Stack<>(); for (RectF rect : partition) { points.add(new Pair<>(rect.right, rect.top)); points.add(new Pair<>(rect.right, rect.bottom)); points.add(new PointF(rect.right, rect.top)); points.add(new PointF(rect.right, rect.bottom)); rects.add(rect); } while (!rects.isEmpty()) { final RectF rect = rects.pop(); points.add(new Pair<>(rect.left, rect.bottom)); points.add(new Pair<>(rect.left, rect.top)); points.add(new PointF(rect.left, rect.bottom)); points.add(new PointF(rect.left, rect.top)); } result.add(points); Loading @@ -426,7 +426,7 @@ final class SmartSelectSprite { * @see #cancelAnimation() */ public void startAnimation( final Pair<Float, Float> start, final PointF start, final List<RectF> destinationRectangles, final Runnable onAnimationEnd) throws IllegalArgumentException { cancelAnimation(); Loading @@ -439,7 +439,7 @@ final class SmartSelectSprite { final RectF centerRectangle = destinationRectangles .stream() .filter((r) -> r.contains(start.first, start.second)) .filter((r) -> contains(r, start)) .findFirst() .orElseThrow(() -> new IllegalArgumentException( "Center point is not inside any of the rectangles!")); Loading @@ -452,7 +452,7 @@ final class SmartSelectSprite { startingOffset += rectangle.width(); } startingOffset += start.first - centerRectangle.left; startingOffset += start.x - centerRectangle.left; final float centerRectangleHalfHeight = centerRectangle.height() / 2; final float startingOffsetLeft = startingOffset - centerRectangleHalfHeight; Loading Loading @@ -632,6 +632,21 @@ final class SmartSelectSprite { return result; } /** * A variant of {@link RectF#contains(float, float)} that also allows the point to reside on * the right boundary of the rectangle. * * @param rectangle the rectangle inside which the point should be to be considered "contained" * @param point the point which will be tested * @return whether the point is inside the rectangle (or on it's right boundary) */ private static boolean contains(final RectF rectangle, final PointF point) { final float x = point.x; final float y = point.y; return x >= rectangle.left && x <= rectangle.right && y >= rectangle.top && y <= rectangle.bottom; } private void addToOverlay(final Drawable drawable) { mView.getOverlay().add(drawable); mExistingAnimationDrawables.add(drawable); Loading
core/tests/coretests/src/android/widget/SelectionActionModeHelperTest.java 0 → 100644 +113 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.widget; import static org.junit.Assert.assertEquals; import android.graphics.PointF; import android.graphics.RectF; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.Arrays; import java.util.List; @RunWith(JUnit4.class) public final class SelectionActionModeHelperTest { /* * The test rectangle set is composed of three 1x1 rectangles as illustrated below. * * (0, 0) ____________ (100001, 0) * |█ █| * |_█________| * (0, 2) (100001, 2) */ private final List<RectF> mRectFList = Arrays.asList( new RectF(0, 0, 1, 1), new RectF(100000, 0, 100001, 1), new RectF(1, 1, 2, 2)); @Test public void testMovePointInsideNearestRectangle_pointIsInsideRectangle() { testMovePointInsideNearestRectangle( 0.1f /* pointX */, 0.1f /* pointY */, 0.1f /* expectedPointX */, 0.5f /* expectedPointY */); } @Test public void testMovePointInsideNearestRectangle_pointIsAboveRectangle() { testMovePointInsideNearestRectangle( 0.1f /* pointX */, -1.0f /* pointY */, 0.1f /* expectedPointX */, 0.5f /* expectedPointY */); } @Test public void testMovePointInsideNearestRectangle_pointIsLeftOfRectangle() { testMovePointInsideNearestRectangle( -1.0f /* pointX */, 0.4f /* pointY */, 0.0f /* expectedPointX */, 0.5f /* expectedPointY */); } @Test public void testMovePointInsideNearestRectangle_pointIsRightOfRectangle() { testMovePointInsideNearestRectangle( 1.1f /* pointX */, 0.0f /* pointY */, 1.0f /* expectedPointX */, 0.5f /* expectedPointY */); } @Test public void testMovePointInsideNearestRectangle_pointIsBelowRectangle() { testMovePointInsideNearestRectangle( 0.1f /* pointX */, 1.1f /* pointY */, 0.1f /* expectedPointX */, 0.5f /* expectedPointY */); } @Test public void testMovePointInsideNearestRectangle_pointIsToRightOfTheRightmostRectangle() { testMovePointInsideNearestRectangle( 200000.0f /* pointX */, 0.1f /* pointY */, 100001.0f /* expectedPointX */, 0.5f /* expectedPointY */); } private void testMovePointInsideNearestRectangle(final float pointX, final float pointY, final float expectedPointX, final float expectedPointY) { final PointF point = new PointF(pointX, pointY); final PointF adjustedPoint = SelectionActionModeHelper.movePointInsideNearestRectangle(point, mRectFList); assertEquals(expectedPointX, adjustedPoint.x, 0.0f); assertEquals(expectedPointY, adjustedPoint.y, 0.0f); } }