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") {
"java/src/org/chromium/content/browser/ChildProcessCreationParams.java",
"java/src/org/chromium/content/browser/ChildProcessLauncherHelper.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/ContentNfcDelegate.java",
"java/src/org/chromium/content/browser/ContentVideoView.java",
......@@ -146,6 +147,7 @@ android_library("content_java") {
"java/src/org/chromium/content/browser/ScreenOrientationProvider.java",
"java/src/org/chromium/content/browser/SelectionEventProxyImpl.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/SmartClipProvider.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 {
// Whether a scroll is in progress.
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.
* @param context Context for action mode.
......@@ -200,6 +208,9 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
mLastSelectedText = "";
mHandleObserver = ContentClassFactory.get().createHandleObserver(view);
mDragStarted = false;
if (initializeNative) nativeInit(webContents);
}
......@@ -1048,13 +1059,17 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
}
// All coordinates are in DIP.
@VisibleForTesting
@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) {
// Ensure the provided selection coordinates form a non-empty rect, as required by
// the selection action mode.
if (left == right) ++right;
if (top == bottom) ++bottom;
final float deviceScale = getDeviceScaleFactor();
boundMiddlePointX *= deviceScale;
boundMiddlePointY *= deviceScale;
switch (eventType) {
case SelectionEventType.SELECTION_HANDLES_SHOWN:
break;
......@@ -1062,6 +1077,9 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
case SelectionEventType.SELECTION_HANDLES_MOVED:
mSelectionRect.set(left, top, right, bottom);
invalidateContentRect();
if (mDragStarted && mHandleObserver != null) {
mHandleObserver.handleDragStartedOrMoved(boundMiddlePointX, boundMiddlePointY);
}
break;
case SelectionEventType.SELECTION_HANDLES_CLEARED:
......@@ -1077,10 +1095,18 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
case SelectionEventType.SELECTION_HANDLE_DRAG_STARTED:
hideActionMode(true);
mDragStarted = true;
if (mHandleObserver != null) {
mHandleObserver.handleDragStartedOrMoved(boundMiddlePointX, boundMiddlePointY);
}
break;
case SelectionEventType.SELECTION_HANDLE_DRAG_STOPPED:
mWebContents.showContextMenuAtTouchHandle(left, bottom);
if (mHandleObserver != null) {
mHandleObserver.handleDragStopped();
}
mDragStarted = false;
break;
case SelectionEventType.INSERTION_HANDLE_SHOWN:
......@@ -1095,6 +1121,9 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
} else {
destroyPastePopup();
}
if (mDragStarted && mHandleObserver != null) {
mHandleObserver.handleDragStartedOrMoved(boundMiddlePointX, boundMiddlePointY);
}
break;
case SelectionEventType.INSERTION_HANDLE_TAPPED:
......@@ -1116,6 +1145,10 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
case SelectionEventType.INSERTION_HANDLE_DRAG_STARTED:
mWasPastePopupShowingOnInsertionDragStart = isPastePopupShowing();
destroyPastePopup();
mDragStarted = true;
if (mHandleObserver != null) {
mHandleObserver.handleDragStartedOrMoved(boundMiddlePointX, boundMiddlePointY);
}
break;
case SelectionEventType.INSERTION_HANDLE_DRAG_STOPPED:
......@@ -1124,6 +1157,10 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
mSelectionRect.left, mSelectionRect.bottom);
}
mWasPastePopupShowingOnInsertionDragStart = false;
if (mHandleObserver != null) {
mHandleObserver.handleDragStopped();
}
mDragStarted = false;
break;
default:
......@@ -1131,7 +1168,6 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
}
if (mSelectionClient != null) {
final float deviceScale = getDeviceScaleFactor();
int xAnchorPix = (int) (mSelectionRect.left * deviceScale);
int yAnchorPix = (int) (mSelectionRect.bottom * deviceScale);
mSelectionClient.onSelectionEvent(eventType, xAnchorPix, yAnchorPix);
......@@ -1172,6 +1208,7 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
/**
* Sets the client that implements selection augmenting functionality, or null if none exists.
*/
@VisibleForTesting
void setSelectionClient(@Nullable SelectionClient selectionClient) {
mSelectionClient = selectionClient;
if (mSelectionClient != null) {
......@@ -1184,6 +1221,15 @@ public class SelectionPopupController extends ActionModeCallbackHelper {
assert !mHidden;
}
/**
* Sets the handle observer, or null if none exists.
*/
@VisibleForTesting
void setSelectionInsertionHandleObserver(
@Nullable SelectionInsertionHandleObserver handleObserver) {
mHandleObserver = handleObserver;
}
@CalledByNative
private void onShowUnhandledTapUIIfNeeded(int x, int y) {
if (x < 0 || y < 0 || mView.getWidth() < x || mView.getHeight() < y) return;
......
......@@ -8,6 +8,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
......@@ -37,6 +38,7 @@ import org.chromium.content_public.browser.SelectionMetricsLogger;
import org.chromium.testing.local.LocalRobolectricTestRunner;
import org.chromium.ui.base.MenuSourceType;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.touch_selection.SelectionEventType;
/**
* Unit tests for {@link SelectionPopupController}.
......@@ -52,6 +54,7 @@ public class SelectionPopupControllerTest {
private ActionMode mActionMode;
private PackageManager mPackageManager;
private SmartSelectionMetricsLogger mLogger;
private RenderCoordinates mRenderCoordinates;
private static final String MOUNTAIN_FULL = "585 Franklin Street, Mountain View, CA 94041";
private static final String MOUNTAIN = "Mountain";
......@@ -120,9 +123,12 @@ public class SelectionPopupControllerTest {
mView = Mockito.mock(View.class);
mActionMode = Mockito.mock(ActionMode.class);
mPackageManager = Mockito.mock(PackageManager.class);
mRenderCoordinates = Mockito.mock(RenderCoordinates.class);
mLogger = Mockito.mock(SmartSelectionMetricsLogger.class);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mWebContents.getRenderCoordinates()).thenReturn(mRenderCoordinates);
when(mRenderCoordinates.getDeviceScaleFactor()).thenReturn(1.f);
mController = SelectionPopupController.createForTesting(
mContext, mWindowAndroid, mWebContents, mView);
......@@ -399,6 +405,70 @@ public class SelectionPopupControllerTest {
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".
private SelectionClient.Result resultForAmphitheatre() {
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