Bug 1923874 - [Menu Redesign] Show extension promotion with updated strings when all installed extensions have been disabled r=android-reviewers,petru
Differential Revision: https://phabricator.services.mozilla.com/D225510
This commit is contained in:
@@ -18,6 +18,8 @@ import mozilla.components.concept.engine.webextension.WebExtensionPageAction
|
||||
* @property enabled Whether or not this web extension is enabled, defaults to true.
|
||||
* @property allowedInPrivateBrowsing Whether or not this web extension is allowed in private browsing
|
||||
* mode. Defaults to false.
|
||||
* @property isBuiltIn Whether or not this web extension is built-in (packaged with the
|
||||
* APK file) or coming from an external source.
|
||||
* @property browserAction The browser action state of this extension.
|
||||
* @property pageAction The page action state of this extension.
|
||||
* @property popupSessionId The ID of the session displaying
|
||||
@@ -30,6 +32,7 @@ data class WebExtensionState(
|
||||
val name: String? = null,
|
||||
val enabled: Boolean = true,
|
||||
val allowedInPrivateBrowsing: Boolean = false,
|
||||
val isBuiltIn: Boolean = false,
|
||||
val browserAction: WebExtensionBrowserAction? = null,
|
||||
val pageAction: WebExtensionPageAction? = null,
|
||||
val popupSessionId: String? = null,
|
||||
|
||||
@@ -547,6 +547,7 @@ object WebExtensionSupport {
|
||||
getMetadata()?.name,
|
||||
isEnabled(),
|
||||
isAllowedInPrivateBrowsing(),
|
||||
isBuiltIn(),
|
||||
)
|
||||
|
||||
private fun SessionState.isCustomTab() = this is CustomTabSessionState
|
||||
|
||||
@@ -303,6 +303,10 @@ class MenuDialogFragment : BottomSheetDialogFragment() {
|
||||
state.extensionMenuState.showExtensionsOnboarding
|
||||
}
|
||||
|
||||
val showDisabledExtensionsOnboarding by store.observeAsState(initialValue = false) { state ->
|
||||
state.extensionMenuState.showDisabledExtensionsOnboarding
|
||||
}
|
||||
|
||||
val initRoute = when (args.accesspoint) {
|
||||
MenuAccessPoint.Browser,
|
||||
MenuAccessPoint.Home,
|
||||
@@ -546,6 +550,7 @@ class MenuDialogFragment : BottomSheetDialogFragment() {
|
||||
recommendedAddons = recommendedAddons,
|
||||
addonInstallationInProgress = addonInstallationInProgress,
|
||||
showExtensionsOnboarding = showExtensionsOnboarding,
|
||||
showDisabledExtensionsOnboarding = showDisabledExtensionsOnboarding,
|
||||
showManageExtensions = updateManageExtensionsMenuItemVisibility,
|
||||
webExtensionMenuItems = browserWebExtensionMenuItem,
|
||||
onBackButtonClick = {
|
||||
|
||||
@@ -61,7 +61,6 @@ class WebExtensionsMenuBinding(
|
||||
}
|
||||
.collect { webExtensionsFlowState ->
|
||||
val eligibleExtensions = webExtensionsFlowState.browserState.extensions.values
|
||||
.filter { it.enabled }
|
||||
.filterNot {
|
||||
!it.allowedInPrivateBrowsing &&
|
||||
webExtensionsFlowState.sessionState.content.private
|
||||
@@ -89,6 +88,14 @@ class WebExtensionsMenuBinding(
|
||||
}
|
||||
}
|
||||
|
||||
if (browserWebExtensionMenuItems.isEmpty() && eligibleExtensions.filter { !it.isBuiltIn }
|
||||
.all { !it.enabled }
|
||||
) {
|
||||
menuStore.dispatch(MenuAction.UpdateShowDisabledExtensionsOnboarding(true))
|
||||
} else {
|
||||
menuStore.dispatch(MenuAction.UpdateShowDisabledExtensionsOnboarding(false))
|
||||
}
|
||||
|
||||
menuStore.dispatch(
|
||||
MenuAction.UpdateWebExtensionBrowserMenuItems(browserWebExtensionMenuItems),
|
||||
)
|
||||
@@ -98,12 +105,17 @@ class WebExtensionsMenuBinding(
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("ReturnCount")
|
||||
private suspend fun getWebExtensionMenuItem(
|
||||
extension: WebExtensionState,
|
||||
webExtensionsFlowState: WebExtensionsFlowState,
|
||||
globalAction: Action,
|
||||
isPageAction: Boolean = false,
|
||||
): WebExtensionMenuItem? {
|
||||
if (!extension.enabled) {
|
||||
return null
|
||||
}
|
||||
|
||||
val tabAction = if (isPageAction) {
|
||||
webExtensionsFlowState.sessionState.extensionState[extension.id]?.pageAction
|
||||
} else {
|
||||
|
||||
@@ -40,12 +40,13 @@ import org.mozilla.fenix.compose.list.TextListItem
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
import org.mozilla.fenix.theme.Theme
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LongMethod")
|
||||
@Composable
|
||||
internal fun ExtensionsSubmenu(
|
||||
recommendedAddons: List<Addon>,
|
||||
webExtensionMenuItems: List<WebExtensionMenuItem.WebExtensionBrowserMenuItem>,
|
||||
showExtensionsOnboarding: Boolean,
|
||||
showDisabledExtensionsOnboarding: Boolean,
|
||||
showManageExtensions: Boolean,
|
||||
addonInstallationInProgress: Addon?,
|
||||
onBackButtonClick: () -> Unit,
|
||||
@@ -65,16 +66,27 @@ internal fun ExtensionsSubmenu(
|
||||
)
|
||||
},
|
||||
) {
|
||||
if (showExtensionsOnboarding) {
|
||||
if (showExtensionsOnboarding || showDisabledExtensionsOnboarding) {
|
||||
ExtensionsSubmenuBanner(
|
||||
title = stringResource(
|
||||
R.string.browser_menu_extensions_banner_onboarding_header,
|
||||
stringResource(R.string.app_name),
|
||||
),
|
||||
description = stringResource(
|
||||
R.string.browser_menu_extensions_banner_onboarding_body,
|
||||
stringResource(R.string.app_name),
|
||||
),
|
||||
title = if (showExtensionsOnboarding) {
|
||||
stringResource(
|
||||
R.string.browser_menu_extensions_banner_onboarding_header,
|
||||
stringResource(R.string.app_name),
|
||||
)
|
||||
} else {
|
||||
stringResource(R.string.browser_menu_disabled_extensions_banner_onboarding_header)
|
||||
},
|
||||
description = if (showExtensionsOnboarding) {
|
||||
stringResource(
|
||||
R.string.browser_menu_extensions_banner_onboarding_body,
|
||||
stringResource(R.string.app_name),
|
||||
)
|
||||
} else {
|
||||
stringResource(
|
||||
R.string.browser_menu_disabled_extensions_banner_onboarding_body,
|
||||
stringResource(R.string.browser_menu_manage_extensions),
|
||||
)
|
||||
},
|
||||
linkText = stringResource(R.string.browser_menu_extensions_banner_learn_more),
|
||||
onClick = onExtensionsLearnMoreClick,
|
||||
)
|
||||
@@ -205,6 +217,7 @@ private fun ExtensionsSubmenuPreview() {
|
||||
),
|
||||
),
|
||||
showExtensionsOnboarding = true,
|
||||
showDisabledExtensionsOnboarding = true,
|
||||
showManageExtensions = true,
|
||||
addonInstallationInProgress = Addon(
|
||||
id = "id",
|
||||
@@ -277,6 +290,7 @@ private fun ExtensionsSubmenuPrivatePreview() {
|
||||
),
|
||||
),
|
||||
showExtensionsOnboarding = true,
|
||||
showDisabledExtensionsOnboarding = false,
|
||||
showManageExtensions = false,
|
||||
addonInstallationInProgress = null,
|
||||
onBackButtonClick = {},
|
||||
|
||||
@@ -125,6 +125,7 @@ private fun ExtensionsSubmenuBannerText(
|
||||
},
|
||||
color = FirefoxTheme.colors.textPrimary,
|
||||
style = FirefoxTheme.typography.headline7,
|
||||
textAlign = if (!isWideScreen) TextAlign.Center else null,
|
||||
)
|
||||
|
||||
Text(
|
||||
|
||||
@@ -253,6 +253,7 @@ class MenuTelemetryMiddleware(
|
||||
is MenuAction.InstallAddonSuccess,
|
||||
is MenuAction.UpdateInstallAddonInProgress,
|
||||
is MenuAction.UpdateShowExtensionsOnboarding,
|
||||
is MenuAction.UpdateShowDisabledExtensionsOnboarding,
|
||||
is MenuAction.UpdateManageExtensionsMenuItemVisibility,
|
||||
-> Unit
|
||||
}
|
||||
|
||||
@@ -174,6 +174,16 @@ sealed class MenuAction : Action {
|
||||
val showExtensionsOnboarding: Boolean,
|
||||
) : MenuAction()
|
||||
|
||||
/**
|
||||
* [MenuAction] dispatched when disabled extensions promotion banner onboarding should be visible or not.
|
||||
*
|
||||
* @property showDisabledExtensionsOnboarding Show extensions promotion banner onboarding when
|
||||
* all installed extensions have been disabled.
|
||||
*/
|
||||
data class UpdateShowDisabledExtensionsOnboarding(
|
||||
val showDisabledExtensionsOnboarding: Boolean,
|
||||
) : MenuAction()
|
||||
|
||||
/**
|
||||
* [MenuAction] dispatched when a custom item is tapped in the custom tab menu.
|
||||
*
|
||||
|
||||
@@ -46,6 +46,8 @@ data class BrowserMenuState(
|
||||
*
|
||||
* @property recommendedAddons A list of recommended [Addon]s to suggest.
|
||||
* @property showExtensionsOnboarding Show extensions promotion banner onboarding.
|
||||
* @property showDisabledExtensionsOnboarding Show extensions promotion banner onboarding when
|
||||
* all installed extensions have been disabled.
|
||||
* @property addonInstallationInProgress The [Addon] that is currently being installed.
|
||||
* @property shouldShowManageExtensionsMenuItem Indicates if manage extensions menu item
|
||||
* should be displayed to the user.
|
||||
@@ -55,6 +57,7 @@ data class BrowserMenuState(
|
||||
data class ExtensionMenuState(
|
||||
val recommendedAddons: List<Addon> = emptyList(),
|
||||
val showExtensionsOnboarding: Boolean = false,
|
||||
val showDisabledExtensionsOnboarding: Boolean = false,
|
||||
val addonInstallationInProgress: Addon? = null,
|
||||
val shouldShowManageExtensionsMenuItem: Boolean = false,
|
||||
val browserWebExtensionMenuItem: List<WebExtensionMenuItem.WebExtensionBrowserMenuItem> = emptyList(),
|
||||
|
||||
@@ -88,6 +88,10 @@ private fun reducer(state: MenuState, action: MenuAction): MenuState {
|
||||
extensionState.copy(showExtensionsOnboarding = action.showExtensionsOnboarding)
|
||||
}
|
||||
|
||||
is MenuAction.UpdateShowDisabledExtensionsOnboarding -> state.copyWithExtensionMenuState { extensionState ->
|
||||
extensionState.copy(showDisabledExtensionsOnboarding = action.showDisabledExtensionsOnboarding)
|
||||
}
|
||||
|
||||
is MenuAction.UpdateManageExtensionsMenuItemVisibility -> state.copyWithExtensionMenuState {
|
||||
it.copy(shouldShowManageExtensionsMenuItem = action.isVisible)
|
||||
}
|
||||
|
||||
@@ -196,9 +196,14 @@
|
||||
<!-- Browser menu banner header text for extensions onboarding.
|
||||
The first parameter is the name of the app defined in app_name (for example: Fenix). -->
|
||||
<string name="browser_menu_extensions_banner_onboarding_header">Make %s your own</string>
|
||||
<!-- Browser menu banner header text for extensions onboarding when all installed extensions have been disabled. -->
|
||||
<string name="browser_menu_disabled_extensions_banner_onboarding_header">You have extensions installed, but not enabled</string>
|
||||
<!-- Browser menu banner body text for extensions onboarding.
|
||||
The first parameter is the name of the app defined in app_name (for example: Fenix). -->
|
||||
<string name="browser_menu_extensions_banner_onboarding_body">Extensions level up your browsing, from changing how %s looks and performs to boosting privacy and safety.</string>
|
||||
<!-- Browser menu banner body text for extensions onboarding when all installed extensions have been disabled.
|
||||
The first parameter is the name of the button that opens extension manager (for example "Manage extensions"). -->
|
||||
<string name="browser_menu_disabled_extensions_banner_onboarding_body">To use extensions, enable them in settings or by selecting “%s” below.</string>
|
||||
<!-- Browser menu banner link text for learning more about extensions -->
|
||||
<string name="browser_menu_extensions_banner_learn_more">Learn more</string>
|
||||
<!-- Browser menu button that opens the extensions manager -->
|
||||
|
||||
@@ -404,4 +404,15 @@ class MenuStoreTest {
|
||||
|
||||
assertTrue(store.state.extensionMenuState.shouldShowManageExtensionsMenuItem)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN update show disabled extensions onboarding dispatched THEN extension state is updated`() =
|
||||
runTest {
|
||||
val initialState = MenuState()
|
||||
val store = MenuStore(initialState = initialState)
|
||||
|
||||
store.dispatch(MenuAction.UpdateShowDisabledExtensionsOnboarding(true)).join()
|
||||
|
||||
assertTrue(store.state.extensionMenuState.showDisabledExtensionsOnboarding)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import mozilla.components.support.test.robolectric.testContext
|
||||
import mozilla.components.support.test.rule.MainCoroutineRule
|
||||
import mozilla.components.support.test.rule.runTestOnMain
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -218,6 +219,96 @@ class WebExtensionsMenuBindingTest {
|
||||
assertTrue(pageItemsUpdateCaptor.value.webExtensionPageMenuItem.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN all web extensions are disabled THEN show disabled extensions onboarding`() =
|
||||
runTestOnMain {
|
||||
val extensions: Map<String, WebExtensionState> = mapOf(
|
||||
"id" to WebExtensionState(
|
||||
id = "id",
|
||||
url = "url",
|
||||
name = "name",
|
||||
enabled = false,
|
||||
),
|
||||
)
|
||||
|
||||
menuStore = spy(MenuStore(MenuState()))
|
||||
browserStore = BrowserStore(
|
||||
BrowserState(
|
||||
tabs = listOf(
|
||||
createTab(
|
||||
url = "https://www.example.org",
|
||||
id = "tab1",
|
||||
extensions = extensions,
|
||||
),
|
||||
),
|
||||
selectedTabId = "tab1",
|
||||
extensions = extensions,
|
||||
),
|
||||
)
|
||||
|
||||
val binding = WebExtensionsMenuBinding(
|
||||
browserStore = browserStore,
|
||||
menuStore = menuStore,
|
||||
iconSize = 24.dpToPx(testContext.resources.displayMetrics),
|
||||
onDismiss = {},
|
||||
)
|
||||
binding.start()
|
||||
|
||||
val showDisabledExtensionsOnboardingCaptor = argumentCaptor<MenuAction.UpdateShowDisabledExtensionsOnboarding>()
|
||||
|
||||
verify(menuStore).dispatch(showDisabledExtensionsOnboardingCaptor.capture())
|
||||
|
||||
assertTrue(showDisabledExtensionsOnboardingCaptor.value.showDisabledExtensionsOnboarding)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN only one web extension is disabled THEN not show disabled extensions onboarding`() =
|
||||
runTestOnMain {
|
||||
val extensions: Map<String, WebExtensionState> = mapOf(
|
||||
"id" to WebExtensionState(
|
||||
id = "id",
|
||||
url = "url",
|
||||
name = "name",
|
||||
enabled = false,
|
||||
),
|
||||
"id2" to WebExtensionState(
|
||||
id = "id2",
|
||||
url = "url2",
|
||||
name = "name2",
|
||||
enabled = true,
|
||||
),
|
||||
)
|
||||
|
||||
menuStore = spy(MenuStore(MenuState()))
|
||||
browserStore = BrowserStore(
|
||||
BrowserState(
|
||||
tabs = listOf(
|
||||
createTab(
|
||||
url = "https://www.example.org",
|
||||
id = "tab1",
|
||||
extensions = extensions,
|
||||
),
|
||||
),
|
||||
selectedTabId = "tab1",
|
||||
extensions = extensions,
|
||||
),
|
||||
)
|
||||
|
||||
val binding = WebExtensionsMenuBinding(
|
||||
browserStore = browserStore,
|
||||
menuStore = menuStore,
|
||||
iconSize = 24.dpToPx(testContext.resources.displayMetrics),
|
||||
onDismiss = {},
|
||||
)
|
||||
binding.start()
|
||||
|
||||
val showDisabledExtensionsOnboardingCaptor = argumentCaptor<MenuAction.UpdateShowDisabledExtensionsOnboarding>()
|
||||
|
||||
verify(menuStore).dispatch(showDisabledExtensionsOnboardingCaptor.capture())
|
||||
|
||||
assertFalse(showDisabledExtensionsOnboardingCaptor.value.showDisabledExtensionsOnboarding)
|
||||
}
|
||||
|
||||
private fun createWebExtensionPageAction(title: String) = WebExtensionPageAction(
|
||||
title = title,
|
||||
enabled = true,
|
||||
|
||||
Reference in New Issue
Block a user