Commit 5baa1238 authored by Shimi Zhang's avatar Shimi Zhang Committed by Commit Bot

aw: Implement SelectionInsertionHandleObserver

This cl introduces SelectionInsertionHandleObserver interface which deals with following
events in SelectionPopupController#onSelectionEvent():

SELECTION_HANDLES_MOVED
SELECTION_HANDLE_DRAG_STARTED
SELECTION_HANDLE_DRAG_STOPPED

INSERTION_HANDLE_MOVED
INSERTION_HANDLE_DRAG_STARTED
INSERTION_HANDLE_DRAG_STOPPED

Bug: 798017
Change-Id: Iff08d5cb0a32528523b0fcc606c9c617e6c4fb48
Reviewed-on: https://chromium-review.googlesource.com/846339
Commit-Queue: Shimi Zhang <ctzsm@chromium.org>
Reviewed-by: default avatarChangwan Ryu <changwan@chromium.org>
Reviewed-by: default avatarBo <boliu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#526593}
parent ddd48e32
...@@ -115,6 +115,7 @@ android_library("content_java") { ...@@ -115,6 +115,7 @@ android_library("content_java") {
"java/src/org/chromium/content/browser/ChildProcessCreationParams.java", "java/src/org/chromium/content/browser/ChildProcessCreationParams.java",
"java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java", "java/src/org/chromium/content/browser/ChildProcessLauncherHelper.java",
"java/src/org/chromium/content/browser/ContentChildProcessConstants.java", "java/src/org/chromium/content/browser/ContentChildProcessConstants.java",
"java/src/org/chromium/content/browser/ContentClassFactory.java",
"java/src/org/chromium/content/browser/ContentFeatureList.java", "java/src/org/chromium/content/browser/ContentFeatureList.java",
"java/src/org/chromium/content/browser/ContentNfcDelegate.java", "java/src/org/chromium/content/browser/ContentNfcDelegate.java",
"java/src/org/chromium/content/browser/ContentVideoView.java", "java/src/org/chromium/content/browser/ContentVideoView.java",
...@@ -146,6 +147,7 @@ android_library("content_java") { ...@@ -146,6 +147,7 @@ android_library("content_java") {
"java/src/org/chromium/content/browser/ScreenOrientationProvider.java", "java/src/org/chromium/content/browser/ScreenOrientationProvider.java",
"java/src/org/chromium/content/browser/SelectionEventProxyImpl.java", "java/src/org/chromium/content/browser/SelectionEventProxyImpl.java",
"java/src/org/chromium/content/browser/SelectionIndicesConverter.java", "java/src/org/chromium/content/browser/SelectionIndicesConverter.java",
"java/src/org/chromium/content/browser/SelectionInsertionHandleObserver.java",
"java/src/org/chromium/content/browser/SelectionPopupController.java", "java/src/org/chromium/content/browser/SelectionPopupController.java",
"java/src/org/chromium/content/browser/SmartClipProvider.java", "java/src/org/chromium/content/browser/SmartClipProvider.java",
"java/src/org/chromium/content/browser/SmartSelectionClient.java", "java/src/org/chromium/content/browser/SmartSelectionClient.java",
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.content.browser;
import android.view.View;
import org.chromium.base.ThreadUtils;
/**
* A class factory for downstream injecting code to content layer.
*/
public class ContentClassFactory {
private static ContentClassFactory sSingleton;
/**
* Sets the factory object.
*/
public static void set(ContentClassFactory factory) {
ThreadUtils.assertOnUiThread();
sSingleton = factory;
}
/**
* Returns the factory object.
*/
public static ContentClassFactory get() {
ThreadUtils.assertOnUiThread();
if (sSingleton == null) sSingleton = new ContentClassFactory();
return sSingleton;
}
/**
* Constructor.
*/
protected ContentClassFactory() {}
/**
* Creates HandleObserver object.
*/
public SelectionInsertionHandleObserver createHandleObserver(View view) {
// Implemented by a subclass.
return null;
}
}
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.content.browser;
/**
* An interface for observing selection/insertion touch handles.
*/
public interface SelectionInsertionHandleObserver {
/**
* Process with handle drag started and handle moving events.
* @param x The x coordinate of the middle point of selection/insertion bound cooresponding to
* the dragging handle.
* @param y The y coordinate of the middle point of selection/insertion bound cooresponding to
* the dragging handle.
*/
void handleDragStartedOrMoved(float x, float y);
/**
* Process with handle drag stopped event.
*/
void handleDragStopped();
}
...@@ -150,6 +150,14 @@ public class SelectionPopupController extends ActionModeCallbackHelper { ...@@ -150,6 +150,14 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
// Whether a scroll is in progress. // Whether a scroll is in progress.
private boolean mScrollInProgress; private boolean mScrollInProgress;
/**
* The {@link SelectionInsertionHandleObserver} that processes handle events, or {@code null} if
* none exists.
*/
private SelectionInsertionHandleObserver mHandleObserver;
// Whether a handle dragging is in progress.
private boolean mDragStarted;
/** /**
* Create {@link SelectionPopupController} instance. * Create {@link SelectionPopupController} instance.
* @param context Context for action mode. * @param context Context for action mode.
...@@ -200,6 +208,9 @@ public class SelectionPopupController extends ActionModeCallbackHelper { ...@@ -200,6 +208,9 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
mLastSelectedText = ""; mLastSelectedText = "";
mHandleObserver = ContentClassFactory.get().createHandleObserver(view);
mDragStarted = false;
if (initializeNative) nativeInit(webContents); if (initializeNative) nativeInit(webContents);
} }
...@@ -1048,13 +1059,17 @@ public class SelectionPopupController extends ActionModeCallbackHelper { ...@@ -1048,13 +1059,17 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
} }
// All coordinates are in DIP. // All coordinates are in DIP.
@VisibleForTesting
@CalledByNative @CalledByNative
private void onSelectionEvent(int eventType, int left, int top, int right, int bottom, void onSelectionEvent(int eventType, int left, int top, int right, int bottom,
float boundMiddlePointX, float boundMiddlePointY) { float boundMiddlePointX, float boundMiddlePointY) {
// Ensure the provided selection coordinates form a non-empty rect, as required by // Ensure the provided selection coordinates form a non-empty rect, as required by
// the selection action mode. // the selection action mode.
if (left == right) ++right; if (left == right) ++right;
if (top == bottom) ++bottom; if (top == bottom) ++bottom;
final float deviceScale = getDeviceScaleFactor();
boundMiddlePointX *= deviceScale;
boundMiddlePointY *= deviceScale;
switch (eventType) { switch (eventType) {
case SelectionEventType.SELECTION_HANDLES_SHOWN: case SelectionEventType.SELECTION_HANDLES_SHOWN:
break; break;
...@@ -1062,6 +1077,9 @@ public class SelectionPopupController extends ActionModeCallbackHelper { ...@@ -1062,6 +1077,9 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
case SelectionEventType.SELECTION_HANDLES_MOVED: case SelectionEventType.SELECTION_HANDLES_MOVED:
mSelectionRect.set(left, top, right, bottom); mSelectionRect.set(left, top, right, bottom);
invalidateContentRect(); invalidateContentRect();
if (mDragStarted && mHandleObserver != null) {
mHandleObserver.handleDragStartedOrMoved(boundMiddlePointX, boundMiddlePointY);
}
break; break;
case SelectionEventType.SELECTION_HANDLES_CLEARED: case SelectionEventType.SELECTION_HANDLES_CLEARED:
...@@ -1077,10 +1095,18 @@ public class SelectionPopupController extends ActionModeCallbackHelper { ...@@ -1077,10 +1095,18 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
case SelectionEventType.SELECTION_HANDLE_DRAG_STARTED: case SelectionEventType.SELECTION_HANDLE_DRAG_STARTED:
hideActionMode(true); hideActionMode(true);
mDragStarted = true;
if (mHandleObserver != null) {
mHandleObserver.handleDragStartedOrMoved(boundMiddlePointX, boundMiddlePointY);
}
break; break;
case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED: case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED:
mWebContents.showContextMenuAtTouchHandle(left, bottom); mWebContents.showContextMenuAtTouchHandle(left, bottom);
if (mHandleObserver != null) {
mHandleObserver.handleDragStopped();
}
mDragStarted = false;
break; break;
case SelectionEventType.INSERTION_HANDLE_SHOWN: case SelectionEventType.INSERTION_HANDLE_SHOWN:
...@@ -1095,6 +1121,9 @@ public class SelectionPopupController extends ActionModeCallbackHelper { ...@@ -1095,6 +1121,9 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
} else { } else {
destroyPastePopup(); destroyPastePopup();
} }
if (mDragStarted && mHandleObserver != null) {
mHandleObserver.handleDragStartedOrMoved(boundMiddlePointX, boundMiddlePointY);
}
break; break;
case SelectionEventType.INSERTION_HANDLE_TAPPED: case SelectionEventType.INSERTION_HANDLE_TAPPED:
...@@ -1116,6 +1145,10 @@ public class SelectionPopupController extends ActionModeCallbackHelper { ...@@ -1116,6 +1145,10 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
case SelectionEventType.INSERTION_HANDLE_DRAG_STARTED: case SelectionEventType.INSERTION_HANDLE_DRAG_STARTED:
mWasPastePopupShowingOnInsertionDragStart = isPastePopupShowing(); mWasPastePopupShowingOnInsertionDragStart = isPastePopupShowing();
destroyPastePopup(); destroyPastePopup();
mDragStarted = true;
if (mHandleObserver != null) {
mHandleObserver.handleDragStartedOrMoved(boundMiddlePointX, boundMiddlePointY);
}
break; break;
case SelectionEventType.INSERTION_HANDLE_DRAG_STOPPED: case SelectionEventType.INSERTION_HANDLE_DRAG_STOPPED:
...@@ -1124,6 +1157,10 @@ public class SelectionPopupController extends ActionModeCallbackHelper { ...@@ -1124,6 +1157,10 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
mSelectionRect.left, mSelectionRect.bottom); mSelectionRect.left, mSelectionRect.bottom);
} }
mWasPastePopupShowingOnInsertionDragStart = false; mWasPastePopupShowingOnInsertionDragStart = false;
if (mHandleObserver != null) {
mHandleObserver.handleDragStopped();
}
mDragStarted = false;
break; break;
default: default:
...@@ -1131,7 +1168,6 @@ public class SelectionPopupController extends ActionModeCallbackHelper { ...@@ -1131,7 +1168,6 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
} }
if (mSelectionClient != null) { if (mSelectionClient != null) {
final float deviceScale = getDeviceScaleFactor();
int xAnchorPix = (int) (mSelectionRect.left * deviceScale); int xAnchorPix = (int) (mSelectionRect.left * deviceScale);
int yAnchorPix = (int) (mSelectionRect.bottom * deviceScale); int yAnchorPix = (int) (mSelectionRect.bottom * deviceScale);
mSelectionClient.onSelectionEvent(eventType, xAnchorPix, yAnchorPix); mSelectionClient.onSelectionEvent(eventType, xAnchorPix, yAnchorPix);
...@@ -1172,6 +1208,7 @@ public class SelectionPopupController extends ActionModeCallbackHelper { ...@@ -1172,6 +1208,7 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
/** /**
* Sets the client that implements selection augmenting functionality, or null if none exists. * Sets the client that implements selection augmenting functionality, or null if none exists.
*/ */
@VisibleForTesting
void setSelectionClient(@Nullable SelectionClient selectionClient) { void setSelectionClient(@Nullable SelectionClient selectionClient) {
mSelectionClient = selectionClient; mSelectionClient = selectionClient;
if (mSelectionClient != null) { if (mSelectionClient != null) {
...@@ -1184,6 +1221,15 @@ public class SelectionPopupController extends ActionModeCallbackHelper { ...@@ -1184,6 +1221,15 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
assert !mHidden; assert !mHidden;
} }
/**
* Sets the handle observer, or null if none exists.
*/
@VisibleForTesting
void setSelectionInsertionHandleObserver(
@Nullable SelectionInsertionHandleObserver handleObserver) {
mHandleObserver = handleObserver;
}
@CalledByNative @CalledByNative
private void onShowUnhandledTapUIIfNeeded(int x, int y) { private void onShowUnhandledTapUIIfNeeded(int x, int y) {
if (x < 0 || y < 0 || mView.getWidth() < x || mView.getHeight() < y) return; if (x < 0 || y < 0 || mView.getWidth() < x || mView.getHeight() < y) return;
......
...@@ -8,6 +8,7 @@ import static org.junit.Assert.assertEquals; ...@@ -8,6 +8,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
...@@ -37,6 +38,7 @@ import org.chromium.content_public.browser.SelectionMetricsLogger; ...@@ -37,6 +38,7 @@ import org.chromium.content_public.browser.SelectionMetricsLogger;
import org.chromium.testing.local.LocalRobolectricTestRunner; import org.chromium.testing.local.LocalRobolectricTestRunner;
import org.chromium.ui.base.MenuSourceType; import org.chromium.ui.base.MenuSourceType;
import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.touch_selection.SelectionEventType;
/** /**
* Unit tests for {@link SelectionPopupController}. * Unit tests for {@link SelectionPopupController}.
...@@ -52,6 +54,7 @@ public class SelectionPopupControllerTest { ...@@ -52,6 +54,7 @@ public class SelectionPopupControllerTest {
private ActionMode mActionMode; private ActionMode mActionMode;
private PackageManager mPackageManager; private PackageManager mPackageManager;
private SmartSelectionMetricsLogger mLogger; private SmartSelectionMetricsLogger mLogger;
private RenderCoordinates mRenderCoordinates;
private static final String MOUNTAIN_FULL = "585 Franklin Street, Mountain View, CA 94041"; private static final String MOUNTAIN_FULL = "585 Franklin Street, Mountain View, CA 94041";
private static final String MOUNTAIN = "Mountain"; private static final String MOUNTAIN = "Mountain";
...@@ -120,9 +123,12 @@ public class SelectionPopupControllerTest { ...@@ -120,9 +123,12 @@ public class SelectionPopupControllerTest {
mView = Mockito.mock(View.class); mView = Mockito.mock(View.class);
mActionMode = Mockito.mock(ActionMode.class); mActionMode = Mockito.mock(ActionMode.class);
mPackageManager = Mockito.mock(PackageManager.class); mPackageManager = Mockito.mock(PackageManager.class);
mRenderCoordinates = Mockito.mock(RenderCoordinates.class);
mLogger = Mockito.mock(SmartSelectionMetricsLogger.class); mLogger = Mockito.mock(SmartSelectionMetricsLogger.class);
when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mWebContents.getRenderCoordinates()).thenReturn(mRenderCoordinates);
when(mRenderCoordinates.getDeviceScaleFactor()).thenReturn(1.f);
mController = SelectionPopupController.createForTesting( mController = SelectionPopupController.createForTesting(
mContext, mWindowAndroid, mWebContents, mView); mContext, mWindowAndroid, mWebContents, mView);
...@@ -399,6 +405,70 @@ public class SelectionPopupControllerTest { ...@@ -399,6 +405,70 @@ public class SelectionPopupControllerTest {
eq("1600 Amphitheatre"), eq(0), isA(SelectionClient.Result.class)); eq("1600 Amphitheatre"), eq(0), isA(SelectionClient.Result.class));
} }
@Test
@Feature({"TextInput", "HandleObserver"})
public void testHandleObserverSelectionHandle() {
SelectionInsertionHandleObserver handleObserver =
Mockito.mock(SelectionInsertionHandleObserver.class);
InOrder order = inOrder(handleObserver);
mController.setSelectionInsertionHandleObserver(handleObserver);
// Selection handles shown.
mController.onSelectionEvent(
SelectionEventType.SELECTION_HANDLES_SHOWN, 0, 0, 0, 0, 0.f, 0.f);
// Selection handles moved, but drag wasn't triggered, so no handleDragStartedOrMoved call.
mController.onSelectionEvent(
SelectionEventType.SELECTION_HANDLES_MOVED, 0, 0, 0, 0, 0.f, 0.f);
order.verify(handleObserver, never()).handleDragStartedOrMoved(anyFloat(), anyFloat());
// Selection handle drag started.
mController.onSelectionEvent(
SelectionEventType.SELECTION_HANDLE_DRAG_STARTED, 0, 0, 0, 0, 0.f, 0.f);
order.verify(handleObserver).handleDragStartedOrMoved(0.f, 0.f);
// Moving.
mController.onSelectionEvent(
SelectionEventType.SELECTION_HANDLES_MOVED, 0, 0, 0, 0, 5.f, 5.f);
order.verify(handleObserver).handleDragStartedOrMoved(5.f, 5.f);
// Selection handle drag stopped.
mController.onSelectionEvent(
SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED, 0, 0, 0, 0, 0.f, 0.f);
order.verify(handleObserver).handleDragStopped();
}
@Test
@Feature({"TextInput", "HandleObserver"})
public void testHandleObserverInsertionHandle() {
SelectionInsertionHandleObserver handleObserver =
Mockito.mock(SelectionInsertionHandleObserver.class);
InOrder order = inOrder(handleObserver);
mController.setSelectionInsertionHandleObserver(handleObserver);
// Insertion handle shown.
mController.onSelectionEvent(
SelectionEventType.INSERTION_HANDLE_SHOWN, 0, 0, 0, 0, 0.f, 0.f);
// Insertion handle moved, but drag wasn't triggered, so no handleDragStartedOrMoved call.
mController.onSelectionEvent(
SelectionEventType.INSERTION_HANDLE_MOVED, 0, 0, 0, 0, 0.f, 0.f);
order.verify(handleObserver, never()).handleDragStartedOrMoved(anyFloat(), anyFloat());
// Insertion handle drag started.
mController.onSelectionEvent(
SelectionEventType.INSERTION_HANDLE_DRAG_STARTED, 0, 0, 0, 0, 0.f, 0.f);
order.verify(handleObserver).handleDragStartedOrMoved(0.f, 0.f);
// Moving.
mController.onSelectionEvent(
SelectionEventType.INSERTION_HANDLE_MOVED, 0, 0, 0, 0, 5.f, 5.f);
order.verify(handleObserver).handleDragStartedOrMoved(5.f, 5.f);
// Insertion handle drag stopped.
mController.onSelectionEvent(
SelectionEventType.INSERTION_HANDLE_DRAG_STOPPED, 0, 0, 0, 0, 0.f, 0.f);
order.verify(handleObserver).handleDragStopped();
}
// Result generated by long press "Amphitheatre" in "1600 Amphitheatre Parkway". // Result generated by long press "Amphitheatre" in "1600 Amphitheatre Parkway".
private SelectionClient.Result resultForAmphitheatre() { private SelectionClient.Result resultForAmphitheatre() {
SelectionClient.Result result = new SelectionClient.Result(); SelectionClient.Result result = new SelectionClient.Result();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment