0

Add MotionEvent button state as a param of tab strip click handlers.

This CL is a no-op. It only changes the interface.

Click events will be handled in slightly different ways depending on
whether they are from pheripherals. The value of
MotionEvent.getButtonState() can be used to differentiate between click
events.

Therefore, we add MotionEvent button state as a parameter of:
  * VirtualView.handleClick(), and
  * StripLayoutViewOnClickHandler.onClick().

As not all clicks have a button state, and the Android framework doesn't
have a constant like "MotionEvent.BUTTON_NONE = 0", this CL needs to
introduce this constant definition as
MotionEventUtils.MOTION_EVENT_BUTTON_NONE, which will be passed to the
click handlers if a click isn't detected via MotionEvents or has no
MotionEvent button state.

Bug: 375468032
Change-Id: I662c856d5c9fc391b15157c883affb311ae05dc9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6507182
Commit-Queue: Linyu He <linyuh@google.com>
Reviewed-by: Wenyu Fu <wenyufu@chromium.org>
Reviewed-by: Neil Coronado <nemco@google.com>
Cr-Commit-Position: refs/heads/main@{#1455914}
This commit is contained in:
Linyu He
2025-05-05 13:26:16 -07:00
committed by Chromium LUCI CQ
parent 8129155eb3
commit b712d85c66
8 changed files with 45 additions and 16 deletions
chrome
android
java
javatests
src
org
chromium
chrome
junit
src
org
chromium
chrome
browser
compositor
browser
ui
android
layouts
java
src
org
chromium
chrome
browser
layouts
ui/android/java/src/org/chromium/ui/util

@ -99,6 +99,7 @@ import org.chromium.ui.mojom.VirtualKeyboardMode;
import org.chromium.ui.resources.AndroidResourceType;
import org.chromium.ui.resources.ResourceManager;
import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
import org.chromium.ui.util.MotionEventUtils;
import org.chromium.url.GURL;
import java.util.ArrayList;
@ -1718,7 +1719,10 @@ public class CompositorViewHolder extends FrameLayout
if (isButtonActivate(event)
&& 0 < mKeyboardFocusIndex
&& mKeyboardFocusIndex < keyboardFocusableViews.size()) {
keyboardFocusableViews.get(mKeyboardFocusIndex).handleClick(LayoutManagerImpl.time());
keyboardFocusableViews
.get(mKeyboardFocusIndex)
.handleClick(
LayoutManagerImpl.time(), MotionEventUtils.MOTION_EVENT_BUTTON_NONE);
return true;
}
return super.dispatchKeyEvent(event);
@ -1827,7 +1831,11 @@ public class CompositorViewHolder extends FrameLayout
int virtualViewId, int action, Bundle arguments) {
switch (action) {
case AccessibilityNodeInfoCompat.ACTION_CLICK:
mVirtualViews.get(virtualViewId).handleClick(LayoutManagerImpl.time());
mVirtualViews
.get(virtualViewId)
.handleClick(
LayoutManagerImpl.time(),
MotionEventUtils.MOTION_EVENT_BUTTON_NONE);
return true;
}

@ -2760,7 +2760,7 @@ public class StripLayoutHelper
if (MotionEventUtils.isSecondaryClick(buttons)) {
showContextMenu(clickedView);
} else {
clickedView.handleClick(time);
clickedView.handleClick(time, buttons);
}
}
@ -2787,7 +2787,7 @@ public class StripLayoutHelper
/** Handle view click */
@Override
public void onClick(long time, StripLayoutView view) {
public void onClick(long time, StripLayoutView view, int motionEventButtonState) {
if (view instanceof StripLayoutTab tab) {
handleTabClick(tab);
} else if (view instanceof StripLayoutGroupTitle groupTitle) {

@ -302,7 +302,7 @@ public class StripLayoutHelperManager
}
long time = time();
if (mModelSelectorButton != null && mModelSelectorButton.click(x, y, buttons)) {
mModelSelectorButton.handleClick(time);
mModelSelectorButton.handleClick(time, buttons);
return;
}
getActiveStripLayoutHelper().click(time(), x, y, buttons);
@ -487,7 +487,7 @@ public class StripLayoutHelperManager
if (!ChromeFeatureList.sTabStripIncognitoMigration.isEnabled()) {
StripLayoutViewOnClickHandler selectorClickHandler =
(time, view) -> handleModelSelectorButtonClick();
(time, view, motionEventButtonState) -> handleModelSelectorButtonClick();
StripLayoutViewOnKeyboardFocusHandler selectorKeyboardFocusHandler =
(isFocused, view) -> {
getActiveStripLayoutHelper().onKeyboardFocus(isFocused, view);

@ -8,6 +8,7 @@ import android.content.Context;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.FloatProperty;
import android.view.MotionEvent;
import androidx.annotation.ColorInt;
@ -31,8 +32,11 @@ public abstract class StripLayoutView implements VirtualView {
*
* @param time The time of the click action.
* @param view View that received the click.
* @param motionEventButtonState {@link MotionEvent#getButtonState()} at the moment of the
* click if the click is detected via motion events; otherwise, this parameter is {@link
* org.chromium.ui.util.MotionEventUtils#MOTION_EVENT_BUTTON_NONE}.
*/
void onClick(long time, StripLayoutView view);
void onClick(long time, StripLayoutView view, int motionEventButtonState);
}
/** Handler for keyboard focus on VirtualViews. */
@ -392,8 +396,8 @@ public abstract class StripLayoutView implements VirtualView {
}
@Override
public void handleClick(long time) {
mOnClickHandler.onClick(time, this);
public void handleClick(long time, int motionEventButtonState) {
mOnClickHandler.onClick(time, this, motionEventButtonState);
}
/** Returns cached touch target bounds. */

@ -39,6 +39,7 @@ import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.chrome.test.util.TabStripUtils;
import org.chromium.content_public.browser.test.util.DOMUtils;
import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.util.MotionEventUtils;
import java.util.concurrent.TimeoutException;
@ -148,6 +149,7 @@ public class UndoIntegrationTest {
private void closeTabViaButton(ChromeTabbedActivity cta, int tabId) {
final StripLayoutTab tab =
TabStripUtils.findStripLayoutTab(cta, /* incognito= */ false, tabId);
tab.getCloseButton().handleClick(SystemClock.uptimeMillis());
tab.getCloseButton()
.handleClick(SystemClock.uptimeMillis(), MotionEventUtils.MOTION_EVENT_BUTTON_NONE);
}
}

@ -148,6 +148,7 @@ import org.chromium.ui.dragdrop.DragDropGlobalState;
import org.chromium.ui.dragdrop.DragDropGlobalState.TrackerToken;
import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.ui.shadows.ShadowAppCompatResources;
import org.chromium.ui.util.MotionEventUtils;
import org.chromium.ui.util.XrUtils;
import org.chromium.ui.widget.RectProvider;
import org.chromium.url.GURL;
@ -5296,7 +5297,7 @@ public class StripLayoutHelperTest {
// Fake a click on the group indicator.
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
mStripLayoutHelper.onClick(TIMESTAMP, views[0]);
mStripLayoutHelper.onClick(TIMESTAMP, views[0], MotionEventUtils.MOTION_EVENT_BUTTON_NONE);
// Verify the proper event was sent to the TabGroupModelFilter.
verify(mTabGroupModelFilter)
@ -5321,7 +5322,7 @@ public class StripLayoutHelperTest {
StripLayoutView[] views = mStripLayoutHelper.getStripLayoutViewsForTesting();
mStripLayoutHelper.collapseTabGroupForTesting((StripLayoutGroupTitle) views[0], true);
when(mTabGroupModelFilter.getTabGroupCollapsed(0)).thenReturn(true);
mStripLayoutHelper.onClick(TIMESTAMP, views[0]);
mStripLayoutHelper.onClick(TIMESTAMP, views[0], MotionEventUtils.MOTION_EVENT_BUTTON_NONE);
// Verify the proper event was sent to the TabGroupModelFilter.
verify(mTabGroupModelFilter)

@ -4,12 +4,13 @@
package org.chromium.chrome.browser.layouts.components;
import android.graphics.RectF;
import android.view.MotionEvent;
import org.chromium.build.annotations.NullMarked;
/**
* {@link VirtualView} is the minimal interface that provides information for
* building accessibility events.
* {@link VirtualView} is the minimal interface that provides information for building accessibility
* events.
*/
@NullMarked
public interface VirtualView {
@ -48,8 +49,11 @@ public interface VirtualView {
* Notifies the view to handle the click action.
*
* @param time The time of the click action.
* @param motionEventButtonState {@link MotionEvent#getButtonState()} at the moment of the click
* if the click is detected via motion events; otherwise, this parameter is {@link
* org.chromium.ui.util.MotionEventUtils#MOTION_EVENT_BUTTON_NONE}.
*/
void handleClick(long time);
void handleClick(long time, int motionEventButtonState);
/**
* Set keyboard focus state of {@link VirtualView} to {@param isFocused}.

@ -22,6 +22,16 @@ import java.lang.reflect.Method;
*/
@NullMarked
public class MotionEventUtils {
/**
* {@link MotionEvent} button constant: no button.
*
* <p>This complements the {@code MotionEvent.BUTTON_*} definitions in Android framework.
* Currently {@link MotionEvent#getButtonState()} returns 0 when no button is pressed, but the
* framework doesn't define a constant for this.
*
* @see MotionEvent#getButtonState()
*/
public static final int MOTION_EVENT_BUTTON_NONE = 0;
private static @Nullable Method sGetTimeNanoMethod;
private static boolean sFailedReflection;
@ -123,7 +133,7 @@ public class MotionEventUtils {
/** Checks if the motion event was generated by touch (buttons == 0) OR via a primary button. */
public static boolean isTouchOrPrimaryButton(int buttons) {
return buttons == 0 || isPrimaryButton(buttons);
return buttons == MOTION_EVENT_BUTTON_NONE || isPrimaryButton(buttons);
}
/**