From 17f96e433ca1f9c2b2da870ed0670d56444ee035 Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Wed, 26 Jan 2022 12:43:23 -0500 Subject: [PATCH] [fenix] For https://github.com/mozilla-mobile/fenix/issues/23424 - Part 2: Add context menu for contile top sites --- .../SessionControlController.kt | 25 +++++++ .../SessionControlInteractor.kt | 20 ++++++ .../fenix/home/topsites/TopSiteItemMenu.kt | 65 +++++++++++++++---- .../home/topsites/TopSiteItemViewHolder.kt | 6 +- .../mozilla/fenix/settings/SupportUtils.kt | 3 +- app/src/main/res/values/strings.xml | 4 ++ .../DefaultSessionControlControllerTest.kt | 27 ++++++++ .../home/SessionControlInteractorTest.kt | 12 ++++ 8 files changed, 147 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt index 91203d4c5..a6b422fdb 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt @@ -120,6 +120,16 @@ interface SessionControlController { */ fun handleSelectTopSite(topSite: TopSite) + /** + * @see [TopSiteInteractor.onSettingsClicked] + */ + fun handleTopSiteSettingsClicked() + + /** + * @see [TopSiteInteractor.onSponsorPrivacyClicked] + */ + fun handleSponsorPrivacyClicked() + /** * @see [OnboardingInteractor.onStartBrowsingClicked] */ @@ -414,6 +424,21 @@ class DefaultSessionControlController( activity.openToBrowser(BrowserDirection.FromHome) } + override fun handleTopSiteSettingsClicked() { + navController.nav( + R.id.homeFragment, + HomeFragmentDirections.actionGlobalHomeSettingsFragment() + ) + } + + override fun handleSponsorPrivacyClicked() { + activity.openToBrowserAndLoad( + searchTermOrURL = SupportUtils.getGenericSumoURLForTopic(SupportUtils.SumoTopic.SPONSOR_PRIVACY), + newTab = true, + from = BrowserDirection.FromHome + ) + } + @VisibleForTesting internal fun getAvailableSearchEngines() = activity.components.core.store.state.search.searchEngines + diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt index d9e244e41..61574ea0f 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt @@ -213,6 +213,18 @@ interface TopSiteInteractor { */ fun onSelectTopSite(topSite: TopSite) + /** + * Navigates to the Homepage Settings. Called when an user clicks on the "Settings" top site + * menu item. + */ + fun onSettingsClicked() + + /** + * Opens the sponsor privacy support articles. Called when an user clicks on the + * "Our sponsors & your privacy" top site menu item. + */ + fun onSponsorPrivacyClicked() + /** * Called when top site menu is opened. */ @@ -301,6 +313,14 @@ class SessionControlInteractor( controller.handleSelectTopSite(topSite) } + override fun onSettingsClicked() { + controller.handleTopSiteSettingsClicked() + } + + override fun onSponsorPrivacyClicked() { + controller.handleSponsorPrivacyClicked() + } + override fun onStartBrowsingClicked() { controller.handleStartBrowsingClicked() } diff --git a/app/src/main/java/org/mozilla/fenix/home/topsites/TopSiteItemMenu.kt b/app/src/main/java/org/mozilla/fenix/home/topsites/TopSiteItemMenu.kt index cda98e512..85305557c 100644 --- a/app/src/main/java/org/mozilla/fenix/home/topsites/TopSiteItemMenu.kt +++ b/app/src/main/java/org/mozilla/fenix/home/topsites/TopSiteItemMenu.kt @@ -7,41 +7,80 @@ package org.mozilla.fenix.home.topsites import android.content.Context import mozilla.components.browser.menu.BrowserMenuBuilder import mozilla.components.browser.menu.item.SimpleBrowserMenuItem +import mozilla.components.feature.top.sites.TopSite import org.mozilla.fenix.R +/** + * Helper class for building a context menu for a top site item. + * + * @property context An Android context. + * @property topSite The [TopSite] to show the context menu for. + * @property onItemTapped Callback invoked when the user taps on a menu item. + */ class TopSiteItemMenu( private val context: Context, - private val isPinnedSite: Boolean, + private val topSite: TopSite, private val onItemTapped: (Item) -> Unit = {} ) { sealed class Item { object OpenInPrivateTab : Item() object RenameTopSite : Item() object RemoveTopSite : Item() + object Settings : Item() + object SponsorPrivacy : Item() } val menuBuilder by lazy { BrowserMenuBuilder(menuItems) } private val menuItems by lazy { + val isPinnedSite = topSite is TopSite.Pinned || topSite is TopSite.Default + val isProvidedSite = topSite is TopSite.Provided + listOfNotNull( SimpleBrowserMenuItem( context.getString(R.string.bookmark_menu_open_in_private_tab_button) ) { onItemTapped.invoke(Item.OpenInPrivateTab) }, - if (isPinnedSite) SimpleBrowserMenuItem( - context.getString(R.string.rename_top_site) - ) { - onItemTapped.invoke(Item.RenameTopSite) - } else null, - SimpleBrowserMenuItem( - if (isPinnedSite) { - context.getString(R.string.remove_top_site) - } else { - context.getString(R.string.delete_from_history) + if (isPinnedSite) { + SimpleBrowserMenuItem( + context.getString(R.string.rename_top_site) + ) { + onItemTapped.invoke(Item.RenameTopSite) } - ) { - onItemTapped.invoke(Item.RemoveTopSite) + } else { + null + }, + if (!isProvidedSite) { + SimpleBrowserMenuItem( + if (isPinnedSite) { + context.getString(R.string.remove_top_site) + } else { + context.getString(R.string.delete_from_history) + } + ) { + onItemTapped.invoke(Item.RemoveTopSite) + } + } else { + null + }, + if (isProvidedSite) { + SimpleBrowserMenuItem( + context.getString(R.string.top_sites_menu_settings) + ) { + onItemTapped.invoke(Item.Settings) + } + } else { + null + }, + if (isProvidedSite) { + SimpleBrowserMenuItem( + context.getString(R.string.top_sites_menu_sponsor_privacy) + ) { + onItemTapped.invoke(Item.SponsorPrivacy) + } + } else { + null } ) } diff --git a/app/src/main/java/org/mozilla/fenix/home/topsites/TopSiteItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/topsites/TopSiteItemViewHolder.kt index 915bb3b2f..19037c2f6 100644 --- a/app/src/main/java/org/mozilla/fenix/home/topsites/TopSiteItemViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/topsites/TopSiteItemViewHolder.kt @@ -43,7 +43,7 @@ class TopSiteItemViewHolder( val topSiteMenu = TopSiteItemMenu( context = view.context, - isPinnedSite = topSite is TopSite.Pinned || topSite is TopSite.Default + topSite = topSite ) { item -> when (item) { is TopSiteItemMenu.Item.OpenInPrivateTab -> interactor.onOpenInPrivateTabClicked( @@ -55,12 +55,16 @@ class TopSiteItemViewHolder( is TopSiteItemMenu.Item.RemoveTopSite -> interactor.onRemoveTopSiteClicked( topSite ) + is TopSiteItemMenu.Item.Settings -> interactor.onSettingsClicked() + is TopSiteItemMenu.Item.SponsorPrivacy -> interactor.onSponsorPrivacyClicked() } } val menu = topSiteMenu.menuBuilder.build(view.context).show(anchor = it) + it.setOnTouchListener @SuppressLint("ClickableViewAccessibility") { v, event -> onTouchEvent(v, event, menu) } + true } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt b/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt index 225cee814..4100ff835 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/SupportUtils.kt @@ -53,7 +53,8 @@ object SupportUtils { CUSTOM_SEARCH_ENGINES("custom-search-engines"), SYNC_SETUP("how-set-firefox-sync-firefox-android"), QR_CAMERA_ACCESS("qr-camera-access"), - SMARTBLOCK("smartblock-enhanced-tracking-protection") + SMARTBLOCK("smartblock-enhanced-tracking-protection"), + SPONSOR_PRIVACY("sponsor-privacy") } enum class MozillaPage(internal val path: String) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1050e2f13..c57e2d648 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1901,6 +1901,10 @@ OK Cancel + + Settings + + Our sponsors & your privacy diff --git a/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt b/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt index 740058f10..372775b89 100644 --- a/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/DefaultSessionControlControllerTest.kt @@ -931,6 +931,33 @@ class DefaultSessionControlControllerTest { } } + @Test + fun `WHEN handleTopSiteSettingsClicked is called THEN navigate to the HomeSettingsFragment`() { + createController().handleTopSiteSettingsClicked() + + verify { + navController.navigate( + match { + it.actionId == R.id.action_global_homeSettingsFragment + }, + null + ) + } + } + + @Test + fun `WHEN handleSponsorPrivacyClicked is called THEN `() { + createController().handleSponsorPrivacyClicked() + + verify { + activity.openToBrowserAndLoad( + searchTermOrURL = SupportUtils.getGenericSumoURLForTopic(SupportUtils.SumoTopic.SPONSOR_PRIVACY), + newTab = true, + from = BrowserDirection.FromHome + ) + } + } + private fun createController( hideOnboarding: () -> Unit = { }, registerCollectionStorageObserver: () -> Unit = { }, diff --git a/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt index 6dca109f7..3e7c0fd8b 100644 --- a/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/home/SessionControlInteractorTest.kt @@ -205,6 +205,18 @@ class SessionControlInteractorTest { verify { controller.handlePrivateModeButtonClicked(newMode, hasBeenOnboarded) } } + @Test + fun `WHEN onSettingsClicked is called THEN handleTopSiteSettingsClicked is called`() { + interactor.onSettingsClicked() + verify { controller.handleTopSiteSettingsClicked() } + } + + @Test + fun `WHEN onSponsorPrivacyClicked is called THEN handleSponsorPrivacyClicked is called`() { + interactor.onSponsorPrivacyClicked() + verify { controller.handleSponsorPrivacyClicked() } + } + @Test fun `GIVEN a PocketStoriesInteractor WHEN stories are shown THEN handle it in a PocketStoriesController`() { val shownStories: List = mockk()