diff --git a/android/brave_java_sources.gni b/android/brave_java_sources.gni index d1552a35e5e..5b1d5dd9532 100644 --- a/android/brave_java_sources.gni +++ b/android/brave_java_sources.gni @@ -512,6 +512,7 @@ brave_java_sources += brave_fullscreen_java_sources brave_browser_ui_android_appmenu_internal_sources = [ "//brave/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/BraveAppMenu.java", "//brave/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/BraveAppMenuDummySuper.java", + "//brave/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/BraveAppMenuHandlerImpl.java", ] brave_browser_ui_android_default_browser_promo_java_sources = [ diff --git a/android/java/apk_for_test.flags b/android/java/apk_for_test.flags index bb27ac5582f..bd089442f78 100644 --- a/android/java/apk_for_test.flags +++ b/android/java/apk_for_test.flags @@ -1066,6 +1066,17 @@ *** mShareButton; } +-keep class org.chromium.chrome.browser.ui.appmenu.AppMenuHandlerImpl { + public (...); + *** mAppMenu; + *** mAppMenuDragHelper; + *** showAppMenu(...); +} + +-keep class org.chromium.chrome.browser.ui.appmenu.BraveAppMenuHandlerImpl { + public (...); +} + -keep class org.chromium.chrome.browser.ui.AdaptiveToolbarUiCoordinator { *** mContext; *** mActivityTabProvider; diff --git a/android/javatests/org/chromium/chrome/browser/BytecodeTest.java b/android/javatests/org/chromium/chrome/browser/BytecodeTest.java index c120be337e6..d57ccf5ca67 100644 --- a/android/javatests/org/chromium/chrome/browser/BytecodeTest.java +++ b/android/javatests/org/chromium/chrome/browser/BytecodeTest.java @@ -145,6 +145,7 @@ import org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer; import org.chromium.chrome.browser.toolbar.top.ToolbarLayout; import org.chromium.chrome.browser.ui.appmenu.AppMenuBlocker; import org.chromium.chrome.browser.ui.appmenu.AppMenuDelegate; +import org.chromium.chrome.browser.ui.appmenu.AppMenuPropertiesDelegate; import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderCoordinator; import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; import org.chromium.chrome.browser.ui.native_page.NativePageHost; @@ -1121,6 +1122,14 @@ public class BytecodeTest { long.class, boolean.class, boolean.class)); + Assert.assertTrue( + methodExists( + "org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl", + "showAppMenu", + MethodModifier.REGULAR, + boolean.class, + View.class, + boolean.class)); // Check for method type declaration changes here Assert.assertTrue( @@ -1687,6 +1696,19 @@ public class BytecodeTest { constructorsMatch( "org/chromium/chrome/browser/app/flags/ChromeCachedFlags", "org/chromium/chrome/browser/app/flags/BraveCachedFlags")); + Assert.assertTrue( + constructorsMatch( + "org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl", + "org/chromium/chrome/browser/ui/appmenu/BraveAppMenuHandlerImpl", + Context.class, + AppMenuPropertiesDelegate.class, + AppMenuDelegate.class, + View.class, + ActivityLifecycleDispatcher.class, + View.class, + Supplier.class, + WindowAndroid.class, + BrowserControlsStateProvider.class)); Assert.assertTrue( constructorsMatch( "org/chromium/components/cached_flags/CachedFlag", @@ -2534,6 +2556,13 @@ public class BytecodeTest { fieldExists( "org/chromium/chrome/browser/ui/AdaptiveToolbarUiCoordinator", "mAdaptiveToolbarButtonController")); + Assert.assertTrue( + fieldExists( + "org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl", "mAppMenu")); + Assert.assertTrue( + fieldExists( + "org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl", + "mAppMenuDragHelper")); } @Test diff --git a/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/BraveAppMenuHandlerImpl.java b/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/BraveAppMenuHandlerImpl.java new file mode 100644 index 00000000000..43ff01be306 --- /dev/null +++ b/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/BraveAppMenuHandlerImpl.java @@ -0,0 +1,67 @@ +/* Copyright (c) 2025 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package org.chromium.chrome.browser.ui.appmenu; + +import android.content.Context; +import android.graphics.Rect; +import android.view.View; + +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; +import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider; +import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; +import org.chromium.ui.base.WindowAndroid; + +import java.util.function.Supplier; + +/** + * Brave extension for {@link AppMenuHandlerImpl}. See {@code BraveAppMenuHandlerImplClassAdapter} + * for bytecode manipulation logic. See {@code BytecodeTest} for bytecode related tests. + */ +@NullMarked +class BraveAppMenuHandlerImpl extends AppMenuHandlerImpl { + /** Will be deleted in bytecode, value from the parent class will be used instead. */ + private @Nullable AppMenu mAppMenu; + + /** Will be deleted in bytecode, value from the parent class will be used instead. */ + private @Nullable AppMenuDragHelper mAppMenuDragHelper; + + public BraveAppMenuHandlerImpl( + Context context, + AppMenuPropertiesDelegate delegate, + AppMenuDelegate appMenuDelegate, + View decorView, + ActivityLifecycleDispatcher activityLifecycleDispatcher, + View hardwareButtonAnchorView, + Supplier appRect, + WindowAndroid windowAndroid, + BrowserControlsStateProvider browserControlsStateProvider) { + super( + context, + delegate, + appMenuDelegate, + decorView, + activityLifecycleDispatcher, + hardwareButtonAnchorView, + appRect, + windowAndroid, + browserControlsStateProvider); + } + + @Override + boolean showAppMenu(@Nullable View anchorView, boolean startDragging) { + final boolean show = super.showAppMenu(anchorView, startDragging); + if (show) { + assert mAppMenuDragHelper != null; + mAppMenuDragHelper.finishDragging(); + assert mAppMenu != null; + if (mAppMenu.getListView() != null) { + mAppMenu.getListView().scrollTo(0, 0); + } + } + return show; + } +} diff --git a/build/android/bytecode/BUILD.gn b/build/android/bytecode/BUILD.gn index f28a4d4553b..87135584d98 100644 --- a/build/android/bytecode/BUILD.gn +++ b/build/android/bytecode/BUILD.gn @@ -15,6 +15,7 @@ java_binary("java_bytecode_rewriter") { "//brave/build/android/bytecode/java/org/brave/bytecode/BraveAdaptiveToolbarUiCoordinatorClassAdapter.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveAppHooksClassAdapter.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveAppMenuClassAdapter.java", + "//brave/build/android/bytecode/java/org/brave/bytecode/BraveAppMenuHandlerImplClassAdapter.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveApplicationImplBaseClassAdapter.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveAutocompleteCoordinatorClassAdapter.java", "//brave/build/android/bytecode/java/org/brave/bytecode/BraveAutocompleteEditTextClassAdapter.java", diff --git a/build/android/bytecode/java/org/brave/bytecode/BraveAppMenuHandlerImplClassAdapter.java b/build/android/bytecode/java/org/brave/bytecode/BraveAppMenuHandlerImplClassAdapter.java new file mode 100644 index 00000000000..0e947472595 --- /dev/null +++ b/build/android/bytecode/java/org/brave/bytecode/BraveAppMenuHandlerImplClassAdapter.java @@ -0,0 +1,25 @@ +/* Copyright (c) 2025 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package org.brave.bytecode; + +import org.objectweb.asm.ClassVisitor; + +public class BraveAppMenuHandlerImplClassAdapter extends BraveClassVisitor { + static String sAppMenuHandlerImpl = "org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl"; + static String sBraveAppMenuHandlerImpl = + "org/chromium/chrome/browser/ui/appmenu/BraveAppMenuHandlerImpl"; + + public BraveAppMenuHandlerImplClassAdapter(ClassVisitor visitor) { + super(visitor); + redirectConstructor(sAppMenuHandlerImpl, sBraveAppMenuHandlerImpl); + + deleteField(sBraveAppMenuHandlerImpl, "mAppMenu"); + makeProtectedField(sAppMenuHandlerImpl, "mAppMenu"); + + deleteField(sBraveAppMenuHandlerImpl, "mAppMenuDragHelper"); + makeProtectedField(sAppMenuHandlerImpl, "mAppMenuDragHelper"); + } +} diff --git a/build/android/bytecode/java/org/brave/bytecode/BraveClassAdapter.java b/build/android/bytecode/java/org/brave/bytecode/BraveClassAdapter.java index 309f571dbbb..8a92e718d0e 100644 --- a/build/android/bytecode/java/org/brave/bytecode/BraveClassAdapter.java +++ b/build/android/bytecode/java/org/brave/bytecode/BraveClassAdapter.java @@ -16,6 +16,7 @@ public class BraveClassAdapter { chain = new BraveAdaptiveToolbarUiCoordinatorClassAdapter(chain); chain = new BraveAppHooksClassAdapter(chain); chain = new BraveAppMenuClassAdapter(chain); + chain = new BraveAppMenuHandlerImplClassAdapter(chain); chain = new BraveBaseCustomTabActivityClassAdapter(chain); chain = new BraveCustomTabActivityClassAdapter(chain); chain = new BraveApplicationImplBaseClassAdapter(chain);