For #17770: New tab three-dot menu reorder (#18427)

* Create new menu order for new tab

* Add new tab menu navigation. Dynamically update menu when sync auth is needed. Make new tab menu and browser menu consistent.

* Lint

Lint and refactoring tests

* Tests for default toolbar menu

* Feature flag for request desktop site

Add todos for UI test issue 17979

Add todos for UI tests
upstream-sync
Elise Richards 3 years ago committed by GitHub
parent 35de7056b3
commit ca33aef036
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -49,6 +49,7 @@ class NavigationToolbarTest {
}
@Test
@Ignore("To be fixed in https://github.com/mozilla-mobile/fenix/issues/17979")
fun goBackTest() {
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
val nextWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
@ -66,8 +67,8 @@ class NavigationToolbarTest {
}
}
@Ignore("Test failures: https://github.com/mozilla-mobile/fenix/issues/18720")
@Test
@Ignore("To be fixed in https://github.com/mozilla-mobile/fenix/issues/17979")
fun goForwardTest() {
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
val nextWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
@ -95,8 +96,8 @@ class NavigationToolbarTest {
}
}
@Ignore("Test failures: https://github.com/mozilla-mobile/fenix/issues/18720")
@Test
@Ignore("To be fixed in https://github.com/mozilla-mobile/fenix/issues/17979")
fun refreshPageTest() {
val refreshWebPage = TestAssetHelper.getRefreshAsset(mockWebServer)

@ -66,8 +66,8 @@ class NoNetworkAccessStartupTests {
}
}
@Ignore("Test failures: https://github.com/mozilla-mobile/fenix/issues/18720")
@Test
@Ignore("To be fixed in https://github.com/mozilla-mobile/fenix/issues/17979")
fun testPageReloadAfterNetworkInterrupted() {
val url = "example.com"
@ -80,7 +80,11 @@ class NoNetworkAccessStartupTests {
browserScreen {
}.openThreeDotMenu {
}.refreshPage {}
verifyRefreshButton()
}
// we verify that the share button exists, but this fails when trying to click
// .refreshPage {}
}
@Test

@ -44,8 +44,8 @@ class ShareButtonTest {
mockWebServer.shutdown()
}
@Ignore("Test failures: https://github.com/mozilla-mobile/fenix/issues/18720")
@Test
@Ignore("To be re-implemented with the three dot menu changes https://github.com/mozilla-mobile/fenix/issues/17979")
fun ShareButtonAppearanceTest() {
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)

