Commit 676c7b48 authored by Ionut Cristian Bedregeanu's avatar Ionut Cristian Bedregeanu Committed by mergify[bot]
Browse files

For #10559 - Adds `showAddonsInMenu` in WebExtensionBrowserMenuBuilder

Adds `showAddonsInMenu` in WebExtensionBrowserMenuBuilder to allow the option of removing `Add-ons`
item even if another extensions are displayed
Add unit tests to test if 'Add-ons' item visibility is updated properly
parent 6d8e7ad4
......@@ -12,14 +12,14 @@ import mozilla.components.browser.menu.item.BrowserMenuDivider
import mozilla.components.browser.menu.item.BrowserMenuImageText
import mozilla.components.browser.menu.item.NO_ID
import mozilla.components.browser.menu.item.ParentBrowserMenuItem
import mozilla.components.browser.menu.item.WebExtensionBrowserMenuItem
import mozilla.components.browser.menu.item.WebExtensionPlaceholderMenuItem
import mozilla.components.browser.state.selector.selectedTab
import mozilla.components.browser.state.store.BrowserStore
/**
* Browser menu builder with web extension support. It allows [WebExtensionBrowserMenu] to add
* web extension browser actions in a nested menu item. If there are no web extensions installed,
* the web extension menu item would return an add-on manager menu item instead.
* web extension browser actions in a nested menu item.
*
* @param store [BrowserStore] required to render web extension browser actions
* @param style Indicates how items should look like.
......@@ -28,6 +28,7 @@ import mozilla.components.browser.state.store.BrowserStore
* to specify the place the extensions sub-menu should be inserted. True if web extension sub menu
* appear at the top (start) of the menu, false if web extensions appear at the bottom of the menu.
* Default to false (bottom). This is also used to decide the back press menu item placement at top or bottom.
* @param showAddonsInMenu Whether to show the 'Add-ons' item in menu
*/
@Suppress("LongParameterList")
class WebExtensionBrowserMenuBuilder(
......@@ -37,9 +38,12 @@ class WebExtensionBrowserMenuBuilder(
private val store: BrowserStore,
private val style: Style = Style(),
private val onAddonsManagerTapped: () -> Unit = {},
private val appendExtensionSubMenuAtStart: Boolean = false
private val appendExtensionSubMenuAtStart: Boolean = false,
private val showAddonsInMenu: Boolean = true
) : BrowserMenuBuilder(items, extras, endOfMenuAlwaysVisible) {
private val finalList = items.toMutableList()
/**
* Builds and returns a browser menu with combination of [items] and web extension browser actions.
*/
......@@ -47,24 +51,40 @@ class WebExtensionBrowserMenuBuilder(
val extensionMenuItems =
WebExtensionBrowserMenu.getOrUpdateWebExtensionMenuItems(store.state, store.state.selectedTab)
val finalList = items.toMutableList()
val filteredExtensionMenuItems = extensionMenuItems.filter { webExtensionBrowserMenuItem ->
// Check if we have a placeholder
val index = finalList.indexOfFirst { browserMenuItem ->
(browserMenuItem as? WebExtensionPlaceholderMenuItem)?.id == webExtensionBrowserMenuItem.id
}
// Replace placeholder with corresponding web extension, and remove it from extensions menu list
if (index != -1) {
webExtensionBrowserMenuItem.setIconTint(
(finalList[index] as? WebExtensionPlaceholderMenuItem)?.iconTintColorResource
)
finalList[index] = webExtensionBrowserMenuItem
}
index == -1
replaceMenuPlaceholderWithExtensions(webExtensionBrowserMenuItem)
}
val webExtMenuItem = if (filteredExtensionMenuItems.isNotEmpty()) {
val menuItems = if (showAddonsInMenu) {
showAddonsMenuItems(context, filteredExtensionMenuItems)
} else {
finalList
}
val adapter = BrowserMenuAdapter(context, menuItems)
return BrowserMenu(adapter)
}
private fun replaceMenuPlaceholderWithExtensions(menuItem: WebExtensionBrowserMenuItem): Boolean {
// Check if we have a placeholder
val index = items.indexOfFirst { browserMenuItem ->
(browserMenuItem as? WebExtensionPlaceholderMenuItem)?.id == menuItem.id
}
// Replace placeholder with corresponding web extension, and remove it from extensions menu list
if (index != -1) {
menuItem.setIconTint(
(finalList[index] as? WebExtensionPlaceholderMenuItem)?.iconTintColorResource
)
finalList[index] = menuItem
}
return index == -1
}
private fun showAddonsMenuItems(
context: Context,
filteredExtensionMenuItems: List<WebExtensionBrowserMenuItem>
): List<BrowserMenuItem> {
val addonsMenuItem = if (filteredExtensionMenuItems.isNotEmpty()) {
val backPressMenuItem = BackPressMenuItem(
label = context.getString(R.string.mozac_browser_menu_addons),
imageResource = style.backPressMenuItemDrawableRes,
......@@ -81,12 +101,12 @@ class WebExtensionBrowserMenuBuilder(
val webExtSubMenuItems = if (appendExtensionSubMenuAtStart) {
listOf(backPressMenuItem) + BrowserMenuDivider() +
filteredExtensionMenuItems +
BrowserMenuDivider() + addonsManagerMenuItem
filteredExtensionMenuItems +
BrowserMenuDivider() + addonsManagerMenuItem
} else {
listOf(addonsManagerMenuItem) + BrowserMenuDivider() +
filteredExtensionMenuItems +
BrowserMenuDivider() + backPressMenuItem
filteredExtensionMenuItems +
BrowserMenuDivider() + backPressMenuItem
}
val webExtBrowserMenuAdapter = BrowserMenuAdapter(context, webExtSubMenuItems)
......@@ -108,26 +128,22 @@ class WebExtensionBrowserMenuBuilder(
onAddonsManagerTapped.invoke()
}
}
val mainMenuIndex = finalList.indexOfFirst { browserMenuItem ->
(browserMenuItem as? WebExtensionPlaceholderMenuItem)?.id ==
WebExtensionPlaceholderMenuItem.MAIN_EXTENSIONS_MENU_ID
}
val menuItems = if (mainMenuIndex != -1) {
finalList[mainMenuIndex] = webExtMenuItem
return if (mainMenuIndex != -1) {
finalList[mainMenuIndex] = addonsMenuItem
finalList
// if we do not have a placeholder we should add the extension submenu at top or bottom
} else {
if (appendExtensionSubMenuAtStart) {
listOf(webExtMenuItem) + finalList
listOf(addonsMenuItem) + finalList
} else {
finalList + webExtMenuItem
finalList + addonsMenuItem
}
}
val adapter = BrowserMenuAdapter(context, menuItems)
return BrowserMenu(adapter)
}
/**
......
......@@ -143,7 +143,7 @@ class WebExtensionBrowserMenuBuilderTest {
}
@Test
fun `web extension submenu is added at the top when usingBottomToolbar is true with no placeholde for submenu`() {
fun `web extension submenu is added at the top when usingBottomToolbar is true with no placeholder for submenu`() {
val browserAction = WebExtensionBrowserAction("browser_action", true, mock(), "", 0, 0) {}
val pageAction = WebExtensionBrowserAction("page_action", true, mock(), "", 0, 0) {}
......@@ -403,6 +403,99 @@ class WebExtensionBrowserMenuBuilderTest {
assertNotNull(addOnsManagerMenuItem)
}
@Test
fun `GIVEN showAddonsInMenu with value true WHEN the menu is built THEN the Add-ons item is added at the bottom`() {
val pageAction = WebExtensionBrowserAction("page_action", true, mock(), "", 0, 0) {}
val extensions = mapOf(
"id" to WebExtensionState(
"id",
"url",
"name",
true,
browserAction = null,
pageAction = pageAction
)
)
val store = BrowserStore(BrowserState(extensions = extensions))
// 2 items initially on the main menu
val items = listOf(
mockMenuItem(),
mockMenuItem()
)
val builder =
WebExtensionBrowserMenuBuilder(
items,
store = store,
showAddonsInMenu = true
)
val menu = builder.build(testContext)
val anchor = ImageButton(testContext)
val popup = menu.show(anchor)
val recyclerView: RecyclerView = popup.contentView.findViewById(R.id.mozac_browser_menu_recyclerView)
assertNotNull(recyclerView)
val recyclerAdapter = recyclerView.adapter as BrowserMenuAdapter
assertNotNull(recyclerAdapter)
// main menu should have 3 items and the last one should be the "Add-ons" item
assertEquals(3, recyclerAdapter.itemCount)
val lastItem = recyclerAdapter.visibleItems[2]
assert(lastItem is ParentBrowserMenuItem && lastItem.label == "Add-ons")
}
@Test
fun `GIVEN showAddonsInMenu with value false WHEN the menu is built THEN the Add-ons item is not added`() {
val pageAction = WebExtensionBrowserAction("page_action", true, mock(), "", 0, 0) {}
val extensions = mapOf(
"id" to WebExtensionState(
"id",
"url",
"name",
true,
browserAction = null,
pageAction = pageAction
)
)
val store = BrowserStore(BrowserState(extensions = extensions))
// 2 items initially on the main menu
val items = listOf(
mockMenuItem(),
mockMenuItem()
)
val builder =
WebExtensionBrowserMenuBuilder(
items,
store = store,
showAddonsInMenu = false
)
val menu = builder.build(testContext)
val anchor = ImageButton(testContext)
val popup = menu.show(anchor)
val recyclerView: RecyclerView = popup.contentView.findViewById(R.id.mozac_browser_menu_recyclerView)
assertNotNull(recyclerView)
val recyclerAdapter = recyclerView.adapter as BrowserMenuAdapter
assertNotNull(recyclerAdapter)
// main menu should have 2 items
assertEquals(2, recyclerAdapter.itemCount)
recyclerAdapter.visibleItems.forEach { item ->
assert(item !is ParentBrowserMenuItem)
}
}
private fun mockMenuItem() = object : BrowserMenuItem {
override val visible: () -> Boolean = { true }
......
......@@ -17,6 +17,9 @@ permalink: /changelog/
* **feature-search**
* Updated the icon of the bing search engine.
* **browser-menu**
* Adds `showAddonsInMenu` in WebExtensionBrowserMenuBuilder to allow the option of removing `Add-ons` item even if another extensions are displayed
* **feature-privatemode**
* Adds `clearFlagOnStop = true` in SecureWindowFeature to allow the option of keeping `FLAG_SECURE` when calling `stop()`
......
Supports Markdown
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