Disable page refresh on overscroll when precision pointer is attached.
When mouse or touchpad is available, do not allow page refresh on overscroll. tools/autotest.py -C out/android_x64 DeviceInputTest Bug: 352167190 Test: tools/autotest.py -C out/android_x64 SwipeRefreshHandlerTest Change-Id: Ied3360f2ecf13a865a2814dd293ff1a89710976b Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6475171 Auto-Submit: Eric Lok <lokeric@google.com> Reviewed-by: Theresa Sullivan <twellington@chromium.org> Commit-Queue: Eric Lok <lokeric@google.com> Reviewed-by: Sirisha Kavuluru <skavuluru@google.com> Cr-Commit-Position: refs/heads/main@{#1454256}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
b13518c296
commit
f4cd5091b9
chrome/android
java
src
org
chromium
chrome
browser
javatests
src
org
chromium
chrome
browser
ui/android/java/src/org/chromium/ui/base
@ -37,6 +37,7 @@ import org.chromium.third_party.android.swiperefresh.SwipeRefreshLayout;
|
||||
import org.chromium.ui.OverscrollAction;
|
||||
import org.chromium.ui.OverscrollRefreshHandler;
|
||||
import org.chromium.ui.base.BackGestureEventSwipeEdge;
|
||||
import org.chromium.ui.base.DeviceInput;
|
||||
import org.chromium.ui.base.WindowAndroid;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
@ -281,23 +282,26 @@ public class SwipeRefreshHandler extends TabWebContentsUserData
|
||||
public boolean start(
|
||||
@OverscrollAction int type, @BackGestureEventSwipeEdge int initiatingEdge) {
|
||||
mSwipeType = type;
|
||||
if (type == OverscrollAction.PULL_TO_REFRESH) {
|
||||
if (mSwipeRefreshLayout == null) initSwipeRefreshLayout(mTab.getContext());
|
||||
attachSwipeRefreshLayoutIfNecessary();
|
||||
return mSwipeRefreshLayout.start();
|
||||
} else if (type == OverscrollAction.HISTORY_NAVIGATION) {
|
||||
if (mNavigationCoordinator != null) {
|
||||
mNavigationCoordinator.startGesture();
|
||||
// Note: triggerUi returns true as long as the handler is in a valid state, i.e.
|
||||
// even if the navigation direction doesn't have further history entries.
|
||||
boolean navigable = mNavigationCoordinator.triggerUi(initiatingEdge);
|
||||
return navigable;
|
||||
}
|
||||
} else if (type == OverscrollAction.PULL_FROM_BOTTOM_EDGE) {
|
||||
if (mBrowserControls != null) {
|
||||
recordEdgeToEdgeOverscrollFromBottom(mBrowserControls);
|
||||
if (isRefreshOnOverscrollSupported()) {
|
||||
if (type == OverscrollAction.PULL_TO_REFRESH) {
|
||||
if (mSwipeRefreshLayout == null) initSwipeRefreshLayout(mTab.getContext());
|
||||
attachSwipeRefreshLayoutIfNecessary();
|
||||
return mSwipeRefreshLayout.start();
|
||||
} else if (type == OverscrollAction.HISTORY_NAVIGATION) {
|
||||
if (mNavigationCoordinator != null) {
|
||||
mNavigationCoordinator.startGesture();
|
||||
// Note: triggerUi returns true as long as the handler is in a valid state, i.e.
|
||||
// even if the navigation direction doesn't have further history entries.
|
||||
boolean navigable = mNavigationCoordinator.triggerUi(initiatingEdge);
|
||||
return navigable;
|
||||
}
|
||||
} else if (type == OverscrollAction.PULL_FROM_BOTTOM_EDGE) {
|
||||
if (mBrowserControls != null) {
|
||||
recordEdgeToEdgeOverscrollFromBottom(mBrowserControls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mSwipeType = OverscrollAction.NONE;
|
||||
return false;
|
||||
}
|
||||
@ -419,4 +423,22 @@ public class SwipeRefreshHandler extends TabWebContentsUserData
|
||||
sample,
|
||||
BottomControlsStatus.NUM_TOTAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if page refresh on overscroll is supported Wrapped so we can stub behavior in
|
||||
* tests.
|
||||
*
|
||||
* <p>Currently, overscroll to refresh is disabled if a precision pointer device is attached.
|
||||
* For example, this will disable it for touch screen when a mouse is attaached. However
|
||||
* long-term, the plan is to selectively enable for things such as touchscreen only.
|
||||
*
|
||||
* <p>TODO(crbug.com/412465463): enable overscroll refresh for touch even when precision pointer
|
||||
* is attached
|
||||
*
|
||||
* @return true if page refresh on overscroll is supported.
|
||||
*/
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
boolean isRefreshOnOverscrollSupported() {
|
||||
return !DeviceInput.supportsPrecisionPointer();
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,13 @@
|
||||
package org.chromium.chrome.browser;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ -63,6 +66,9 @@ public class SwipeRefreshHandlerTest {
|
||||
private OnRefreshListener mOnRefreshListener;
|
||||
private OnResetListener mOnResetListener;
|
||||
private SwipeRefreshLayout mSwipeRefreshLayout;
|
||||
|
||||
private SwipeRefreshHandler mHandler;
|
||||
|
||||
private final SwipeRefreshHandler.SwipeRefreshLayoutCreator mSwipeRefreshLayoutCreator =
|
||||
context -> {
|
||||
mSwipeRefreshLayout = mock();
|
||||
@ -87,14 +93,18 @@ public class SwipeRefreshHandlerTest {
|
||||
when(mTab.getContext()).thenReturn(activityTestRule.getActivity());
|
||||
when(mTab.getUserDataHost()).thenReturn(new UserDataHost());
|
||||
when(mTab.getContentView()).thenReturn(mock());
|
||||
|
||||
// Limited use of spy() so we can test actual object, while changing some behaviors
|
||||
// dynamically (such as whether mouse is attached or not)
|
||||
mHandler = spy(SwipeRefreshHandler.from(mTab, mSwipeRefreshLayoutCreator));
|
||||
mHandler.initWebContents(mock()); // Needed to enable the overscroll refresh handler.
|
||||
doReturn(true).when(mHandler).isRefreshOnOverscrollSupported(); // Default no mouse/touchpad
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
public void testAccessibilityAnnouncement() {
|
||||
var handler = SwipeRefreshHandler.from(mTab, mSwipeRefreshLayoutCreator);
|
||||
handler.initWebContents(mock()); // Needed to enable the overscroll refresh handler.
|
||||
triggerRefresh(handler);
|
||||
triggerRefresh(mHandler);
|
||||
|
||||
InOrder orderVerifier = inOrder(mSwipeRefreshLayout);
|
||||
orderVerifier
|
||||
@ -104,17 +114,25 @@ public class SwipeRefreshHandlerTest {
|
||||
.verify(mSwipeRefreshLayout, times(1))
|
||||
.setContentDescription(sAccessibilitySwipeRefreshString);
|
||||
|
||||
reset(handler);
|
||||
reset(mHandler);
|
||||
|
||||
orderVerifier.verify(mSwipeRefreshLayout, times(1)).setContentDescription(null);
|
||||
}
|
||||
|
||||
/** Ensures that we do not trigger refresh if precision pointing device is attached */
|
||||
@Test
|
||||
@SmallTest
|
||||
public void testOverscrollButNoRefresh() {
|
||||
doReturn(false).when(mHandler).isRefreshOnOverscrollSupported(); // pointer device attached
|
||||
triggerRefresh(mHandler);
|
||||
// When refresh is NOT triggered, then refresh layout is NOT created
|
||||
assertNull(mSwipeRefreshLayout);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
public void testAccessibilityAnnouncement_swipingASecondTime() {
|
||||
var handler = SwipeRefreshHandler.from(mTab, mSwipeRefreshLayoutCreator);
|
||||
handler.initWebContents(mock()); // Needed to enable the overscroll refresh handler.
|
||||
triggerRefresh(handler);
|
||||
triggerRefresh(mHandler);
|
||||
|
||||
var firstSwipeRefreshLayout = mSwipeRefreshLayout;
|
||||
|
||||
@ -126,11 +144,11 @@ public class SwipeRefreshHandlerTest {
|
||||
.verify(firstSwipeRefreshLayout, times(1))
|
||||
.setContentDescription(sAccessibilitySwipeRefreshString);
|
||||
|
||||
reset(handler);
|
||||
reset(mHandler);
|
||||
|
||||
orderVerifier.verify(mSwipeRefreshLayout, times(1)).setContentDescription(null);
|
||||
|
||||
triggerRefresh(handler);
|
||||
triggerRefresh(mHandler);
|
||||
|
||||
var secondSwipeRefreshLayout = mSwipeRefreshLayout;
|
||||
|
||||
|
@ -52,7 +52,9 @@ public class DeviceInput implements InputDeviceListener {
|
||||
for (int i = 0; i < deviceIds.length; i++) {
|
||||
int deviceId = deviceIds[i];
|
||||
InputDevice device = InputDevice.getDevice(deviceId);
|
||||
if (device != null) mDeviceSnapshotsById.put(deviceId, DeviceSnapshot.from(device));
|
||||
if (device != null) {
|
||||
mDeviceSnapshotsById.put(deviceId, DeviceSnapshot.from(device));
|
||||
}
|
||||
}
|
||||
|
||||
// Register listener to perform cache updates.
|
||||
@ -89,7 +91,9 @@ public class DeviceInput implements InputDeviceListener {
|
||||
return sSupportsAlphabeticKeyboardForTesting;
|
||||
}
|
||||
for (int i = 0; i < mDeviceSnapshotsById.size(); i++) {
|
||||
if (mDeviceSnapshotsById.valueAt(i).supportsAlphabeticKeyboard) return true;
|
||||
if (mDeviceSnapshotsById.valueAt(i).supportsAlphabeticKeyboard) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -117,7 +121,9 @@ public class DeviceInput implements InputDeviceListener {
|
||||
return sSupportsPrecisionPointerForTesting;
|
||||
}
|
||||
for (int i = 0; i < mDeviceSnapshotsById.size(); i++) {
|
||||
if (mDeviceSnapshotsById.valueAt(i).supportsPrecisionPointer) return true;
|
||||
if (mDeviceSnapshotsById.valueAt(i).supportsPrecisionPointer) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -126,15 +132,20 @@ public class DeviceInput implements InputDeviceListener {
|
||||
public void onInputDeviceAdded(int deviceId) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
InputDevice device = InputDevice.getDevice(deviceId);
|
||||
if (device != null) mDeviceSnapshotsById.put(deviceId, DeviceSnapshot.from(device));
|
||||
if (device != null) {
|
||||
mDeviceSnapshotsById.put(deviceId, DeviceSnapshot.from(device));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputDeviceChanged(int deviceId) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
InputDevice device = InputDevice.getDevice(deviceId);
|
||||
if (device != null) mDeviceSnapshotsById.put(deviceId, DeviceSnapshot.from(device));
|
||||
else mDeviceSnapshotsById.remove(deviceId);
|
||||
if (device != null) {
|
||||
mDeviceSnapshotsById.put(deviceId, DeviceSnapshot.from(device));
|
||||
} else {
|
||||
mDeviceSnapshotsById.remove(deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -171,6 +182,7 @@ public class DeviceInput implements InputDeviceListener {
|
||||
return new DeviceSnapshot(
|
||||
/* supportsAlphabeticKeyboard= */ isPhysical
|
||||
&& device.getKeyboardType() == KEYBOARD_TYPE_ALPHABETIC,
|
||||
// SOURCE_MOUSE applies to pointer devices, including mouse and touchpad
|
||||
/* supportsPrecisionPointer= */ isPhysical
|
||||
&& device.supportsSource(SOURCE_MOUSE));
|
||||
}
|
||||
|
Reference in New Issue
Block a user