@ -17,6 +17,7 @@ import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
@ -41,7 +42,7 @@ import org.mozilla.fenix.ui.util.STRING_ONBOARDING_TRACKING_PROTECTION_HEADER
* Test Suite that contains tests defined as part of the Smoke and Sanity check defined in Test rail.
* These tests will verify different functionalities of the app as a way to quickly detect regressions in main areas
*/
@Suppress("ForbiddenComment")
class SmokeTest {
private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
private lateinit var mockWebServer: MockWebServer
@ -316,35 +317,41 @@ class SmokeTest {
@Test
// Verifies the Bookmark button in a tab's 3 dot menu
@Ignore("To be re-implemented in https://github.com/mozilla-mobile/fenix/issues/17979")
// TODO: To be removed in https://github.com/mozilla-mobile/fenix/issues/17979 since the bookmark button is no longer in the nav bar.
fun mainMenuBookmarkButtonTest() {
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
if (!FeatureFlags.toolbarMenuFeature) {
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
navigationToolbar {
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
}.openThreeDotMenu {
}.bookmarkPage {
verifySnackBarText("Bookmark saved!")
navigationToolbar {
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
}.openThreeDotMenu {
}.bookmarkPage {
verifySnackBarText("Bookmark saved!")
}
}
}
@Ignore("Test failures: https://github.com/mozilla-mobile/fenix/issues/18720")
@Test
// Verifies the Share button in a tab's 3 dot menu
@Ignore("To be fixed in https://github.com/mozilla-mobile/fenix/issues/17979")
fun mainMenuShareButtonTest() {
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
navigationToolbar {
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
}.openThreeDotMenu {
}.sharePage {
verifyShareAppsLayout()
verifyShareButton()
}
// we verify that the share button exists, but this fails when trying to click
// .sharePage {
// verifyShareAppsLayout()
// }
}
@Ignore("Test failures: https://github.com/mozilla-mobile/fenix/issues/18720")
@Test
// Verifies the refresh button in a tab's 3 dot menu
@Ignore("To be fixed in https://github.com/mozilla-mobile/fenix/issues/17979")
fun mainMenuRefreshButtonTest() {
val refreshWebPage = TestAssetHelper.getRefreshAsset(mockWebServer)
@ -354,14 +361,17 @@ class SmokeTest {
}.openThreeDotMenu {
verifyThreeDotMenuExists()
verifyRefreshButton()
}.refreshPage {
verifyPageContent("REFRESHED")
}
// we verify that the refresh button exists, but this fails when trying to click
// .refreshPage {
// verifyPageContent("REFRESHED")
// }
}
@Test
// Turns ETP toggle off from Settings and verifies the ETP shield is not displayed in the nav bar
@Ignore("To be re-implemented with the three dot menu changes https://github.com/mozilla-mobile/fenix/issues/17870")
@Ignore("To be fixed in https://github.com/mozilla-mobile/fenix/issues/17979")
fun verifyETPShieldNotDisplayedIfOFFGlobally() {
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)

@ -109,8 +109,7 @@ class NavigationToolbarRobot {
withResourceName("onboarding_message"), // Req ETP dialog
withResourceName("download_button")
)
)
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
}
BrowserRobot().interact()

@ -48,6 +48,7 @@ import org.mozilla.fenix.share.ShareFragment
/**
* Implementation of Robot Pattern for the three dot (main) menu.
*/
@Suppress("ForbiddenComment")
class ThreeDotMenuMainRobot {
fun verifyTabSettingsButton() = assertTabSettingsButton()
fun verifyRecentlyClosedTabsButton() = assertRecentlyClosedTabsButton()
@ -166,7 +167,7 @@ class ThreeDotMenuMainRobot {
private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
fun openSettings(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition {
onView(withId(R.id.mozac_browser_menu_recyclerView)).perform(ViewActions.swipeDown())
onView(withId(R.id.mozac_browser_menu_recyclerView)).perform(swipeDown())
onView(allOf(withResourceName("text"), withText(R.string.browser_menu_settings)))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
.check(matches(isCompletelyDisplayed()))
@ -185,7 +186,7 @@ class ThreeDotMenuMainRobot {
}
fun openSyncedTabs(interact: SyncedTabsRobot.() -> Unit): SyncedTabsRobot.Transition {
onView(withId(R.id.mozac_browser_menu_recyclerView)).perform(ViewActions.swipeDown())
onView(withId(R.id.mozac_browser_menu_recyclerView)).perform(swipeDown())
mDevice.waitNotNull(Until.findObject(By.text("Synced tabs")), waitingTime)
syncedTabsButton().click()
@ -205,7 +206,7 @@ class ThreeDotMenuMainRobot {
}
fun openHistory(interact: HistoryRobot.() -> Unit): HistoryRobot.Transition {
onView(withId(R.id.mozac_browser_menu_recyclerView)).perform(ViewActions.swipeDown())
onView(withId(R.id.mozac_browser_menu_recyclerView)).perform(swipeDown())
mDevice.waitNotNull(Until.findObject(By.text("History")), waitingTime)
historyButton().click()
@ -273,6 +274,7 @@ class ThreeDotMenuMainRobot {
}
fun refreshPage(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
// TODO: this is not finding the button correctly
mDevice.waitNotNull(Until.findObject(By.desc("Refresh")), waitingTime)
refreshButton().click()
@ -303,7 +305,7 @@ class ThreeDotMenuMainRobot {
}
fun openFindInPage(interact: FindInPageRobot.() -> Unit): FindInPageRobot.Transition {
onView(withId(R.id.mozac_browser_menu_recyclerView)).perform(ViewActions.swipeDown())
onView(withId(R.id.mozac_browser_menu_recyclerView)).perform(swipeDown())
mDevice.waitNotNull(Until.findObject(By.text("Find in page")), waitingTime)
findInPageButton().click()
@ -463,12 +465,12 @@ private fun assertShareTabButton() = shareTabButton()
private fun shareButton() = onView(ViewMatchers.withContentDescription("Share"))
private fun assertShareButton() = shareButton()
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun browserViewSaveCollectionButton() = onView(
allOf(
withText("Save to collection"),
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)
withEffectiveVisibility(Visibility.VISIBLE)
)
)
@ -493,9 +495,11 @@ private fun assertCollectionNameTextField() = collectionNameTextField()
private fun reportSiteIssueButton() = onView(withText("Report Site Issue…"))
private fun findInPageButton() = onView(allOf(withText("Find in page")))
private fun assertFindInPageButton() = findInPageButton()
private fun shareScrim() = onView(withResourceName("closeSharingScrim"))
private fun assertShareScrim() =
shareScrim().check(matches(ViewMatchers.withAlpha(ShareFragment.SHOW_PAGE_ALPHA)))
@ -544,6 +548,7 @@ private fun assertAddToFirefoxHome() {
private fun addToMobileHomeButton() =
onView(allOf(withText(R.string.browser_menu_add_to_homescreen)))
private fun assertAddToMobileHome() {
onView(withId(R.id.mozac_browser_menu_recyclerView))
.perform(

@ -128,6 +128,18 @@ class DefaultBrowserToolbarMenuController(
activity.finishAndRemoveTask()
}
}
// todo === End ===
is ToolbarMenu.Item.OpenInApp -> {
settings.openInAppOpened = true
val appLinksUseCases = activity.components.useCases.appLinksUseCases
val getRedirect = appLinksUseCases.appLinkRedirect
currentSession?.let {
val redirect = getRedirect.invoke(it.content.url)
redirect.appIntent?.flags = Intent.FLAG_ACTIVITY_NEW_TASK
appLinksUseCases.openAppLink.invoke(redirect.appIntent)
}
}
is ToolbarMenu.Item.Quit -> {
// We need to show the snackbar while the browsing data is deleting (if "Delete
// browsing data on quit" is activated). After the deletion is over, the snackbar
@ -147,19 +159,6 @@ class DefaultBrowserToolbarMenuController(
readerModeController.showControls()
metrics.track(Event.ReaderModeAppearanceOpened)
}
is ToolbarMenu.Item.OpenInApp -> {
settings.openInAppOpened = true
val appLinksUseCases = activity.components.useCases.appLinksUseCases
val getRedirect = appLinksUseCases.appLinkRedirect
currentSession?.let {
val redirect = getRedirect.invoke(it.content.url)
redirect.appIntent?.flags = Intent.FLAG_ACTIVITY_NEW_TASK
appLinksUseCases.openAppLink.invoke(redirect.appIntent)
}
}
// todo === End ===
is ToolbarMenu.Item.Back -> {
if (item.viewHistory) {
navController.navigate(

@ -163,14 +163,13 @@ class BrowserToolbarView(
} else {
menuToolbar = DefaultToolbarMenu(
context = this,
store = components.core.store,
hasAccountProblem = components.backgroundServices.accountManager.accountNeedsReauth(),
shouldReverseItems = toolbarPosition == ToolbarPosition.TOP,
onItemTapped = {
it.performHapticIfNeeded(view)
interactor.onBrowserToolbarMenuItemTapped(it)
},
lifecycleOwner = lifecycleOwner,
store = components.core.store,
bookmarksStorage = bookmarkStorage,
isPinningSupported = isPinningSupported
)

@ -7,6 +7,7 @@ package org.mozilla.fenix.components.toolbar
import android.content.Context
import androidx.annotation.ColorRes
import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.PRIVATE
import androidx.core.content.ContextCompat.getColor
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
@ -53,11 +54,10 @@ import org.mozilla.fenix.theme.ThemeManager
*/
@Suppress("LargeClass", "LongParameterList")
@ExperimentalCoroutinesApi
class DefaultToolbarMenu(
open class DefaultToolbarMenu(
private val context: Context,
private val store: BrowserStore,
hasAccountProblem: Boolean = false,
shouldReverseItems: Boolean,
private val onItemTapped: (ToolbarMenu.Item) -> Unit = {},
private val lifecycleOwner: LifecycleOwner,
private val bookmarksStorage: BookmarksStorage,
@ -66,10 +66,15 @@ class DefaultToolbarMenu(
private var isCurrentUrlBookmarked = false
private var isBookmarkedJob: Job? = null
private val isTopToolbarSelected = shouldReverseItems
private val shouldUseBottomToolbar = context.settings().shouldUseBottomToolbar
private val selectedSession: TabSessionState?
get() = store.state.selectedTab
private val primaryTextColor =
ThemeManager.resolveAttribute(R.attr.primaryText, context)
override val menuBuilder by lazy {
WebExtensionBrowserMenuBuilder(
items =
@ -78,13 +83,13 @@ class DefaultToolbarMenu(
} else {
oldCoreMenuItems
},
endOfMenuAlwaysVisible = !shouldReverseItems,
endOfMenuAlwaysVisible = !shouldUseBottomToolbar,
store = store,
webExtIconTintColorResource = primaryTextColor(),
webExtIconTintColorResource = primaryTextColor,
onAddonsManagerTapped = {
onItemTapped.invoke(ToolbarMenu.Item.AddonsManager)
},
appendExtensionSubMenuAtStart = !shouldReverseItems
appendExtensionSubMenuAtStart = !shouldUseBottomToolbar
)
}
@ -92,7 +97,7 @@ class DefaultToolbarMenu(
val back = BrowserMenuItemToolbar.TwoStateButton(
primaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_back,
primaryContentDescription = context.getString(R.string.browser_menu_back),
primaryImageTintResource = primaryTextColor(),
primaryImageTintResource = primaryTextColor,
isInPrimaryState = {
selectedSession?.content?.canGoBack ?: true
},
@ -106,7 +111,7 @@ class DefaultToolbarMenu(
val forward = BrowserMenuItemToolbar.TwoStateButton(
primaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_forward,
primaryContentDescription = context.getString(R.string.browser_menu_forward),
primaryImageTintResource = primaryTextColor(),
primaryImageTintResource = primaryTextColor,
isInPrimaryState = {
selectedSession?.content?.canGoForward ?: true
},
@ -120,13 +125,13 @@ class DefaultToolbarMenu(
val refresh = BrowserMenuItemToolbar.TwoStateButton(
primaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_refresh,
primaryContentDescription = context.getString(R.string.browser_menu_refresh),
primaryImageTintResource = primaryTextColor(),
primaryImageTintResource = primaryTextColor,
isInPrimaryState = {
selectedSession?.content?.loading == false
},
secondaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_stop,
secondaryContentDescription = context.getString(R.string.browser_menu_stop),
secondaryImageTintResource = primaryTextColor(),
secondaryImageTintResource = primaryTextColor,
disableInSecondaryState = false,
longClickListener = { onItemTapped.invoke(ToolbarMenu.Item.Reload(bypassCache = true)) }
) {
@ -140,7 +145,7 @@ class DefaultToolbarMenu(
val share = BrowserMenuItemToolbar.Button(
imageResource = R.drawable.ic_share_filled,
contentDescription = context.getString(R.string.browser_menu_share),
iconTintColorResource = primaryTextColor(),
iconTintColorResource = primaryTextColor,
listener = {
onItemTapped.invoke(ToolbarMenu.Item.Share)
}
@ -154,14 +159,14 @@ class DefaultToolbarMenu(
val bookmark = BrowserMenuItemToolbar.TwoStateButton(
primaryImageResource = R.drawable.ic_bookmark_filled,
primaryContentDescription = context.getString(R.string.browser_menu_edit_bookmark),
primaryImageTintResource = primaryTextColor(),
primaryImageTintResource = primaryTextColor,
// TwoStateButton.isInPrimaryState must be synchronous, and checking bookmark state is
// relatively slow. The best we can do here is periodically compute and cache a new "is
// bookmarked" state, and use that whenever the menu has been opened.
isInPrimaryState = { isCurrentUrlBookmarked },
secondaryImageResource = R.drawable.ic_bookmark_outline,
secondaryContentDescription = context.getString(R.string.browser_menu_bookmark),
secondaryImageTintResource = primaryTextColor(),
secondaryImageTintResource = primaryTextColor,
disableInSecondaryState = false
) {
handleBookmarkItemTapped()
@ -172,20 +177,24 @@ class DefaultToolbarMenu(
}
// Predicates that need to be repeatedly called as the session changes
private fun canAddToHomescreen(): Boolean =
@VisibleForTesting(otherwise = PRIVATE)
fun canAddToHomescreen(): Boolean =
selectedSession != null && isPinningSupported &&
!context.components.useCases.webAppUseCases.isInstallable()
private fun canInstall(): Boolean =
@VisibleForTesting(otherwise = PRIVATE)
fun canInstall(): Boolean =
selectedSession != null && isPinningSupported &&
context.components.useCases.webAppUseCases.isInstallable()
private fun shouldShowOpenInApp(): Boolean = selectedSession?.let { session ->
@VisibleForTesting(otherwise = PRIVATE)
fun shouldShowOpenInApp(): Boolean = selectedSession?.let { session ->
val appLink = context.components.useCases.appLinksUseCases.appLinkRedirect
appLink(session.content.url).hasExternalApp()
} ?: false
private fun shouldShowReaderViewCustomization(): Boolean = selectedSession?.let {
@VisibleForTesting(otherwise = PRIVATE)
fun shouldShowReaderViewCustomization(): Boolean = selectedSession?.let {
store.state.findTab(it.id)?.readerState?.active
} ?: false
// End of predicates //
@ -196,10 +205,10 @@ class DefaultToolbarMenu(
startImageResource = R.drawable.ic_settings,
iconTintColorResource = if (hasAccountProblem)
ThemeManager.resolveAttribute(R.attr.syncDisconnected, context) else
primaryTextColor(),
primaryTextColor,
textColorResource = if (hasAccountProblem)
ThemeManager.resolveAttribute(R.attr.primaryText, context) else
primaryTextColor(),
primaryTextColor,
highlight = BrowserMenuHighlight.HighPriority(
endImageResource = R.drawable.ic_sync_disconnected,
backgroundTint = context.getColorFromAttr(R.attr.syncDisconnectedBackground),
@ -223,7 +232,7 @@ class DefaultToolbarMenu(
val addToTopSites = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_add_to_top_sites),
imageResource = R.drawable.ic_top_sites,
iconTintColorResource = primaryTextColor()
iconTintColorResource = primaryTextColor
) {
onItemTapped.invoke(ToolbarMenu.Item.AddToTopSites)
}
@ -231,7 +240,7 @@ class DefaultToolbarMenu(
val addToHomescreen = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_add_to_homescreen),
imageResource = R.drawable.ic_add_to_homescreen,
iconTintColorResource = primaryTextColor()
iconTintColorResource = primaryTextColor
) {
onItemTapped.invoke(ToolbarMenu.Item.AddToHomeScreen)
}
@ -239,7 +248,7 @@ class DefaultToolbarMenu(
val syncedTabs = BrowserMenuImageText(
label = context.getString(R.string.synced_tabs),
imageResource = R.drawable.ic_synced_tabs,
iconTintColorResource = primaryTextColor()
iconTintColorResource = primaryTextColor
) {
onItemTapped.invoke(ToolbarMenu.Item.SyncedTabs)
}
@ -247,7 +256,7 @@ class DefaultToolbarMenu(
val installToHomescreen = BrowserMenuHighlightableItem(
label = context.getString(R.string.browser_menu_install_on_homescreen),
startImageResource = R.drawable.ic_add_to_homescreen,
iconTintColorResource = primaryTextColor(),
iconTintColorResource = primaryTextColor,
highlight = BrowserMenuHighlight.LowPriority(
label = context.getString(R.string.browser_menu_install_on_homescreen),
notificationTint = getColor(context, R.color.whats_new_notification_color)
@ -262,7 +271,7 @@ class DefaultToolbarMenu(
val findInPage = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_find_in_page),
imageResource = R.drawable.mozac_ic_search,
iconTintColorResource = primaryTextColor()
iconTintColorResource = primaryTextColor
) {
onItemTapped.invoke(ToolbarMenu.Item.FindInPage)
}
@ -274,7 +283,7 @@ class DefaultToolbarMenu(
val saveToCollection = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_save_to_collection_2),
imageResource = R.drawable.ic_tab_collection,
iconTintColorResource = primaryTextColor()
iconTintColorResource = primaryTextColor
) {
onItemTapped.invoke(ToolbarMenu.Item.SaveToCollection)
}
@ -282,7 +291,7 @@ class DefaultToolbarMenu(
val deleteDataOnQuit = BrowserMenuImageText(
label = context.getString(R.string.delete_browsing_data_on_quit_action),
imageResource = R.drawable.ic_exit,
iconTintColorResource = primaryTextColor()
iconTintColorResource = primaryTextColor
) {
onItemTapped.invoke(ToolbarMenu.Item.Quit)
}
@ -290,7 +299,7 @@ class DefaultToolbarMenu(
val readerAppearance = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_read_appearance),
imageResource = R.drawable.ic_readermode_appearance,
iconTintColorResource = primaryTextColor()
iconTintColorResource = primaryTextColor
) {
onItemTapped.invoke(ToolbarMenu.Item.CustomizeReaderView)
}
@ -298,7 +307,7 @@ class DefaultToolbarMenu(
val openInApp = BrowserMenuHighlightableItem(
label = context.getString(R.string.browser_menu_open_app_link),
startImageResource = R.drawable.ic_open_in_app,
iconTintColorResource = primaryTextColor(),
iconTintColorResource = primaryTextColor,
highlight = BrowserMenuHighlight.LowPriority(
label = context.getString(R.string.browser_menu_open_app_link),
notificationTint = getColor(context, R.color.whats_new_notification_color)
@ -311,7 +320,7 @@ class DefaultToolbarMenu(
val historyItem = BrowserMenuImageText(
context.getString(R.string.library_history),
R.drawable.ic_history,
primaryTextColor()
primaryTextColor
) {
onItemTapped.invoke(ToolbarMenu.Item.History)
}
@ -319,7 +328,7 @@ class DefaultToolbarMenu(
val bookmarksItem = BrowserMenuImageText(
context.getString(R.string.library_bookmarks),
R.drawable.ic_bookmark_filled,
primaryTextColor()
primaryTextColor
) {
onItemTapped.invoke(ToolbarMenu.Item.Bookmarks)
}
@ -327,7 +336,7 @@ class DefaultToolbarMenu(
val downloadsItem = BrowserMenuImageText(
context.getString(R.string.library_downloads),
R.drawable.ic_download,
primaryTextColor()
primaryTextColor
) {
onItemTapped.invoke(ToolbarMenu.Item.Downloads)
}
@ -359,154 +368,154 @@ class DefaultToolbarMenu(
menuToolbar
)
if (shouldReverseItems) {
menuItems.reversed()
} else {
if (shouldUseBottomToolbar) {
menuItems
} else {
menuItems.reversed()
}
}
val newTabItem = BrowserMenuImageText(
context.getString(R.string.library_new_tab),
R.drawable.ic_new,
primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.NewTab)
}
private val newCoreMenuItems by lazy {
val newTabItem = BrowserMenuImageText(
context.getString(R.string.library_new_tab),
R.drawable.ic_new,
primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.NewTab)
}
val historyItem = BrowserMenuImageText(
context.getString(R.string.library_history),
R.drawable.ic_history,
primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.History)
}
val historyItem = BrowserMenuImageText(
context.getString(R.string.library_history),
R.drawable.ic_history,
primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.History)
}
val downloadsItem = BrowserMenuImageText(
context.getString(R.string.library_downloads),
R.drawable.ic_download,
primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.Downloads)
}
val downloadsItem = BrowserMenuImageText(
context.getString(R.string.library_downloads),
R.drawable.ic_download,
primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.Downloads)
}
val extensionsItem = WebExtensionPlaceholderMenuItem(
id = WebExtensionPlaceholderMenuItem.MAIN_EXTENSIONS_MENU_ID
)
val extensionsItem = WebExtensionPlaceholderMenuItem(
id = WebExtensionPlaceholderMenuItem.MAIN_EXTENSIONS_MENU_ID
)
val syncedTabs = BrowserMenuImageText(
label = context.getString(R.string.synced_tabs),
imageResource = R.drawable.ic_synced_tabs,
iconTintColorResource = primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.SyncedTabs)
}
val syncedTabs = BrowserMenuImageText(
label = context.getString(R.string.synced_tabs),
imageResource = R.drawable.ic_synced_tabs,
iconTintColorResource = primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.SyncedTabs)
}
val findInPageItem = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_find_in_page),
imageResource = R.drawable.mozac_ic_search,
iconTintColorResource = primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.FindInPage)
}
val findInPageItem = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_find_in_page),
imageResource = R.drawable.mozac_ic_search,
iconTintColorResource = primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.FindInPage)
}
val desktopSiteItem = BrowserMenuImageSwitch(
imageResource = R.drawable.ic_desktop,
label = context.getString(R.string.browser_menu_desktop_site),
initialState = {
selectedSession?.content?.desktopMode ?: false
}
) { checked ->
onItemTapped.invoke(ToolbarMenu.Item.RequestDesktop(checked))
val desktopSiteItem = BrowserMenuImageSwitch(
imageResource = R.drawable.ic_desktop,
label = context.getString(R.string.browser_menu_desktop_site),
initialState = {
selectedSession?.content?.desktopMode ?: false
}
) { checked ->
onItemTapped.invoke(ToolbarMenu.Item.RequestDesktop(checked))
}
val customizeReaderView = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_customize_reader_view),
imageResource = R.drawable.ic_readermode_appearance,
iconTintColorResource = primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.CustomizeReaderView)
}
val customizeReaderView = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_customize_reader_view),
imageResource = R.drawable.ic_readermode_appearance,
iconTintColorResource = primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.CustomizeReaderView)
}
val openInApp = BrowserMenuHighlightableItem(
val openInApp = BrowserMenuHighlightableItem(
label = context.getString(R.string.browser_menu_open_app_link),
startImageResource = R.drawable.ic_open_in_app,
iconTintColorResource = primaryTextColor(),
highlight = BrowserMenuHighlight.LowPriority(
label = context.getString(R.string.browser_menu_open_app_link),
startImageResource = R.drawable.ic_open_in_app,
iconTintColorResource = primaryTextColor(),
highlight = BrowserMenuHighlight.LowPriority(
label = context.getString(R.string.browser_menu_open_app_link),
notificationTint = getColor(context, R.color.whats_new_notification_color)
),
isHighlighted = { !context.settings().openInAppOpened }
) {
onItemTapped.invoke(ToolbarMenu.Item.OpenInApp)
}
notificationTint = getColor(context, R.color.whats_new_notification_color)
),
isHighlighted = { !context.settings().openInAppOpened }
) {
onItemTapped.invoke(ToolbarMenu.Item.OpenInApp)
}
val reportSiteIssuePlaceholder = WebExtensionPlaceholderMenuItem(
id = WebCompatReporterFeature.WEBCOMPAT_REPORTER_EXTENSION_ID
)
val reportSiteIssuePlaceholder = WebExtensionPlaceholderMenuItem(
id = WebCompatReporterFeature.WEBCOMPAT_REPORTER_EXTENSION_ID
)
val addToHomeScreenItem = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_add_to_homescreen),
imageResource = R.drawable.ic_add_to_homescreen,
iconTintColorResource = primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.AddToHomeScreen)
}
val addToHomeScreenItem = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_add_to_homescreen),
imageResource = R.drawable.ic_add_to_homescreen,
iconTintColorResource = primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.AddToHomeScreen)
}
val addToTopSitesItem = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_add_to_top_sites),
imageResource = R.drawable.ic_top_sites,
iconTintColorResource = primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.AddToTopSites)
}
val addToTopSitesItem = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_add_to_top_sites),
imageResource = R.drawable.ic_top_sites,
iconTintColorResource = primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.AddToTopSites)
}
val saveToCollectionItem = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_save_to_collection_2),
imageResource = R.drawable.ic_tab_collection,
iconTintColorResource = primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.SaveToCollection)
}
val saveToCollectionItem = BrowserMenuImageText(
label = context.getString(R.string.browser_menu_save_to_collection_2),
imageResource = R.drawable.ic_tab_collection,
iconTintColorResource = primaryTextColor()
) {
onItemTapped.invoke(ToolbarMenu.Item.SaveToCollection)
}
val settingsItem = BrowserMenuHighlightableItem(
label = context.getString(R.string.browser_menu_settings),
startImageResource = R.drawable.ic_settings,
iconTintColorResource = primaryTextColor(),
textColorResource = if (hasAccountProblem)
ThemeManager.resolveAttribute(R.attr.primaryText, context) else
primaryTextColor(),
highlight = BrowserMenuHighlight.HighPriority(
endImageResource = R.drawable.ic_sync_disconnected,
backgroundTint = context.getColorFromAttr(R.attr.syncDisconnectedBackground),
canPropagate = false
),
isHighlighted = { hasAccountProblem }
) {
onItemTapped.invoke(ToolbarMenu.Item.Settings)
}
val settingsItem = BrowserMenuHighlightableItem(
label = context.getString(R.string.browser_menu_settings),
startImageResource = R.drawable.ic_settings,
iconTintColorResource = primaryTextColor(),
textColorResource = if (hasAccountProblem)
ThemeManager.resolveAttribute(R.attr.primaryText, context) else
primaryTextColor,
highlight = BrowserMenuHighlight.HighPriority(
endImageResource = R.drawable.ic_sync_disconnected,
backgroundTint = context.getColorFromAttr(R.attr.syncDisconnectedBackground),
canPropagate = false
),
isHighlighted = { hasAccountProblem }
) {
onItemTapped.invoke(ToolbarMenu.Item.Settings)
}
val bookmarksItem = BrowserMenuImageTextCheckboxButton(
imageResource = R.drawable.ic_bookmarks_menu,
iconTintColorResource = primaryTextColor(),
label = context.getString(R.string.library_bookmarks),
labelListener = {
onItemTapped.invoke(ToolbarMenu.Item.Bookmarks)
},
primaryStateIconResource = R.drawable.ic_bookmark_outline,
secondaryStateIconResource = R.drawable.ic_bookmark_filled,
tintColorResource = accentBrightTextColor(),
primaryLabel = context.getString(R.string.add_label),
secondaryLabel = context.getString(R.string.edit_label),
isInPrimaryState = { !isCurrentUrlBookmarked }
) {
handleBookmarkItemTapped()
}
val bookmarksItem = BrowserMenuImageTextCheckboxButton(
imageResource = R.drawable.ic_bookmarks_menu,
iconTintColorResource = primaryTextColor(),
label = context.getString(R.string.library_bookmarks),
labelListener = {
onItemTapped.invoke(ToolbarMenu.Item.Bookmarks)
},
primaryStateIconResource = R.drawable.ic_bookmark_outline,
secondaryStateIconResource = R.drawable.ic_bookmark_filled,
tintColorResource = accentBrightTextColor(),
primaryLabel = context.getString(R.string.add_label),
secondaryLabel = context.getString(R.string.edit_label),
isInPrimaryState = { !isCurrentUrlBookmarked }
) {
handleBookmarkItemTapped()
}
@VisibleForTesting(otherwise = PRIVATE)
val newCoreMenuItems by lazy {
val menuItems =
listOfNotNull(
if (isTopToolbarSelected) menuToolbar else null,
if (shouldUseBottomToolbar) null else menuToolbar,
newTabItem,
BrowserMenuDivider(),
bookmarksItem,
@ -526,8 +535,8 @@ class DefaultToolbarMenu(
saveToCollectionItem,
BrowserMenuDivider(),
settingsItem,
if (isTopToolbarSelected) null else BrowserMenuDivider(),
if (isTopToolbarSelected) null else menuToolbar
if (shouldUseBottomToolbar) BrowserMenuDivider() else null,
if (shouldUseBottomToolbar) menuToolbar else null
)
menuItems

@ -779,7 +779,7 @@ class HomeFragment : Fragment() {
HomeFragmentDirections.actionGlobalSettingsFragment()
)
}
HomeMenu.Item.SyncedTabs -> {
HomeMenu.Item.SyncTabs -> {
hideOnboardingIfNeeded()
nav(
R.id.homeFragment,
@ -842,14 +842,14 @@ class HomeFragment : Fragment() {
}
)
}
HomeMenu.Item.Sync -> {
HomeMenu.Item.ReconnectSync -> {
hideOnboardingIfNeeded()
nav(
R.id.homeFragment,
HomeFragmentDirections.actionGlobalAccountProblemFragment()
)
}
HomeMenu.Item.AddonsManager -> {
HomeMenu.Item.Extensions -> {
nav(
R.id.homeFragment,
HomeFragmentDirections.actionGlobalAddonsManagementFragment()

@ -13,15 +13,18 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import mozilla.components.browser.menu.BrowserMenuBuilder
import mozilla.components.browser.menu.BrowserMenuHighlight
import mozilla.components.browser.menu.BrowserMenuItem
import mozilla.components.browser.menu.ext.getHighlight
import mozilla.components.browser.menu.item.BrowserMenuDivider
import mozilla.components.browser.menu.item.BrowserMenuHighlightableItem
import mozilla.components.browser.menu.item.BrowserMenuImageSwitch
import mozilla.components.browser.menu.item.BrowserMenuImageText
import mozilla.components.browser.menu.item.BrowserMenuItemToolbar
import mozilla.components.concept.sync.AccountObserver
import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.support.ktx.android.content.getColorFromAttr
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.R
import org.mozilla.fenix.experiments.ExperimentBranch
import org.mozilla.fenix.experiments.Experiments
@ -31,6 +34,7 @@ import org.mozilla.fenix.ext.withExperiment
import org.mozilla.fenix.theme.ThemeManager
import org.mozilla.fenix.whatsnew.WhatsNew
@Suppress("LargeClass", "LongMethod")
class HomeMenu(
private val lifecycleOwner: LifecycleOwner,
private val context: Context,
@ -39,26 +43,29 @@ class HomeMenu(
private val onHighlightPresent: (BrowserMenuHighlight) -> Unit = {}
) {
sealed class Item {
data class Back(val viewHistory: Boolean) : Item()
data class Forward(val viewHistory: Boolean) : Item()
object Bookmarks : Item()
object History : Item()
object Downloads : Item()
object Extensions : Item()
object SyncTabs : Item()
object WhatsNew : Item()
object Help : Item()
object AddonsManager : Item()
object Settings : Item()
object SyncedTabs : Item()
object History : Item()
object Bookmarks : Item()
object Downloads : Item()
object Quit : Item()
object Sync : Item()
object ReconnectSync : Item()
data class DesktopMode(val checked: Boolean) : Item()
}
private val primaryTextColor =
ThemeManager.resolveAttribute(R.attr.primaryText, context)
private val syncDisconnectedColor = ThemeManager.resolveAttribute(R.attr.syncDisconnected, context)
private val syncDisconnectedBackgroundColor = context.getColorFromAttr(R.attr.syncDisconnectedBackground)
private val syncDisconnectedColor =
ThemeManager.resolveAttribute(R.attr.syncDisconnected, context)
private val syncDisconnectedBackgroundColor =
context.getColorFromAttr(R.attr.syncDisconnectedBackground)
private val menuCategoryTextColor =
ThemeManager.resolveAttribute(R.attr.menuCategoryText, context)
private val shouldUseBottomToolbar = context.settings().shouldUseBottomToolbar
// 'Reconnect' and 'Quit' items aren't needed most of the time, so we'll only create the if necessary.
@ -74,7 +81,7 @@ class HomeMenu(
),
isHighlighted = { true }
) {
onItemTapped.invoke(Item.Sync)
onItemTapped.invoke(Item.ReconnectSync)
}
}
@ -88,7 +95,35 @@ class HomeMenu(
}
}
private val coreMenuItems by lazy {
val menuToolbar by lazy {
val back = BrowserMenuItemToolbar.TwoStateButton(
primaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_back,
primaryContentDescription = context.getString(R.string.browser_menu_back),
primaryImageTintResource = primaryTextColor,
isInPrimaryState = { false },
secondaryImageTintResource = ThemeManager.resolveAttribute(R.attr.disabled, context),
disableInSecondaryState = true,
longClickListener = { onItemTapped.invoke(Item.Back(viewHistory = true)) }
) {
onItemTapped.invoke(Item.Back(viewHistory = false))
}
val forward = BrowserMenuItemToolbar.TwoStateButton(
primaryImageResource = mozilla.components.ui.icons.R.drawable.mozac_ic_forward,
primaryContentDescription = context.getString(R.string.browser_menu_forward),
primaryImageTintResource = primaryTextColor,
isInPrimaryState = { false },
secondaryImageTintResource = ThemeManager.resolveAttribute(R.attr.disabled, context),
disableInSecondaryState = true,
longClickListener = { onItemTapped.invoke(Item.Forward(viewHistory = true)) }
) {
onItemTapped.invoke(Item.Forward(viewHistory = false))
}
BrowserMenuItemToolbar(listOf(back, forward))
}
private val oldCoreMenuItems by lazy {
val whatsNewItem = BrowserMenuHighlightableItem(
context.getString(R.string.browser_menu_whats_new),
R.drawable.ic_whats_new,
@ -143,7 +178,7 @@ class HomeMenu(
R.drawable.ic_addons_extensions,
primaryTextColor
) {
onItemTapped.invoke(Item.AddonsManager)
onItemTapped.invoke(Item.Extensions)
}
val settingsItem = BrowserMenuImageText(
@ -159,7 +194,7 @@ class HomeMenu(
R.drawable.ic_synced_tabs,
primaryTextColor
) {
onItemTapped.invoke(Item.SyncedTabs)
onItemTapped.invoke(Item.SyncTabs)
}
val helpItem = BrowserMenuImageText(
@ -178,21 +213,15 @@ class HomeMenu(
onItemTapped.invoke(Item.Downloads)
}
val desktopItem = BrowserMenuImageSwitch(
imageResource = R.drawable.ic_desktop,
label = context.getString(R.string.browser_menu_desktop_site),
initialState = { context.settings().openNextTabInDesktopMode }
) { checked ->
onItemTapped.invoke(Item.DesktopMode(checked))
}
// Only query account manager if it has been initialized.
// We don't want to cause its initialization just for this check.
val accountAuthItem = if (context.components.backgroundServices.accountManagerAvailableQueue.isReady()) {
if (context.components.backgroundServices.accountManager.accountNeedsReauth()) reconnectToSyncItem else null
} else {
null
}
val accountAuthItem =
if (context.components.backgroundServices.accountManagerAvailableQueue.isReady() &&
context.components.backgroundServices.accountManager.accountNeedsReauth()) {
reconnectToSyncItem
} else {
null
}
val settings = context.components.settings
@ -203,9 +232,6 @@ class HomeMenu(
syncedTabsItem,
bookmarksItem,
historyItem,
BrowserMenuDivider(),
desktopItem,
BrowserMenuDivider(),
downloadsItem,
BrowserMenuDivider(),
addons,
@ -224,9 +250,157 @@ class HomeMenu(
}
}
val desktopItem = BrowserMenuImageSwitch(
imageResource = R.drawable.ic_desktop,
label = context.getString(R.string.browser_menu_desktop_site),
initialState = { context.settings().openNextTabInDesktopMode }
) { checked ->
onItemTapped.invoke(Item.DesktopMode(checked))
}
@Suppress("ComplexMethod")
private fun newCoreMenuItems(): List<BrowserMenuItem> {
val experiments = context.components.analytics.experiments
val settings = context.components.settings
val bookmarksIcon = experiments.withExperiment(Experiments.BOOKMARK_ICON) {
when (it) {
ExperimentBranch.TREATMENT -> R.drawable.ic_bookmark_list
else -> R.drawable.ic_bookmark_filled
}
}
val bookmarksItem = BrowserMenuImageText(
context.getString(R.string.library_bookmarks),
bookmarksIcon,
primaryTextColor
) {
onItemTapped.invoke(Item.Bookmarks)
}
// We want to validate that the Nimbus experiments library is working, from the android UI
// all the way back to the data science backend. We're not testing the user's preference
// or response, we're end-to-end testing the experiments platform.
// So here, we're running multiple identical branches with the same treatment, and if the
// user isn't targeted, then we get still get the same treatment.
// The `let` block is degenerate here, but left here so as to document the form of how experiments
// are implemented here.
val historyIcon = experiments.withExperiment(Experiments.A_A_NIMBUS_VALIDATION) {
when (it) {
ExperimentBranch.A1 -> R.drawable.ic_history
ExperimentBranch.A2 -> R.drawable.ic_history
else -> R.drawable.ic_history
}
}
val historyItem = BrowserMenuImageText(
context.getString(R.string.library_history),
historyIcon,
primaryTextColor
) {
onItemTapped.invoke(Item.History)
}
val downloadsItem = BrowserMenuImageText(
context.getString(R.string.library_downloads),
R.drawable.ic_download,
primaryTextColor
) {
onItemTapped.invoke(Item.Downloads)
}
val extensionsItem = BrowserMenuImageText(
context.getString(R.string.browser_menu_add_ons),
R.drawable.ic_addons_extensions,
primaryTextColor
) {
onItemTapped.invoke(Item.Extensions)
}
val syncSignInItem = BrowserMenuImageText(
context.getString(R.string.library_synced_tabs),
R.drawable.ic_synced_tabs,
primaryTextColor
) {
onItemTapped.invoke(Item.SyncTabs)
}
val whatsNewItem = BrowserMenuHighlightableItem(
context.getString(R.string.browser_menu_whats_new),
R.drawable.ic_whats_new,
iconTintColorResource = primaryTextColor,
highlight = BrowserMenuHighlight.LowPriority(
notificationTint = getColor(context, R.color.whats_new_notification_color)
),
isHighlighted = { WhatsNew.shouldHighlightWhatsNew(context) }
) {
onItemTapped.invoke(Item.WhatsNew)
}
val helpItem = BrowserMenuImageText(
context.getString(R.string.browser_menu_help),
R.drawable.ic_help,
primaryTextColor
) {
onItemTapped.invoke(Item.Help)
}
val settingsItem = BrowserMenuImageText(
context.getString(R.string.browser_menu_settings),
R.drawable.ic_settings,
primaryTextColor
) {
onItemTapped.invoke(Item.Settings)
}
// Only query account manager if it has been initialized.
// We don't want to cause its initialization just for this check.
val accountAuthItem =
if (context.components.backgroundServices.accountManagerAvailableQueue.isReady() &&
context.components.backgroundServices.accountManager.accountNeedsReauth()) {
reconnectToSyncItem
} else {
null
}
val menuItems = listOfNotNull(
if (shouldUseBottomToolbar) null else menuToolbar,
bookmarksItem,
historyItem,
downloadsItem,
extensionsItem,
syncSignInItem,
accountAuthItem,
BrowserMenuDivider(),
desktopItem,
BrowserMenuDivider(),
whatsNewItem,
helpItem,
settingsItem,
if (settings.shouldDeleteBrowsingDataOnQuit) quitItem else null,
if (shouldUseBottomToolbar) BrowserMenuDivider() else null,
if (shouldUseBottomToolbar) menuToolbar else null
).also { items ->
items.getHighlight()?.let { onHighlightPresent(it) }
}
return menuItems
}
init {
val menuItems = if (FeatureFlags.toolbarMenuFeature) {
newCoreMenuItems()
} else {
oldCoreMenuItems
}
// Report initial state.
onMenuBuilderChanged(BrowserMenuBuilder(coreMenuItems))
onMenuBuilderChanged(BrowserMenuBuilder(menuItems))
val menuItemsWithReconnectItem = if (FeatureFlags.toolbarMenuFeature) {
menuItems
} else {
// reconnect item is manually added to the beginning of the list
listOf(reconnectToSyncItem) + menuItems
}
// Observe account state changes, and update menu item builder with a new set of items.
context.components.backgroundServices.accountManagerAvailableQueue.runIfReadyOrQueue {
@ -237,9 +411,11 @@ class HomeMenu(
context.components.backgroundServices.accountManager.register(object : AccountObserver {
override fun onAuthenticationProblems() {
lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
onMenuBuilderChanged(BrowserMenuBuilder(
listOf(reconnectToSyncItem) + coreMenuItems
))
onMenuBuilderChanged(
BrowserMenuBuilder(
menuItemsWithReconnectItem
)
)
}
}
@ -247,7 +423,7 @@ class HomeMenu(
lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
onMenuBuilderChanged(
BrowserMenuBuilder(
coreMenuItems
menuItems
)
)
}
@ -257,7 +433,7 @@ class HomeMenu(
lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
onMenuBuilderChanged(
BrowserMenuBuilder(
coreMenuItems
menuItems
)
)
}

@ -187,7 +187,6 @@ class DefaultBrowserToolbarMenuControllerTest {
@Test
fun `WHEN open in Fenix menu item is pressed THEN menu item is handled correctly`() = runBlockingTest {
if (!FeatureFlags.toolbarMenuFeature) {
val customTab = createCustomTab("https://mozilla.org")
browserStore.dispatch(CustomTabListAction.AddCustomTabAction(customTab)).joinBlocking()
val controller = createController(
@ -207,46 +206,31 @@ class DefaultBrowserToolbarMenuControllerTest {
verify { activity.finishAndRemoveTask() }
}
}
// todo === End ===
@Test
fun `WHEN quit menu item is pressed THEN menu item is handled correctly`() = runBlockingTest {
if (!FeatureFlags.toolbarMenuFeature) {
val item = ToolbarMenu.Item.Quit
val testScope = this
val controller = createController(scope = this, store = browserStore)
controller.handleToolbarItemInteraction(item)
verify { deleteAndQuit(activity, testScope, null) }
}
}
@Test
fun handleToolbarOpenInAppPress() = runBlockingTest {
if (!FeatureFlags.toolbarMenuFeature) {
val item = ToolbarMenu.Item.OpenInApp
fun `WHEN reader mode menu item is pressed THEN handle appearance change`() = runBlockingTest {
val item = ToolbarMenu.Item.CustomizeReaderView
val controller = createController(scope = this, store = browserStore)
val controller = createController(scope = this, store = browserStore)
controller.handleToolbarItemInteraction(item)
controller.handleToolbarItemInteraction(item)
verify { settings.openInAppOpened = true }
}
verify { readerModeController.showControls() }
verify { metrics.track(Event.ReaderModeAppearanceOpened) }
}
@Test
fun `WHEN reader mode menu item is pressed THEN handle appearance change`() = runBlockingTest {
val item = ToolbarMenu.Item.CustomizeReaderView
fun `WHEN quit menu item is pressed THEN menu item is handled correctly`() = runBlockingTest {
val item = ToolbarMenu.Item.Quit
val testScope = this
val controller = createController(scope = this, store = browserStore)
controller.handleToolbarItemInteraction(item)
verify { readerModeController.showControls() }
verify { metrics.track(Event.ReaderModeAppearanceOpened) }
verify { deleteAndQuit(activity, testScope, null) }
}
// todo === End ===
@Test
fun `WHEN backwards nav menu item is pressed THEN the session navigates back with active session`() = runBlockingTest {

@ -6,38 +6,34 @@ package org.mozilla.fenix.toolbar
import android.content.Context
import android.net.Uri
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.spyk
import io.mockk.unmockkStatic
import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineDispatcher
import mozilla.components.browser.state.action.ContentAction
import mozilla.components.browser.state.action.TabListAction
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.storage.BookmarksStorage
import mozilla.components.support.test.ext.joinBlocking
import mozilla.components.support.test.rule.MainCoroutineRule
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.components.toolbar.DefaultToolbarMenu
import org.mozilla.fenix.ext.settings
@ExperimentalCoroutinesApi
class DefaultToolbarMenuTest {
private lateinit var store: BrowserStore
private lateinit var lifecycleOwner: MockedLifecycleOwner
private lateinit var lifecycleOwner: LifecycleOwner
private lateinit var toolbarMenu: DefaultToolbarMenu
private lateinit var context: Context
private lateinit var bookmarksStorage: BookmarksStorage
@ -52,8 +48,11 @@ class DefaultToolbarMenuTest {
mockkStatic(Uri::class)
every { Uri.parse(any()) } returns mockk(relaxed = true)
lifecycleOwner = mockk(relaxed = true)
context = mockk(relaxed = true)
every { context.theme } returns mockk(relaxed = true)
bookmarksStorage = mockk(relaxed = true)
store = BrowserStore(
BrowserState(
@ -63,53 +62,91 @@ class DefaultToolbarMenuTest {
), selectedTabId = "1"
)
)
lifecycleOwner = MockedLifecycleOwner(Lifecycle.State.STARTED)
}
@After
fun tearDown() {
unmockkStatic(Uri::class)
}
private fun createMenu() {
toolbarMenu = spyk(DefaultToolbarMenu(
context = context,
store = store,
hasAccountProblem = false,
shouldReverseItems = false,
onItemTapped = { },
lifecycleOwner = lifecycleOwner,
bookmarksStorage = bookmarksStorage,
isPinningSupported = false
))
every { toolbarMenu.updateCurrentUrlIsBookmarked(any()) } returns Unit
every { toolbarMenu.updateCurrentUrlIsBookmarked(any()) } returns mockk()
every { toolbarMenu.shouldShowOpenInApp() } returns mockk()
}
@After
fun tearDown() {
unmockkStatic(Uri::class)
@Test
fun `WHEN the bottom toolbar is set THEN the first item in the list is not the navigation`() {
if (FeatureFlags.toolbarMenuFeature) {
every { context.settings().shouldUseBottomToolbar } returns true
createMenu()
val menuItems = toolbarMenu.newCoreMenuItems
assertNotNull(menuItems)
val firstItem = menuItems[0]
val newTabItem = toolbarMenu.newTabItem
assertEquals(newTabItem, firstItem)
}
}
@Test
fun `WHEN url changes THEN bookmarked state is updated`() {
toolbarMenu.registerForIsBookmarkedUpdates()
fun `WHEN the top toolbar is set THEN the first item in the list is the navigation`() {
if (FeatureFlags.toolbarMenuFeature) {
every { context.settings().shouldUseBottomToolbar } returns false
createMenu()
val newUrl = "https://mozilla.org"
val menuItems = toolbarMenu.newCoreMenuItems
assertNotNull(menuItems)
store.dispatch(ContentAction.UpdateUrlAction("1", newUrl)).joinBlocking()
verify(exactly = 1) { toolbarMenu.updateCurrentUrlIsBookmarked(newUrl) }
val firstItem = menuItems[0]
val navToolbar = toolbarMenu.menuToolbar
assertEquals(navToolbar, firstItem)
}
}
@Test
fun `WHEN selected tab changes THEN bookmarked state is updated`() {
toolbarMenu.registerForIsBookmarkedUpdates()
fun `WHEN the bottom toolbar is set THEN the nav menu should be the last item`() {
if (FeatureFlags.toolbarMenuFeature) {
every { context.settings().shouldUseBottomToolbar } returns true
val newSelectedTab = store.state.findTab("2")
assertNotNull(newSelectedTab)
createMenu()
store.dispatch(TabListAction.SelectTabAction(newSelectedTab!!.id)).joinBlocking()
verify(exactly = 1) { toolbarMenu.updateCurrentUrlIsBookmarked(newSelectedTab.content.url) }
}
val menuItems = toolbarMenu.newCoreMenuItems
assertNotNull(menuItems)
val lastItem = menuItems[menuItems.size - 1]
val navToolbar = toolbarMenu.menuToolbar
internal class MockedLifecycleOwner(initialState: Lifecycle.State) : LifecycleOwner {
val lifecycleRegistry = LifecycleRegistry(this).apply {
currentState = initialState
assertEquals(navToolbar, lastItem)
}
}
override fun getLifecycle(): Lifecycle = lifecycleRegistry
@Test
fun `WHEN the top toolbar is set THEN settings should be the last item`() {
if (FeatureFlags.toolbarMenuFeature) {
every { context.settings().shouldUseBottomToolbar } returns false
createMenu()
val menuItems = toolbarMenu.newCoreMenuItems
assertNotNull(menuItems)
val lastItem = menuItems[menuItems.size - 1]
val settingsItem = toolbarMenu.settingsItem
assertEquals(settingsItem, lastItem)
}
}
}

Loading…
Cancel
Save