diff --git a/android/java/org/chromium/chrome/browser/firstrun/WelcomeOnboardingActivity.java b/android/java/org/chromium/chrome/browser/firstrun/WelcomeOnboardingActivity.java index 10f17e7f37b..8c21de8429a 100644 --- a/android/java/org/chromium/chrome/browser/firstrun/WelcomeOnboardingActivity.java +++ b/android/java/org/chromium/chrome/browser/firstrun/WelcomeOnboardingActivity.java @@ -52,6 +52,7 @@ import org.chromium.base.ApplicationStatus; import org.chromium.base.ApplicationStatus.ActivityStateListener; import org.chromium.base.BravePreferenceKeys; import org.chromium.base.Log; +import org.chromium.brave.browser.customize_menu.CustomizeBraveMenu; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; @@ -234,6 +235,7 @@ public class WelcomeOnboardingActivity extends FirstRunActivityBase ChromeSharedPreferences.getInstance() .writeBoolean(OnboardingPrefManager.SHOULD_SHOW_SEARCH_WIDGET_PROMO, true); } + CustomizeBraveMenu.initDefaultInvisibleItems(getResources()); OnboardingPrefManager.getInstance().setP3aOnboardingShown(true); FirstRunStatus.setFirstRunFlowComplete(true); diff --git a/browser/customize_menu/android/java/src/org/chromium/brave/browser/customize_menu/CustomizeBraveMenu.java b/browser/customize_menu/android/java/src/org/chromium/brave/browser/customize_menu/CustomizeBraveMenu.java index a0122a4f783..184de4b96c1 100644 --- a/browser/customize_menu/android/java/src/org/chromium/brave/browser/customize_menu/CustomizeBraveMenu.java +++ b/browser/customize_menu/android/java/src/org/chromium/brave/browser/customize_menu/CustomizeBraveMenu.java @@ -22,6 +22,7 @@ import androidx.annotation.DrawableRes; import androidx.annotation.IdRes; import androidx.annotation.Nullable; import androidx.annotation.Size; +import androidx.annotation.VisibleForTesting; import androidx.appcompat.content.res.AppCompatResources; import androidx.core.content.ContextCompat; import androidx.preference.Preference; @@ -88,6 +89,9 @@ public class CustomizeBraveMenu { public static final int PREFERENCE_MENU_ICON_SIZE_DP = 24; + // Menu items initialized as hidden unless an explicit user preference already exists. + private static final int[] MENU_IDS_INVISIBLE_BY_DEFAULT = {R.id.exit_id}; + /** * Static mapping of menu item IDs to their corresponding drawable resource IDs. Uses * SparseIntArray for optimal performance and memory efficiency on Android when mapping resource @@ -297,10 +301,10 @@ public class CustomizeBraveMenu { * @param itemId the resource ID of the menu item to check * @return {@code true} if the item should be visible, {@code false} if it should be hidden */ - public static boolean isVisible(final Resources resource, final int itemId) { + public static boolean isVisible(final Resources resources, final int itemId) { String resourceName; try { - resourceName = resource.getResourceEntryName(itemId); + resourceName = resources.getResourceEntryName(itemId); } catch (Resources.NotFoundException notFoundException) { assert false : "Resource not found for item with ID " + itemId; // We are referencing a resource that does not @@ -309,12 +313,57 @@ public class CustomizeBraveMenu { return true; } return ChromeSharedPreferences.getInstance() - .readBoolean( - String.format( - Locale.ENGLISH, - CUSTOMIZABLE_BRAVE_MENU_ITEM_ID_FORMAT, - resourceName), - true); + .readBoolean(getFormattedSharedPreferenceMenuItemName(resourceName), true); + } + + /** + * Initializes menu items in the {@link MENU_IDS_INVISIBLE_BY_DEFAULT} array as invisible by + * default. This method should be called once during onboarding process. + * + * @see org.chromium.chrome.browser.firstrun.WelcomeOnboardingActivity + * @param resources the resources used to access the entry name of a given menu item ID + */ + public static void initDefaultInvisibleItems(final Resources resources) { + for (final int menuItemId : MENU_IDS_INVISIBLE_BY_DEFAULT) { + initAsInvisible(resources, menuItemId); + } + } + + /** + * Initializes a menu item as invisible in the user preferences. This method is a no-op when a + * preference already exists. + * + * @param resources the resources used to access the entry name of a given menu item ID + * @param itemId the resource ID of the menu item to initialize as invisible + */ + @VisibleForTesting + static void initAsInvisible(final Resources resources, final int itemId) { + String resourceName; + try { + resourceName = resources.getResourceEntryName(itemId); + } catch (Resources.NotFoundException notFoundException) { + assert false : "Resource not found for item with ID " + itemId; + // We are referencing a resource that does not + // exist. This should never happen. + // Leave visibility unchanged by returning. + return; + } + + if (!ChromeSharedPreferences.getInstance() + .contains(getFormattedSharedPreferenceMenuItemName(resourceName))) { + ChromeSharedPreferences.getInstance() + .writeBoolean(getFormattedSharedPreferenceMenuItemName(resourceName), false); + } + } + + /** + * Builds the shared-preference key used to persist visibility state for a menu item. + * + * @param resourceName the Android resource entry name for the menu item ID + * @return the formatted shared-preference key for that menu item + */ + private static String getFormattedSharedPreferenceMenuItemName(final String resourceName) { + return String.format(Locale.ENGLISH, CUSTOMIZABLE_BRAVE_MENU_ITEM_ID_FORMAT, resourceName); } /** diff --git a/browser/customize_menu/android/javatests/src/org/chromium/brave/browser/customize_menu/CustomizeBraveMenuTest.java b/browser/customize_menu/android/javatests/src/org/chromium/brave/browser/customize_menu/CustomizeBraveMenuTest.java index 0b13590b275..4e41d4a497f 100644 --- a/browser/customize_menu/android/javatests/src/org/chromium/brave/browser/customize_menu/CustomizeBraveMenuTest.java +++ b/browser/customize_menu/android/javatests/src/org/chromium/brave/browser/customize_menu/CustomizeBraveMenuTest.java @@ -61,6 +61,7 @@ public class CustomizeBraveMenuTest { mContext = ApplicationProvider.getApplicationContext(); mModelList = new MVCListAdapter.ModelList(); mResources = ApplicationProvider.getApplicationContext().getResources(); + ChromeSharedPreferences.getInstance().removeKey(getMenuItemPrefKey(R.id.exit_id)); } @Test @@ -97,6 +98,33 @@ public class CustomizeBraveMenuTest { mModelList.get(0).model.get(AppMenuItemProperties.MENU_ITEM_ID)); } + @Test + public void testApplyCustomization_ExitIsInvisibleByDefault() { + PropertyModel visibleItem = + new PropertyModel.Builder(AppMenuItemProperties.ALL_KEYS) + .with(AppMenuItemProperties.MENU_ITEM_ID, R.id.new_tab_menu_id) + .with(AppMenuItemProperties.TITLE, "New Tab") + .build(); + PropertyModel exitItem = + new PropertyModel.Builder(AppMenuItemProperties.ALL_KEYS) + .with(AppMenuItemProperties.MENU_ITEM_ID, R.id.exit_id) + .with(AppMenuItemProperties.TITLE, "Exit") + .build(); + + mModelList.add( + new MVCListAdapter.ListItem(AppMenuHandler.AppMenuItemType.STANDARD, visibleItem)); + mModelList.add( + new MVCListAdapter.ListItem(AppMenuHandler.AppMenuItemType.STANDARD, exitItem)); + + CustomizeBraveMenu.applyCustomization(mResources, mModelList); + + assertEquals(1, mModelList.size()); + assertEquals( + R.id.new_tab_menu_id, + mModelList.get(0).model.get(AppMenuItemProperties.MENU_ITEM_ID)); + assertFalse(CustomizeBraveMenu.isVisible(mResources, R.id.exit_id)); + } + @Test public void testApplyCustomization_PreservesCustomizeAndSettingsItems() { // Add customize menu and settings items (should not be removed). @@ -315,6 +343,28 @@ public class CustomizeBraveMenuTest { assertFalse(CustomizeBraveMenu.isVisible(mResources, R.id.downloads_menu_id)); } + @Test + public void testInitAsInvisible_SetsFalse_WhenPreferenceIsMissing() { + String key = getMenuItemPrefKey(R.id.exit_id); + ChromeSharedPreferences.getInstance().removeKey(key); + assertFalse(ChromeSharedPreferences.getInstance().contains(key)); + + CustomizeBraveMenu.initAsInvisible(mResources, R.id.exit_id); + + assertTrue(ChromeSharedPreferences.getInstance().contains(key)); + assertFalse(ChromeSharedPreferences.getInstance().readBoolean(key, true)); + } + + @Test + public void testInitAsInvisible_DoesNotOverrideExistingPreference() { + String key = getMenuItemPrefKey(R.id.exit_id); + ChromeSharedPreferences.getInstance().writeBoolean(key, true); + + CustomizeBraveMenu.initAsInvisible(mResources, R.id.exit_id); + + assertTrue(ChromeSharedPreferences.getInstance().readBoolean(key, false)); + } + @Test public void testGetDrawableResFromMenuItemId_ValidIds() { // Test common menu items. @@ -422,4 +472,10 @@ public class CustomizeBraveMenuTest { assertEquals(AppMenuHandler.AppMenuItemType.DIVIDER, mModelList.get(3).type); assertEquals(AppMenuHandler.AppMenuItemType.STANDARD, mModelList.get(4).type); } + + private String getMenuItemPrefKey(int menuItemId) { + return String.format( + BravePreferenceKeys.CUSTOMIZABLE_BRAVE_MENU_ITEM_ID_FORMAT, + mResources.getResourceEntryName(menuItemId)); + } }