Unverified Commit 2397695f authored by Tiger Oakes's avatar Tiger Oakes Committed by GitHub
Browse files

For #10596 - Redirect moz://a URL (#10688)

parent 422cf747
......@@ -98,6 +98,8 @@ import org.mozilla.fenix.home.sessioncontrol.SessionControlView
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder
import org.mozilla.fenix.onboarding.FenixOnboarding
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.settings.SupportUtils.MozillaPage.PRIVATE_NOTICE
import org.mozilla.fenix.settings.SupportUtils.SumoTopic.HELP
import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
import org.mozilla.fenix.tabtray.TabTrayDialogFragment
import org.mozilla.fenix.theme.ThemeManager
......@@ -230,7 +232,7 @@ class HomeFragment : Fragment() {
openSettingsScreen = ::openSettingsScreen,
openSearchScreen = ::navigateToSearch,
openWhatsNewLink = { openCustomTab(SupportUtils.getWhatsNewUrl(activity)) },
openPrivacyNotice = { openCustomTab(SupportUtils.getPrivacyNoticeUrl()) }
openPrivacyNotice = { openCustomTab(SupportUtils.getMozillaPageUrl(PRIVATE_NOTICE)) }
)
)
updateLayout(view)
......@@ -696,10 +698,7 @@ class HomeFragment : Fragment() {
invokePendingDeleteJobs()
hideOnboardingIfNeeded()
(activity as HomeActivity).openToBrowserAndLoad(
searchTermOrURL = SupportUtils.getSumoURLForTopic(
context,
SupportUtils.SumoTopic.HELP
),
searchTermOrURL = SupportUtils.getSumoURLForTopic(context, HELP),
newTab = true,
from = BrowserDirection.FromHome
)
......
......@@ -5,7 +5,6 @@
package org.mozilla.fenix.search
import android.content.Context
import android.content.Intent
import androidx.navigation.NavController
import kotlinx.coroutines.CoroutineScope
......@@ -28,6 +27,8 @@ import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.navigateSafe
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.settings.SupportUtils.MozillaPage.MANIFESTO
/**
* An interface that handles the view manipulation of the Search, triggered by the Interactor
......@@ -45,8 +46,9 @@ interface SearchController {
fun handleSearchShortcutsButtonClicked()
}
@Suppress("TooManyFunctions")
class DefaultSearchController(
private val context: Context,
private val activity: HomeActivity,
private val store: SearchFragmentStore,
private val navController: NavController,
private val viewLifecycleScope: CoroutineScope,
......@@ -54,41 +56,46 @@ class DefaultSearchController(
) : SearchController {
override fun handleUrlCommitted(url: String) {
if (url == "about:crashes") {
// The list of past crashes can be accessed via "settings > about", but desktop and
// fennec users may be used to navigating to "about:crashes". So we intercept this here
// and open the crash list activity instead.
context.startActivity(Intent(context, CrashListActivity::class.java))
return
when (url) {
"about:crashes" -> {
// The list of past crashes can be accessed via "settings > about", but desktop and
// fennec users may be used to navigating to "about:crashes". So we intercept this here
// and open the crash list activity instead.
activity.startActivity(Intent(activity, CrashListActivity::class.java))
}
"moz://a" -> openSearchOrUrl(SupportUtils.getMozillaPageUrl(MANIFESTO))
else -> if (url.isNotBlank()) {
openSearchOrUrl(url)
}
}
}
if (url.isNotBlank()) {
(context as HomeActivity).openToBrowserAndLoad(
searchTermOrURL = url,
newTab = store.state.session == null,
from = BrowserDirection.FromSearch,
engine = store.state.searchEngineSource.searchEngine
)
private fun openSearchOrUrl(url: String) {
activity.openToBrowserAndLoad(
searchTermOrURL = url,
newTab = store.state.session == null,
from = BrowserDirection.FromSearch,
engine = store.state.searchEngineSource.searchEngine
)
val event = if (url.isUrl()) {
Event.EnteredUrl(false)
} else {
val searchAccessPoint = when (store.state.searchAccessPoint) {
NONE -> ACTION
else -> store.state.searchAccessPoint
}
searchAccessPoint?.let { sap ->
MetricsUtils.createSearchEvent(
store.state.searchEngineSource.searchEngine,
context,
sap
)
}
val event = if (url.isUrl()) {
Event.EnteredUrl(false)
} else {
val searchAccessPoint = when (store.state.searchAccessPoint) {
NONE -> ACTION
else -> store.state.searchAccessPoint
}
event?.let { context.metrics.track(it) }
searchAccessPoint?.let { sap ->
MetricsUtils.createSearchEvent(
store.state.searchEngineSource.searchEngine,
activity,
sap
)
}
}
event?.let { activity.metrics.track(it) }
}
override fun handleEditingCancelled() {
......@@ -102,6 +109,7 @@ class DefaultSearchController(
}
override fun handleTextChanged(text: String) {
val settings = activity.settings()
// Display the search shortcuts on each entry of the search fragment (see #5308)
val textMatchesCurrentUrl = store.state.session?.url ?: "" == text
val textMatchesCurrentSearch = store.state.session?.searchTerms ?: "" == text
......@@ -110,31 +118,31 @@ class DefaultSearchController(
store.dispatch(
SearchFragmentAction.ShowSearchShortcutEnginePicker(
(textMatchesCurrentUrl || textMatchesCurrentSearch || text.isEmpty()) &&
context.settings().shouldShowSearchShortcuts
settings.shouldShowSearchShortcuts
)
)
store.dispatch(
SearchFragmentAction.AllowSearchSuggestionsInPrivateModePrompt(
text.isNotEmpty() &&
(context as HomeActivity).browsingModeManager.mode.isPrivate &&
!context.settings().shouldShowSearchSuggestionsInPrivate &&
!context.settings().showSearchSuggestionsInPrivateOnboardingFinished
activity.browsingModeManager.mode.isPrivate &&
!settings.shouldShowSearchSuggestionsInPrivate &&
!settings.showSearchSuggestionsInPrivateOnboardingFinished
)
)
}
override fun handleUrlTapped(url: String) {
(context as HomeActivity).openToBrowserAndLoad(
activity.openToBrowserAndLoad(
searchTermOrURL = url,
newTab = store.state.session == null,
from = BrowserDirection.FromSearch
)
context.metrics.track(Event.EnteredUrl(false))
activity.metrics.track(Event.EnteredUrl(false))
}
override fun handleSearchTermsTapped(searchTerms: String) {
(context as HomeActivity).openToBrowserAndLoad(
activity.openToBrowserAndLoad(
searchTermOrURL = searchTerms,
newTab = store.state.session == null,
from = BrowserDirection.FromSearch,
......@@ -150,18 +158,18 @@ class DefaultSearchController(
val event = searchAccessPoint?.let { sap ->
MetricsUtils.createSearchEvent(
store.state.searchEngineSource.searchEngine,
context,
activity,
sap
)
}
event?.let { context.metrics.track(it) }
event?.let { activity.metrics.track(it) }
}
override fun handleSearchShortcutEngineSelected(searchEngine: SearchEngine) {
store.dispatch(SearchFragmentAction.SearchShortcutEngineSelected(searchEngine))
val isCustom =
CustomSearchEngineStore.isCustomSearchEngine(context, searchEngine.identifier)
context.metrics.track(Event.SearchShortcutSelected(searchEngine, isCustom))
CustomSearchEngineStore.isCustomSearchEngine(activity, searchEngine.identifier)
activity.metrics.track(Event.SearchShortcutSelected(searchEngine, isCustom))
}
override fun handleSearchShortcutsButtonClicked() {
......@@ -175,14 +183,14 @@ class DefaultSearchController(
}
override fun handleExistingSessionSelected(session: Session) {
context.components.core.sessionManager.select(session)
(context as HomeActivity).openToBrowser(
activity.components.core.sessionManager.select(session)
activity.openToBrowser(
from = BrowserDirection.FromSearch
)
}
override fun handleExistingSessionSelected(tabId: String) {
val session = context.components.core.sessionManager.findSessionById(tabId)
val session = activity.components.core.sessionManager.findSessionById(tabId)
if (session != null) {
handleExistingSessionSelected(session)
}
......
......@@ -81,11 +81,10 @@ class SearchFragment : Fragment(), UserInteractionHandler {
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val args = arguments?.let { navArgs<SearchFragmentArgs>().value }
val session = args?.sessionId
val activity = activity as HomeActivity
val args by navArgs<SearchFragmentArgs>()
val session = args.sessionId
?.let(requireComponents.core.sessionManager::findSessionById)
val pastedText = args?.pastedText
val searchAccessPoint = args?.searchAccessPoint
val view = inflater.inflate(R.layout.fragment_search, container, false)
val url = session?.url.orEmpty()
......@@ -93,7 +92,7 @@ class SearchFragment : Fragment(), UserInteractionHandler {
requireComponents.search.provider.getDefaultEngine(requireContext())
)
val isPrivate = (activity as HomeActivity).browsingModeManager.mode.isPrivate
val isPrivate = activity.browsingModeManager.mode.isPrivate
requireComponents.analytics.metrics.track(Event.InteractWithSearchURLArea)
......@@ -110,14 +109,14 @@ class SearchFragment : Fragment(), UserInteractionHandler {
showHistorySuggestions = requireContext().settings().shouldShowHistorySuggestions,
showBookmarkSuggestions = requireContext().settings().shouldShowBookmarkSuggestions,
session = session,
pastedText = pastedText,
searchAccessPoint = searchAccessPoint
pastedText = args.pastedText,
searchAccessPoint = args.searchAccessPoint
)
)
}
val searchController = DefaultSearchController(
context = activity as HomeActivity,
activity = activity,
store = searchStore,
navController = findNavController(),
viewLifecycleScope = viewLifecycleOwner.lifecycleScope,
......
......@@ -263,7 +263,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
resources.getString(R.string.pref_key_privacy_link) -> {
val intent = SupportUtils.createCustomTabIntent(
requireContext(),
SupportUtils.getPrivacyNoticeUrl()
SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.PRIVATE_NOTICE)
)
startActivity(intent)
null
......
......@@ -43,6 +43,11 @@ object SupportUtils {
SYNC_SETUP("how-set-firefox-sync-firefox-preview")
}
enum class MozillaPage(internal val path: String) {
PRIVATE_NOTICE("privacy/firefox/"),
MANIFESTO("about/manifesto/")
}
/**
* Gets a support page URL for the corresponding topic.
*/
......@@ -69,8 +74,11 @@ object SupportUtils {
return "https://support.mozilla.org/$langTag/kb/$escapedTopic"
}
fun getPrivacyNoticeUrl(locale: Locale = Locale.getDefault()) =
"https://www.mozilla.org/${getLanguageTag(locale)}/privacy/firefox/"
fun getMozillaPageUrl(page: MozillaPage, locale: Locale = Locale.getDefault()): String {
val path = page.path
val langTag = getLanguageTag(locale)
return "https://www.mozilla.org/$langTag/$path"
}
fun getWhatsNewUrl(context: Context) = if (Config.channel.isFennec) {
getGenericSumoURLForTopic(SumoTopic.UPGRADE_FAQ)
......
......@@ -160,7 +160,7 @@ class AboutFragment : Fragment(), AboutPageListener {
AboutPageItem.Item(
AboutItem.ExternalLink(
PRIVACY_NOTICE,
SupportUtils.getPrivacyNoticeUrl()
SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.PRIVATE_NOTICE)
), getString(R.string.about_privacy_notice)
),
AboutPageItem.Item(
......
......@@ -7,7 +7,6 @@ package org.mozilla.fenix.search
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.navigation.NavController
import androidx.navigation.NavDirections
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
......@@ -32,7 +31,9 @@ import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.navigateSafe
import org.mozilla.fenix.ext.searchEngineManager
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.search.DefaultSearchController.Companion.KEYBOARD_ANIMATION_DELAY
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.utils.Settings
import org.mozilla.fenix.whatsnew.clear
......@@ -40,7 +41,7 @@ import org.mozilla.fenix.whatsnew.clear
@RunWith(FenixRobolectricTestRunner::class)
class DefaultSearchControllerTest {
private val context: HomeActivity = mockk(relaxed = true)
private val activity: HomeActivity = mockk(relaxed = true)
private val store: SearchFragmentStore = mockk(relaxed = true)
private val navController: NavController = mockk(relaxed = true)
private val defaultSearchEngine: SearchEngine? = mockk(relaxed = true)
......@@ -56,14 +57,14 @@ class DefaultSearchControllerTest {
@Before
fun setUp() {
every { context.searchEngineManager.defaultSearchEngine } returns defaultSearchEngine
every { activity.searchEngineManager.defaultSearchEngine } returns defaultSearchEngine
every { store.state.session } returns session
every { store.state.searchEngineSource.searchEngine } returns searchEngine
every { context.metrics } returns metrics
every { context.components.core.sessionManager } returns sessionManager
every { activity.metrics } returns metrics
every { activity.components.core.sessionManager } returns sessionManager
controller = DefaultSearchController(
context = context,
activity = activity,
store = store,
navController = navController,
viewLifecycleScope = lifecycleScope,
......@@ -80,7 +81,7 @@ class DefaultSearchControllerTest {
controller.handleUrlCommitted(url)
verify {
context.openToBrowserAndLoad(
activity.openToBrowserAndLoad(
searchTermOrURL = url,
newTab = session == null,
from = BrowserDirection.FromSearch,
......@@ -90,10 +91,36 @@ class DefaultSearchControllerTest {
verify { metrics.track(Event.EnteredUrl(false)) }
}
@Test
fun handleCrashesUrlCommitted() {
val url = "about:crashes"
controller.handleUrlCommitted(url)
verify { activity.startActivity(any()) }
}
@Test
fun handleMozillaUrlCommitted() {
val url = "moz://a"
controller.handleUrlCommitted(url)
verify {
activity.openToBrowserAndLoad(
searchTermOrURL = SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.MANIFESTO),
newTab = session == null,
from = BrowserDirection.FromSearch,
engine = searchEngine
)
}
verify { metrics.track(Event.EnteredUrl(false)) }
}
@Test
fun handleEditingCancelled() = runBlockingTest {
controller = DefaultSearchController(
context = context,
activity = activity,
store = store,
navController = navController,
viewLifecycleScope = this,
......@@ -197,7 +224,7 @@ class DefaultSearchControllerTest {
controller.handleUrlTapped(url)
verify {
context.openToBrowserAndLoad(
activity.openToBrowserAndLoad(
searchTermOrURL = url,
newTab = session == null,
from = BrowserDirection.FromSearch
......@@ -213,7 +240,7 @@ class DefaultSearchControllerTest {
controller.handleSearchTermsTapped(searchTerms)
verify {
context.openToBrowserAndLoad(
activity.openToBrowserAndLoad(
searchTermOrURL = searchTerms,
newTab = session == null,
from = BrowserDirection.FromSearch,
......@@ -268,6 +295,6 @@ class DefaultSearchControllerTest {
controller.handleExistingSessionSelected(session)
verify { sessionManager.select(session) }
verify { context.openToBrowser(from = BrowserDirection.FromSearch) }
verify { activity.openToBrowser(from = BrowserDirection.FromSearch) }
}
}
......@@ -44,14 +44,14 @@ class SupportUtilsTest {
}
@Test
fun getPrivacyNoticeUrl() {
fun getMozillaPageUrl() {
assertEquals(
"https://www.mozilla.org/en-CA/privacy/firefox/",
SupportUtils.getPrivacyNoticeUrl(Locale("en", "CA"))
"https://www.mozilla.org/en-US/about/manifesto/",
SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.MANIFESTO, Locale("en", "US"))
)
assertEquals(
"https://www.mozilla.org/zh/privacy/firefox/",
SupportUtils.getPrivacyNoticeUrl(Locale("zh"))
SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.PRIVATE_NOTICE, Locale("zh"))
)
}
......
Markdown is supported
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