[fenix] For https://github.com/mozilla-mobile/fenix/issues/24991 - Refactor the HomeMenu creation from HomeFragment to HomeMenuBuilder
parent
4eb6d2ea18
commit
b04e075f51
@ -0,0 +1,185 @@
|
||||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.home
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.annotation.VisibleForTesting.PRIVATE
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavController
|
||||
import mozilla.appservices.places.BookmarkRoot
|
||||
import mozilla.components.browser.menu.view.MenuButton
|
||||
import mozilla.components.service.glean.private.NoExtras
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.GleanMetrics.Events
|
||||
import org.mozilla.fenix.GleanMetrics.HomeScreen
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.components.accounts.AccountState
|
||||
import org.mozilla.fenix.ext.nav
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.settings.SupportUtils
|
||||
import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
|
||||
import org.mozilla.fenix.theme.ThemeManager
|
||||
import org.mozilla.fenix.whatsnew.WhatsNew
|
||||
import java.lang.ref.WeakReference
|
||||
import org.mozilla.fenix.GleanMetrics.HomeMenu as HomeMenuMetrics
|
||||
|
||||
/**
|
||||
* Helper class for building the [HomeMenu].
|
||||
*
|
||||
* @property view The [View] to attach the snackbar to.
|
||||
* @property context An Android [Context].
|
||||
* @property lifecycleOwner [LifecycleOwner] for the view.
|
||||
* @property homeActivity [HomeActivity] used to open URLs in a new tab.
|
||||
* @property navController [NavController] used for navigation.
|
||||
* @property menuButton The [MenuButton] that will be used to create a menu when the button is
|
||||
* clicked.
|
||||
* @property hideOnboardingIfNeeded Lambda invoked to dismiss onboarding.
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
class HomeMenuBuilder(
|
||||
private val view: View,
|
||||
private val context: Context,
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
private val homeActivity: HomeActivity,
|
||||
private val navController: NavController,
|
||||
private val menuButton: WeakReference<MenuButton>,
|
||||
private val hideOnboardingIfNeeded: () -> Unit,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Builds the [HomeMenu].
|
||||
*/
|
||||
fun build() {
|
||||
HomeMenu(
|
||||
lifecycleOwner = lifecycleOwner,
|
||||
context = context,
|
||||
onItemTapped = ::onItemTapped,
|
||||
onHighlightPresent = { menuButton.get()?.setHighlight(it) },
|
||||
onMenuBuilderChanged = { menuButton.get()?.menuBuilder = it }
|
||||
)
|
||||
|
||||
menuButton.get()?.setColorFilter(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
ThemeManager.resolveAttribute(R.attr.textPrimary, context)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked when a menu item is tapped on.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@VisibleForTesting(otherwise = PRIVATE)
|
||||
internal fun onItemTapped(item: HomeMenu.Item) {
|
||||
if (item !is HomeMenu.Item.DesktopMode) {
|
||||
hideOnboardingIfNeeded()
|
||||
}
|
||||
|
||||
when (item) {
|
||||
HomeMenu.Item.Settings -> {
|
||||
HomeMenuMetrics.settingsItemClicked.record(NoExtras())
|
||||
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalSettingsFragment()
|
||||
)
|
||||
}
|
||||
HomeMenu.Item.CustomizeHome -> {
|
||||
HomeScreen.customizeHomeClicked.record(NoExtras())
|
||||
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalHomeSettingsFragment()
|
||||
)
|
||||
}
|
||||
is HomeMenu.Item.SyncAccount -> {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
when (item.accountState) {
|
||||
AccountState.AUTHENTICATED ->
|
||||
HomeFragmentDirections.actionGlobalAccountSettingsFragment()
|
||||
AccountState.NEEDS_REAUTHENTICATION ->
|
||||
HomeFragmentDirections.actionGlobalAccountProblemFragment()
|
||||
AccountState.NO_ACCOUNT ->
|
||||
HomeFragmentDirections.actionGlobalTurnOnSync()
|
||||
}
|
||||
)
|
||||
}
|
||||
HomeMenu.Item.Bookmarks -> {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalBookmarkFragment(BookmarkRoot.Mobile.id)
|
||||
)
|
||||
}
|
||||
HomeMenu.Item.History -> {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalHistoryFragment()
|
||||
)
|
||||
}
|
||||
HomeMenu.Item.Downloads -> {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalDownloadsFragment()
|
||||
)
|
||||
}
|
||||
HomeMenu.Item.Help -> {
|
||||
homeActivity.openToBrowserAndLoad(
|
||||
searchTermOrURL = SupportUtils.getSumoURLForTopic(
|
||||
context = context,
|
||||
topic = SupportUtils.SumoTopic.HELP
|
||||
),
|
||||
newTab = true,
|
||||
from = BrowserDirection.FromHome
|
||||
)
|
||||
}
|
||||
HomeMenu.Item.WhatsNew -> {
|
||||
WhatsNew.userViewedWhatsNew(context)
|
||||
Events.whatsNewTapped.record(NoExtras())
|
||||
|
||||
homeActivity.openToBrowserAndLoad(
|
||||
searchTermOrURL = SupportUtils.getWhatsNewUrl(context),
|
||||
newTab = true,
|
||||
from = BrowserDirection.FromHome
|
||||
)
|
||||
}
|
||||
HomeMenu.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
|
||||
// is dismissed.
|
||||
deleteAndQuit(
|
||||
activity = homeActivity,
|
||||
coroutineScope = lifecycleOwner.lifecycleScope,
|
||||
snackbar = FenixSnackbar.make(
|
||||
view = view,
|
||||
isDisplayedWithBrowserToolbar = false
|
||||
)
|
||||
)
|
||||
}
|
||||
HomeMenu.Item.ReconnectSync -> {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalAccountProblemFragment()
|
||||
)
|
||||
}
|
||||
HomeMenu.Item.Extensions -> {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalAddonsManagementFragment()
|
||||
)
|
||||
}
|
||||
is HomeMenu.Item.DesktopMode -> {
|
||||
context.settings().openNextTabInDesktopMode = item.checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,235 @@
|
||||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.home
|
||||
|
||||
import android.view.View
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.navigation.NavController
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.appservices.places.BookmarkRoot
|
||||
import mozilla.components.browser.menu.view.MenuButton
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import mozilla.telemetry.glean.testing.GleanTestRule
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.GleanMetrics.Events
|
||||
import org.mozilla.fenix.GleanMetrics.HomeScreen
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.accounts.AccountState
|
||||
import org.mozilla.fenix.ext.nav
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.settings.SupportUtils
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
import org.mozilla.fenix.whatsnew.WhatsNew
|
||||
import java.lang.ref.WeakReference
|
||||
import org.mozilla.fenix.GleanMetrics.HomeMenu as HomeMenuMetrics
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class HomeMenuBuilderTest {
|
||||
|
||||
@get:Rule
|
||||
val gleanTestRule = GleanTestRule(testContext)
|
||||
|
||||
private lateinit var view: View
|
||||
private lateinit var lifecycleOwner: LifecycleOwner
|
||||
private lateinit var homeActivity: HomeActivity
|
||||
private lateinit var navController: NavController
|
||||
private lateinit var menuButton: WeakReference<MenuButton>
|
||||
private lateinit var homeMenuBuilder: HomeMenuBuilder
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
view = mockk(relaxed = true)
|
||||
lifecycleOwner = mockk(relaxed = true)
|
||||
homeActivity = mockk(relaxed = true)
|
||||
menuButton = mockk(relaxed = true)
|
||||
navController = mockk(relaxed = true)
|
||||
|
||||
homeMenuBuilder = HomeMenuBuilder(
|
||||
view = view,
|
||||
context = testContext,
|
||||
lifecycleOwner = lifecycleOwner,
|
||||
homeActivity = homeActivity,
|
||||
navController = navController,
|
||||
menuButton = menuButton,
|
||||
hideOnboardingIfNeeded = {},
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN Settings menu item is tapped THEN navigate to settings fragment and record metrics`() {
|
||||
assertFalse(HomeMenuMetrics.settingsItemClicked.testHasValue())
|
||||
|
||||
homeMenuBuilder.onItemTapped(HomeMenu.Item.Settings)
|
||||
|
||||
assertTrue(HomeMenuMetrics.settingsItemClicked.testHasValue())
|
||||
|
||||
verify {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalSettingsFragment()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN Customize Home menu item is tapped THEN navigate to home settings fragment and record metrics`() {
|
||||
assertFalse(HomeScreen.customizeHomeClicked.testHasValue())
|
||||
|
||||
homeMenuBuilder.onItemTapped(HomeMenu.Item.CustomizeHome)
|
||||
|
||||
assertTrue(HomeScreen.customizeHomeClicked.testHasValue())
|
||||
|
||||
verify {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalHomeSettingsFragment()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN various sync account state WHEN Sync Account menu item is tapped THEN navigate to the appropriate sync fragment`() {
|
||||
homeMenuBuilder.onItemTapped(HomeMenu.Item.SyncAccount(AccountState.AUTHENTICATED))
|
||||
|
||||
verify {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalAccountSettingsFragment()
|
||||
)
|
||||
}
|
||||
|
||||
homeMenuBuilder.onItemTapped(HomeMenu.Item.SyncAccount(AccountState.NEEDS_REAUTHENTICATION))
|
||||
|
||||
verify {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalAccountProblemFragment()
|
||||
)
|
||||
}
|
||||
|
||||
homeMenuBuilder.onItemTapped(HomeMenu.Item.SyncAccount(AccountState.NO_ACCOUNT))
|
||||
|
||||
verify {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalTurnOnSync()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN Bookmarks menu item is tapped THEN navigate to the bookmarks fragment`() {
|
||||
homeMenuBuilder.onItemTapped(HomeMenu.Item.Bookmarks)
|
||||
|
||||
verify {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalBookmarkFragment(BookmarkRoot.Mobile.id)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN History menu item is tapped THEN navigate to the history fragment`() {
|
||||
homeMenuBuilder.onItemTapped(HomeMenu.Item.History)
|
||||
|
||||
verify {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalHistoryFragment()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN Downloads menu item is tapped THEN navigate to the downloads fragment`() {
|
||||
homeMenuBuilder.onItemTapped(HomeMenu.Item.Downloads)
|
||||
|
||||
verify {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalDownloadsFragment()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN Help menu item is tapped THEN open the browser to the SUMO help page`() {
|
||||
homeMenuBuilder.onItemTapped(HomeMenu.Item.Help)
|
||||
|
||||
verify {
|
||||
homeActivity.openToBrowserAndLoad(
|
||||
searchTermOrURL = SupportUtils.getSumoURLForTopic(
|
||||
context = testContext,
|
||||
topic = SupportUtils.SumoTopic.HELP
|
||||
),
|
||||
newTab = true,
|
||||
from = BrowserDirection.FromHome
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN Whats New menu item is tapped THEN open the browser to the SUMO whats new page and record metrics`() {
|
||||
assertFalse(Events.whatsNewTapped.testHasValue())
|
||||
|
||||
homeMenuBuilder.onItemTapped(HomeMenu.Item.WhatsNew)
|
||||
|
||||
assertTrue(Events.whatsNewTapped.testHasValue())
|
||||
|
||||
verify {
|
||||
WhatsNew.userViewedWhatsNew(testContext)
|
||||
|
||||
homeActivity.openToBrowserAndLoad(
|
||||
searchTermOrURL = SupportUtils.getWhatsNewUrl(testContext),
|
||||
newTab = true,
|
||||
from = BrowserDirection.FromHome
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN Reconnect Sync menu item is tapped THEN navigate to the account problem fragment`() {
|
||||
homeMenuBuilder.onItemTapped(HomeMenu.Item.ReconnectSync)
|
||||
|
||||
verify {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalAccountProblemFragment()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN Extensions menu item is tapped THEN navigate to the addons management fragment`() {
|
||||
homeMenuBuilder.onItemTapped(HomeMenu.Item.Extensions)
|
||||
|
||||
verify {
|
||||
navController.nav(
|
||||
R.id.homeFragment,
|
||||
HomeFragmentDirections.actionGlobalAddonsManagementFragment()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN Desktop Mode menu item is tapped THEN set the desktop mode settings`() {
|
||||
every { testContext.settings() } returns Settings(testContext)
|
||||
|
||||
homeMenuBuilder.onItemTapped(HomeMenu.Item.DesktopMode(checked = true))
|
||||
|
||||
assertTrue(testContext.settings().openNextTabInDesktopMode)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue