tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 39906288368f00a147435a3a592b5bae05adb7d1
parent c443284550de11f767a767fc43c02263aa525bae
Author: agoloman <agoloman@mozilla.com>
Date:   Wed, 22 Oct 2025 21:41:48 +0300

Revert (Bug 1995625, Bug 1990028, Bug 1990029, Bug 1990024, Bug 1989460) - for causing fenix failures @SettingsSearchMiddlewareTest.kt. This reverts commit 329afcb5bcc65fae8c97c1198667b3c9065a6df7.

Revert "Bug 1990028 - Part 2: get NavigationDirections from SettingsSearchItem r=android-reviewers,petru"

This reverts commit 09aa349f363df14656cc29b2bc17582232440e3b.

Revert "Bug 1990028 - Part 1: Changes to settings fragments to enable scrollToPreference in navigation r=android-reviewers,petru"

This reverts commit 2dc2ec687d843eb8b0bf6a69c94e7a67c770d88e.

Revert "Bug 1990029 - Indexing of Fenix preference xml pages r=android-reviewers,petru"

This reverts commit 57429b92e714cb1c8e40f1aa0b67e9dffd9285c3.

Revert "Bug 1990024 - Create FenixSettingsIndexer r=android-reviewers,petru"

This reverts commit b4d447c4b6285c7e27ea52cc842546d9a3c9bc2e.

Revert "Bug 1989460 - Create SettingsSearchResultItem UI r=android-reviewers,android-l10n-reviewers,delphine,petru"

This reverts commit 76191f008c2a5f5d0c67681cc973274164fbbce8.

Diffstat:
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/Components.kt | 5-----
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/AccessibilityFragment.kt | 8--------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt | 5-----
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/DownloadsSettingsFragment.kt | 6------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/HomeSettingsFragment.kt | 6------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/OpenLinksInAppsFragment.kt | 5-----
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt | 6+-----
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SponsoredStoriesSettingsFragment.kt | 9---------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt | 5-----
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/TrackingProtectionFragment.kt | 6------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/account/AccountSettingsFragment.kt | 5-----
Dmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/DefaultFenixSettingsIndexer.kt | 335-------------------------------------------------------------------------------
Dmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/PreferenceFileInformation.kt | 138-------------------------------------------------------------------------------
Dmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsIndexer.kt | 23-----------------------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchAction.kt | 7-------
Dmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchBar.kt | 106-------------------------------------------------------------------------------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchFragment.kt | 10++++++----
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchItem.kt | 6------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchMiddleware.kt | 45++++-----------------------------------------
Dmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchResultItem.kt | 100-------------------------------------------------------------------------------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchScreen.kt | 160+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchStore.kt | 11+++--------
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SiteSettingsFragment.kt | 6------
Mmobile/android/fenix/app/src/main/res/navigation/nav_graph.xml | 78++++++------------------------------------------------------------------------
Mmobile/android/fenix/app/src/main/res/values/static_strings.xml | 3---
Mmobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchMiddlewareTest.kt | 72++----------------------------------------------------------------------
26 files changed, 118 insertions(+), 1048 deletions(-)

diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/Components.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/Components.kt @@ -71,7 +71,6 @@ import org.mozilla.fenix.perf.StartupStateProvider import org.mozilla.fenix.perf.StrictModeManager import org.mozilla.fenix.perf.lazyMonitored import org.mozilla.fenix.reviewprompt.ReviewPromptMiddleware -import org.mozilla.fenix.settings.settingssearch.DefaultFenixSettingsIndexer import org.mozilla.fenix.termsofuse.TermsOfUseManager import org.mozilla.fenix.utils.Settings import org.mozilla.fenix.utils.isLargeScreenSize @@ -350,10 +349,6 @@ class Components(private val context: Context) { val termsOfUseManager by lazyMonitored { TermsOfUseManager(settings) } - - val settingsIndexer by lazyMonitored { - DefaultFenixSettingsIndexer(context) - } } /** diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/AccessibilityFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/AccessibilityFragment.kt @@ -5,7 +5,6 @@ package org.mozilla.fenix.settings import android.os.Bundle -import androidx.navigation.fragment.navArgs import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference import org.mozilla.fenix.R @@ -20,9 +19,6 @@ import org.mozilla.fenix.ext.showToolbar * When turned off, the font sizing can be controlled manually within the app. */ class AccessibilityFragment : PreferenceFragmentCompat() { - - private val args by navArgs<AccessibilityFragmentArgs>() - override fun onResume() { super.onResume() showToolbar(getString(R.string.preferences_accessibility)) @@ -85,10 +81,6 @@ class AccessibilityFragment : PreferenceFragmentCompat() { components.useCases.sessionUseCases.reload() true } - - args.preferenceToScrollTo?.let { - scrollToPreference(it) - } } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt @@ -8,7 +8,6 @@ import android.os.Build import android.os.Build.VERSION.SDK_INT import android.os.Bundle import androidx.appcompat.app.AppCompatDelegate -import androidx.navigation.fragment.navArgs import androidx.preference.Preference import androidx.preference.PreferenceCategory import androidx.preference.PreferenceFragmentCompat @@ -39,7 +38,6 @@ class CustomizationFragment : PreferenceFragmentCompat() { private lateinit var radioDarkTheme: RadioButtonPreference private lateinit var radioAutoBatteryTheme: RadioButtonPreference private lateinit var radioFollowDeviceTheme: RadioButtonPreference - private val args by navArgs<CustomizationFragmentArgs>() override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.customization_preferences, rootKey) @@ -50,9 +48,6 @@ class CustomizationFragment : PreferenceFragmentCompat() { override fun onResume() { super.onResume() showToolbar(getString(R.string.preferences_customize)) - args.preferenceToScrollTo?.let { - scrollToPreference(it) - } } private fun setupPreferences() { diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/DownloadsSettingsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/DownloadsSettingsFragment.kt @@ -5,7 +5,6 @@ package org.mozilla.fenix.settings import android.os.Bundle -import androidx.navigation.fragment.navArgs import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference import org.mozilla.fenix.R @@ -15,8 +14,6 @@ import org.mozilla.fenix.ext.showToolbar * A [PreferenceFragmentCompat] that displays settings related to downloads. */ class DownloadsSettingsFragment : PreferenceFragmentCompat() { - private val args by navArgs<DownloadsSettingsFragmentArgs>() - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.downloads_settings_preferences, rootKey) requirePreference<SwitchPreference>(R.string.pref_key_downloads_clean_up_files_automatically).apply { @@ -30,8 +27,5 @@ class DownloadsSettingsFragment : PreferenceFragmentCompat() { override fun onResume() { super.onResume() showToolbar(getString(R.string.preferences_downloads)) - args.preferenceToScrollTo?.let { - scrollToPreference(it) - } } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/HomeSettingsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/HomeSettingsFragment.kt @@ -6,7 +6,6 @@ package org.mozilla.fenix.settings import android.os.Bundle import androidx.navigation.findNavController -import androidx.navigation.fragment.navArgs import androidx.preference.CheckBoxPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat @@ -26,8 +25,6 @@ import org.mozilla.fenix.utils.view.addToRadioGroup */ class HomeSettingsFragment : PreferenceFragmentCompat() { - private val args by navArgs<HomeSettingsFragmentArgs>() - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.home_preferences, rootKey) setupPreferences() @@ -36,9 +33,6 @@ class HomeSettingsFragment : PreferenceFragmentCompat() { override fun onResume() { super.onResume() showToolbar(getString(R.string.preferences_home_2)) - args.preferenceToScrollTo?.let { - scrollToPreference(it) - } } private fun setupPreferences() { diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/OpenLinksInAppsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/OpenLinksInAppsFragment.kt @@ -5,7 +5,6 @@ package org.mozilla.fenix.settings import android.os.Bundle -import androidx.navigation.fragment.navArgs import androidx.preference.PreferenceFragmentCompat import org.mozilla.fenix.R import org.mozilla.fenix.browser.browsingmode.BrowsingMode @@ -20,7 +19,6 @@ class OpenLinksInAppsFragment : PreferenceFragmentCompat() { private lateinit var radioAlways: RadioButtonPreference private lateinit var radioAskBeforeOpening: RadioButtonPreference private lateinit var radioNever: RadioButtonPreference - private val args by navArgs<OpenLinksInAppsFragmentArgs>() override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.open_links_in_apps_preferences, rootKey) @@ -38,9 +36,6 @@ class OpenLinksInAppsFragment : PreferenceFragmentCompat() { showToolbar(getString(R.string.preferences_open_links_in_apps)) setupPreferences() - args.preferenceToScrollTo?.let { - scrollToPreference(it) - } } private fun setupPreferences() { diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt @@ -217,11 +217,7 @@ class SettingsFragment : PreferenceFragmentCompat() { val title = nimbusValidation.settingsTitle val suffix = nimbusValidation.settingsPunctuation val toolbarTitle = "$title$suffix" - - val showSearch = requireContext().settings().isSettingsSearchEnabled && - (!args.searchInProgress) - - if (showSearch) { + if (requireContext().settings().isSettingsSearchEnabled) { showToolbarWithIconButton( title = toolbarTitle, iconResId = R.drawable.ic_search, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SponsoredStoriesSettingsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SponsoredStoriesSettingsFragment.kt @@ -5,7 +5,6 @@ package org.mozilla.fenix.settings import android.os.Bundle -import androidx.navigation.fragment.navArgs import androidx.preference.EditTextPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat @@ -18,7 +17,6 @@ import org.mozilla.fenix.ext.settings * Allows customizing sponsored stories fetch parameters. */ class SponsoredStoriesSettingsFragment : PreferenceFragmentCompat() { - private val args by navArgs<SponsoredStoriesSettingsFragmentArgs>() override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.sponsored_stories_settings, rootKey) @@ -53,11 +51,4 @@ class SponsoredStoriesSettingsFragment : PreferenceFragmentCompat() { } } } - - override fun onResume() { - super.onResume() - args.preferenceToScrollTo?.let { - scrollToPreference(it) - } - } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/TabsSettingsFragment.kt @@ -6,7 +6,6 @@ package org.mozilla.fenix.settings import android.os.Bundle import android.view.View -import androidx.navigation.fragment.navArgs import androidx.preference.PreferenceCategory import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference @@ -30,7 +29,6 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { private lateinit var radioOneMonth: RadioButtonPreference private lateinit var inactiveTabsCategory: PreferenceCategory private lateinit var inactiveTabs: SwitchPreference - private val args by navArgs<TabsSettingsFragmentArgs>() override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.tabs_preferences, rootKey) @@ -46,9 +44,6 @@ class TabsSettingsFragment : PreferenceFragmentCompat() { showToolbar(getString(R.string.preferences_tabs)) setupPreferences() - args.preferenceToScrollTo?.let { - scrollToPreference(it) - } } private fun setupPreferences() { diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/TrackingProtectionFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/TrackingProtectionFragment.kt @@ -9,7 +9,6 @@ import android.os.Bundle import androidx.annotation.VisibleForTesting import androidx.appcompat.app.AlertDialog import androidx.navigation.findNavController -import androidx.navigation.fragment.navArgs import androidx.preference.CheckBoxPreference import androidx.preference.DropDownPreference import androidx.preference.Preference @@ -32,7 +31,6 @@ import org.mozilla.fenix.utils.view.addToRadioGroup * to open info about the tracking protection [org.mozilla.fenix.settings.TrackingProtectionFragment]. */ class TrackingProtectionFragment : PreferenceFragmentCompat() { - private val args by navArgs<TrackingProtectionFragmentArgs>() private val exceptionsClickListener = Preference.OnPreferenceClickListener { val directions = @@ -157,10 +155,6 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() { } } } - - args.preferenceToScrollTo?.let { - scrollToPreference(it) - } } private fun bindTrackingProtectionRadio( diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/account/AccountSettingsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/account/AccountSettingsFragment.kt @@ -16,7 +16,6 @@ import android.view.View import androidx.core.content.edit import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs import androidx.preference.CheckBoxPreference import androidx.preference.EditTextPreference import androidx.preference.Preference @@ -61,7 +60,6 @@ class AccountSettingsFragment : PreferenceFragmentCompat() { private lateinit var accountManager: FxaAccountManager private lateinit var accountSettingsStore: AccountSettingsFragmentStore private lateinit var accountSettingsInteractor: AccountSettingsInteractor - private val args by navArgs<AccountSettingsFragmentArgs>() // Navigate away from this fragment when we encounter auth problems or logout events. private val accountStateObserver = object : AccountObserver { @@ -87,9 +85,6 @@ class AccountSettingsFragment : PreferenceFragmentCompat() { override fun onResume() { super.onResume() showToolbar(getString(R.string.preferences_account_settings)) - args.preferenceToScrollTo?.let { - scrollToPreference(it) - } } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/DefaultFenixSettingsIndexer.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/DefaultFenixSettingsIndexer.kt @@ -1,335 +0,0 @@ -/* 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.settings.settingssearch - -import android.content.Context -import android.content.res.Resources -import android.content.res.XmlResourceParser -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import java.io.IOException - -/** - * Indexes Settings preferences for the Settings Search screen. - * - * All the preference files that are parsed and indexed are listed in the companion object. - */ -class DefaultFenixSettingsIndexer(private val context: Context) : SettingsIndexer { - private val settings: MutableList<SettingsSearchItem> = mutableListOf() - private val breadcrumbs: MutableList<String> = mutableListOf() - - /** - * Index all settings. - */ - override fun indexAllSettings() { - settings.clear() - - for (preferenceFileInformation in preferenceFileInformationList) { - breadcrumbs.clear() - val settingFileParser = getXmlParserForFile(preferenceFileInformation.xmlResourceId) - breadcrumbs.add(context.getString(preferenceFileInformation.topBreadcrumbResourceId)) - if (settingFileParser != null) { - parseXmlFile(settingFileParser, preferenceFileInformation) - } - } - } - - /** - * Get settings filtered by query. - * - * @param query Query [String] to filter by. - * @return List of [SettingsSearchItem]s that match the query. - */ - override suspend fun getSettingsWithQuery(query: String): List<SettingsSearchItem> { - if (query.isBlank()) return emptyList() - - val trimmedQuery = query.trim() - - return withContext(Dispatchers.Default) { - settings.distinctBy { it.preferenceKey }.filter { item -> - item.title.contains(trimmedQuery, ignoreCase = true) || - item.summary.contains(trimmedQuery, ignoreCase = true) - } - } - } - - private fun getXmlParserForFile(xmlResourceId: Int): XmlResourceParser? { - try { - if (xmlResourceId == 0) return null - return context.resources.getXml(xmlResourceId) - } catch (e: Resources.NotFoundException) { - println("Error: failed to find resource $xmlResourceId. ${e.message}") - } catch (e: IOException) { - println("Error: I/O exception while parsing ${e.message}") - } - return null - } - - @Suppress("NestedBlockDepth") - private fun parseXmlFile( - parser: XmlResourceParser, - preferenceFileInformation: PreferenceFileInformation, - ) { - try { - var eventType = parser.next() - var categoryItem: SettingsSearchItem? = null - var categoryItemAdded = false - - while (eventType != XmlResourceParser.END_DOCUMENT) { - when (eventType) { - XmlResourceParser.START_TAG -> { - when (parser.name) { - PREFERENCE_CATEGORY_TAG -> { - addCategoryToBreadcrumbs(parser) - categoryItem = createCategoryItem( - parser, - preferenceFileInformation, - ) - } - CHECKBOX_PREFERENCE_TAG, - CUSTOM_CBH_SWITCH_PREFERENCE_TAG, - DEFAULT_BROWSER_PREFERENCE_TAG, - PREFERENCE_TAG, - SWITCH_PREFERENCE_TAG, - TEXT_PERCENTAGE_SEEK_BAR_PREFERENCE_TAG, - TOGGLE_RADIO_BUTTON_PREFERENCE_TAG, - -> { - val item = createSettingsSearchItemFromAttributes( - parser = parser, - preferenceFileInformation = preferenceFileInformation, - ) - if (item != null) { - settings.add(item) - } - } - RADIO_BUTTON_PREFERENCE_TAG, - SWITCH_PREFERENCE_PLAIN_TAG, - -> { - if (categoryItem != null && !categoryItemAdded) { - categoryItemAdded = true - val preferenceKey = getPreferenceKeyForRadioButtonPref(parser) - settings.add(categoryItem.copy(preferenceKey = preferenceKey ?: "")) - categoryItem = null - } else { - val item = createSettingsSearchItemFromAttributes( - parser, - preferenceFileInformation, - ) - if (item != null) { - settings.add(item) - } - } - } - } - } - XmlResourceParser.END_TAG -> { - when (parser.name) { - PREFERENCE_CATEGORY_TAG -> { - categoryItem = null - categoryItemAdded = false - if (breadcrumbs.isNotEmpty()) { - breadcrumbs.removeLastOrNull() - } - } - } - } - } - eventType = parser.next() - } - } catch (e: IOException) { - println("Error: I/O exception while parsing ${e.message}") - } finally { - parser.close() - } - } - - private fun addCategoryToBreadcrumbs( - parser: XmlResourceParser, - ) { - for (i in 0 until parser.attributeCount) { - val attributeName = parser.getAttributeName(i) - val attributeValue = parser.getAttributeValue(i) - - when (attributeName) { - TITLE_ATTRIBUTE_NAME -> { - val categoryName = getStringResource(attributeValue.substring(1)) - if (categoryName.isNotBlank()) { - breadcrumbs.add(categoryName) - } - } - } - } - } - - private fun createSettingsSearchItemFromAttributes( - parser: XmlResourceParser, - preferenceFileInformation: PreferenceFileInformation, - ): SettingsSearchItem? { - var key: String? = null - var title: String? = null - var summary = "" - - for (i in 0 until parser.attributeCount) { - val attributeName = parser.getAttributeName(i) - val attributeValue = parser.getAttributeValue(i) - - when (attributeName) { - KEY_ATTRIBUTE_NAME -> { - key = attributeValue.takeIf { it.isNotBlank() } - ?.substring(1) - ?.let { getStringResource(it) } - } - TITLE_ATTRIBUTE_NAME -> { - title = attributeValue.takeIf { it.isNotBlank() } - ?.substring(1) - ?.let { getStringResource(it) } - } - SUMMARY_ATTRIBUTE_NAME -> { - summary = attributeValue.takeIf { it.isNotBlank() } - ?.substring(1) - ?.let { getStringResource(it) } - ?: "" - } - IS_VISIBLE_ATTRIBUTE_NAME -> { - if (attributeValue == "false") { - return null - } - } - } - } - - if (key == null || title == null) return null - - return SettingsSearchItem( - preferenceKey = key, - title = title, - summary = summary, - breadcrumbs = breadcrumbs.toList(), - preferenceFileInformation = preferenceFileInformation, - ) - } - - /** - * Create a category item in case the category contains only radio buttons. - * - * The category item will be the reference for searching and the first radio button - * in the category will be used for navigation. - * - * @param parser [XmlResourceParser] for the category. - * @param preferenceFileInformation [PreferenceFileInformation] for the category. - */ - private fun createCategoryItem( - parser: XmlResourceParser, - preferenceFileInformation: PreferenceFileInformation, - ): SettingsSearchItem? { - var key: String? = null - var title: String? = null - var summary = "" - - for (i in 0 until parser.attributeCount) { - val attributeName = parser.getAttributeName(i) - val attributeValue = parser.getAttributeValue(i) - - when (attributeName) { - KEY_ATTRIBUTE_NAME -> key = getStringResource(attributeValue.substring(1)) - TITLE_ATTRIBUTE_NAME -> title = getStringResource(attributeValue.substring(1)) - SUMMARY_ATTRIBUTE_NAME -> summary = getStringResource(attributeValue.substring(1)) - IS_VISIBLE_ATTRIBUTE_NAME, IS_ENABLED_ATTRIBUTE_NAME -> { - if (attributeValue == "false") { - return null - } - } - } - } - - return SettingsSearchItem( - preferenceKey = key ?: "", - title = title ?: "", - summary = summary, - breadcrumbs = breadcrumbs.toList(), - preferenceFileInformation = preferenceFileInformation, - ) - } - - /** - * Get the preference key for a radio button preference. - * - * @param parser [XmlResourceParser] for the radio button preference. - */ - private fun getPreferenceKeyForRadioButtonPref(parser: XmlResourceParser): String? { - var key: String? = null - for (i in 0 until parser.attributeCount) { - val attributeName = parser.getAttributeName(i) - val attributeValue = parser.getAttributeValue(i) - - when (attributeName) { - KEY_ATTRIBUTE_NAME -> key = getStringResource(attributeValue.substring(1)) - } - } - return key - } - - /** - * Get the string resource from the given resource name. - * Uses the locale context. - * - * @param resourceName The name of the resource. - */ - private fun getStringResource(resourceName: String): String { - return try { - val resourceId = context.resources.getIdentifier( - resourceName, "string", context.packageName, - ) - if (resourceId != 0) { - context.getString(resourceId) - } else { - "" - } - } catch (e: Resources.NotFoundException) { - "" - } - } - - companion object { - // Attribute names - private const val PREFERENCE_CATEGORY_TAG = "androidx.preference.PreferenceCategory" - private const val CHECKBOX_PREFERENCE_TAG = "androidx.preference.CheckBoxPreference" - private const val PREFERENCE_TAG = "androidx.preference.Preference" - private const val SWITCH_PREFERENCE_TAG = "androidx.preference.SwitchPreference" - private const val SWITCH_PREFERENCE_PLAIN_TAG = "SwitchPreference" - private const val CUSTOM_CBH_SWITCH_PREFERENCE_TAG = - "org.mozilla.fenix.settings.cookiebannerhandling.CustomCBHSwitchPreference" - private const val DEFAULT_BROWSER_PREFERENCE_TAG = "org.mozilla.fenix.settings.DefaultBrowserPreference" - private const val TEXT_PERCENTAGE_SEEK_BAR_PREFERENCE_TAG = - "org.mozilla.fenix.settings.TextPercentageSeekBarPreference" - private const val RADIO_BUTTON_PREFERENCE_TAG = "org.mozilla.fenix.settings.RadioButtonPreference" - private const val TOGGLE_RADIO_BUTTON_PREFERENCE_TAG = "org.mozilla.fenix.settings.ToggleRadioButtonPreference" - private const val KEY_ATTRIBUTE_NAME = "key" - private const val TITLE_ATTRIBUTE_NAME = "title" - private const val SUMMARY_ATTRIBUTE_NAME = "summary" - private const val IS_VISIBLE_ATTRIBUTE_NAME = "isPreferenceVisible" - private const val IS_ENABLED_ATTRIBUTE_NAME = "enabled" - - /** - * All the preference xml files to load with information for the indexer. - * In a [List] of [PreferenceFileInformation]s. - */ - val preferenceFileInformationList = listOf( - PreferenceFileInformation.GeneralPreferences, - PreferenceFileInformation.AccessibilityPreferences, - PreferenceFileInformation.AutofillPreferences, - PreferenceFileInformation.CustomizationPreferences, - PreferenceFileInformation.DefaultSearchEnginePreferences, - PreferenceFileInformation.DownloadsSettingsPreferences, - PreferenceFileInformation.HomePreferences, - PreferenceFileInformation.OpenLinksInAppsPreferences, - PreferenceFileInformation.PrivateBrowsingPreferences, - PreferenceFileInformation.SearchSettingsPreferences, - PreferenceFileInformation.TabsPreferences, - PreferenceFileInformation.TrackingProtectionPreferences, - PreferenceFileInformation.SaveLoginsPreferences, - ) - } -} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/PreferenceFileInformation.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/PreferenceFileInformation.kt @@ -1,138 +0,0 @@ -/* 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.settings.settingssearch - -import org.mozilla.fenix.R - -/** - * Data class for a settings search item navigation information based on the xml file it comes from. - * - * @property xmlResourceId The resource ID of the xml file that the item comes from. - * @property topBreadcrumbResourceId The top breadcrumb of the item as a string resource. - * @property fragmentId The fragment ID of the fragment where the item is displayed. - */ -sealed class PreferenceFileInformation( - val xmlResourceId: Int, - val topBreadcrumbResourceId: Int, - val fragmentId: Int, -) { - - /** - * Represents the main "General" settings screen. - */ - object GeneralPreferences : PreferenceFileInformation( - xmlResourceId = R.xml.preferences, - topBreadcrumbResourceId = R.string.settings_title, - fragmentId = R.id.settingsFragment, - ) - - /** - * Represents the "Accessibility" settings screen. - */ - object AccessibilityPreferences : PreferenceFileInformation( - xmlResourceId = R.xml.accessibility_preferences, - topBreadcrumbResourceId = R.string.preferences_accessibility, - fragmentId = R.id.accessibilityFragment, - ) - - /** - * Represents the "Autofill" settings screen. - */ - object AutofillPreferences : PreferenceFileInformation( - xmlResourceId = R.xml.autofill_preferences, - topBreadcrumbResourceId = R.string.preferences_autofill, - fragmentId = R.id.autofill_graph, - ) - - /** - * Represents the "Customization" settings screen. - */ - object CustomizationPreferences : PreferenceFileInformation( - xmlResourceId = R.xml.customization_preferences, - topBreadcrumbResourceId = R.string.preferences_customize, - fragmentId = R.id.customizationFragment, - ) - - /** - * Represents the "Default Search Engine" settings screen. - */ - object DefaultSearchEnginePreferences : PreferenceFileInformation( - xmlResourceId = R.xml.default_search_engine_preferences, - topBreadcrumbResourceId = R.string.preferences_default_search_engine, - fragmentId = R.id.search_engine_graph, - ) - - /** - * Represents the "Downloads" settings screen. - */ - object DownloadsSettingsPreferences : PreferenceFileInformation( - xmlResourceId = R.xml.downloads_settings_preferences, - topBreadcrumbResourceId = R.string.preferences_downloads, - fragmentId = R.id.downloadsFragment, - ) - - /** - * Represents the "Home" settings screen. - */ - object HomePreferences : PreferenceFileInformation( - xmlResourceId = R.xml.home_preferences, - topBreadcrumbResourceId = R.string.preferences_home_2, - fragmentId = R.id.homeSettingsFragment, - ) - - /** - * Represents the "Open Links in Apps" settings screen. - */ - object OpenLinksInAppsPreferences : PreferenceFileInformation( - xmlResourceId = R.xml.open_links_in_apps_preferences, - topBreadcrumbResourceId = R.string.preferences_open_links_in_apps, - fragmentId = R.id.openLinksInAppsFragment, - ) - - /** - * Represents the "Private Browsing" settings screen. - */ - object PrivateBrowsingPreferences : PreferenceFileInformation( - xmlResourceId = R.xml.private_browsing_preferences, - topBreadcrumbResourceId = R.string.preferences_private_browsing_options, - fragmentId = R.id.privateBrowsingFragment, - ) - - /** - * Represents the "Search Settings" settings screen. - */ - object SearchSettingsPreferences : PreferenceFileInformation( - xmlResourceId = R.xml.search_settings_preferences, - topBreadcrumbResourceId = R.string.preferences_search, - fragmentId = R.id.search_engine_graph, - ) - - /** - * Represents the "Tabs" settings screen. - */ - object TabsPreferences : PreferenceFileInformation( - xmlResourceId = R.xml.tabs_preferences, - topBreadcrumbResourceId = R.string.preferences_tabs, - fragmentId = R.id.tabsSettingsFragment, - ) - - /** - * Represents the "Tracking Protection" settings screen. - */ - object TrackingProtectionPreferences : PreferenceFileInformation( - xmlResourceId = R.xml.tracking_protection_preferences, - topBreadcrumbResourceId = R.string.preference_enhanced_tracking_protection, - fragmentId = R.id.trackingProtectionFragment, - ) - - /** - * Represents the "Save Logins" settings screen. - */ - object SaveLoginsPreferences : PreferenceFileInformation( - xmlResourceId = R.xml.save_logins_preferences, - topBreadcrumbResourceId = R.string.preferences_passwords_save_logins_2, - fragmentId = R.id.savedLogins, - ) -} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsIndexer.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsIndexer.kt @@ -1,23 +0,0 @@ -/* 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.settings.settingssearch - -/** - * This interface provides a static methods for indexing settings and querying the index. - */ -interface SettingsIndexer { - /** - * Indexes all settings in the app. - */ - fun indexAllSettings() - - /** - * Get all settings that match the query. - * - * @param query The query to search for. - * @return A list of [SettingsSearchItem]s that match the query. - */ - suspend fun getSettingsWithQuery(query: String): List<SettingsSearchItem> -} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchAction.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchAction.kt @@ -34,11 +34,4 @@ sealed interface SettingsSearchAction : Action { val query: String, val results: List<SettingsSearchItem>, ) : SettingsSearchAction - - /** - * User has clicked on a search result item. - * - * @property item [SettingsSearchItem] that was clicked. - */ - data class ResultItemClicked(val item: SettingsSearchItem) : SettingsSearchAction } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchBar.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchBar.kt @@ -1,106 +0,0 @@ -/* 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.settings.settingssearch - -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.TopAppBar -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import mozilla.components.compose.base.button.IconButton -import mozilla.components.compose.base.textfield.TextField -import mozilla.components.lib.state.ext.observeAsComposableState -import org.mozilla.fenix.R -import org.mozilla.fenix.theme.FirefoxTheme -import mozilla.components.ui.icons.R as iconsR - -/** - * Composable for the settings search bar. - * - * @param store [SettingsSearchStore] for the screen. - * @param onBackClick Invoked when the app bar's back button is clicked. - */ -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun SettingsSearchBar( - store: SettingsSearchStore, - onBackClick: () -> Unit, -) { - val state by store.observeAsComposableState { it } - var searchQuery by remember { mutableStateOf(state.searchQuery) } - - TopAppBar( - title = { - TextField( - value = searchQuery, - onValueChange = { value -> - searchQuery = value - store.dispatch(SettingsSearchAction.SearchQueryUpdated(value)) - }, - modifier = Modifier - .fillMaxWidth() - .padding(end = 8.dp), - placeholder = stringResource(R.string.settings_search_title), - singleLine = true, - errorText = stringResource(R.string.settings_search_error_message), - trailingIcons = { - when (state) { - is SettingsSearchState.SearchInProgress, - is SettingsSearchState.NoSearchResults, - -> { - IconButton( - onClick = { - searchQuery = "" - store.dispatch(SettingsSearchAction.SearchQueryUpdated("")) - }, - contentDescription = stringResource( - R.string.content_description_settings_search_clear_search, - ), - ) { - Icon( - painter = painterResource(iconsR.drawable.mozac_ic_cross_circle_fill_24), - contentDescription = null, - tint = FirefoxTheme.colors.textPrimary, - ) - } - } - else -> Unit - } - }, - ) - }, - navigationIcon = { - IconButton( - onClick = onBackClick, - contentDescription = - stringResource( - R.string.content_description_settings_search_navigate_back, - ), - ) { - Icon( - painter = painterResource( - iconsR.drawable.mozac_ic_back_24, - ), - contentDescription = null, - tint = FirefoxTheme.colors.textPrimary, - ) - } - }, - windowInsets = WindowInsets( - top = 0.dp, - bottom = 0.dp, - ), - ) -} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchFragment.kt @@ -16,7 +16,6 @@ import androidx.fragment.app.Fragment import androidx.fragment.compose.content import androidx.navigation.fragment.findNavController import org.mozilla.fenix.components.StoreProvider -import org.mozilla.fenix.ext.components import org.mozilla.fenix.theme.FirefoxTheme /** @@ -26,12 +25,16 @@ class SettingsSearchFragment : Fragment() { lateinit var settingsSearchStore: SettingsSearchStore + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + settingsSearchStore = buildSettingsSearchStore() + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View = content { - settingsSearchStore = buildSettingsSearchStore() (activity as? AppCompatActivity)?.supportActionBar?.hide() FirefoxTheme { SettingsSearchScreen( @@ -55,9 +58,8 @@ class SettingsSearchFragment : Fragment() { middleware = listOf( SettingsSearchMiddleware( SettingsSearchMiddleware.Companion.Dependencies( - navController = findNavController(), + context = requireContext(), ), - fenixSettingsIndexer = requireContext().components.settingsIndexer, ), ), ) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchItem.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchItem.kt @@ -14,14 +14,12 @@ package org.mozilla.fenix.settings.settingssearch * [0] is the section on the main Settings page * [1] is the subPage title, if any * [2] is the subPage section, if any - * @property preferenceFileInformation Preference file information [PreferenceFileInformation] of the settings item. */ data class SettingsSearchItem( val title: String, val summary: String, val preferenceKey: String, val breadcrumbs: List<String>, - val preferenceFileInformation: PreferenceFileInformation, ) { /** @@ -31,22 +29,18 @@ data class SettingsSearchItem( * @param summary New summary [String]. * @param preferenceKey New preference key [String]. * @param breadcrumbs New breadcrumbs [List] of [String]s. - * @param preferenceFileInformation New preference file information [PreferenceFileInformation]. - * @return New [SettingsSearchItem] with the given parameters replaced. */ fun copyWith( title: String? = null, summary: String? = null, preferenceKey: String? = null, breadcrumbs: List<String>? = null, - preferenceFileInformation: PreferenceFileInformation? = null, ): SettingsSearchItem { return SettingsSearchItem( title = title ?: this.title, summary = summary ?: this.summary, preferenceKey = preferenceKey ?: this.preferenceKey, breadcrumbs = breadcrumbs ?: this.breadcrumbs, - preferenceFileInformation = preferenceFileInformation ?: this.preferenceFileInformation, ) } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchMiddleware.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchMiddleware.kt @@ -4,74 +4,37 @@ package org.mozilla.fenix.settings.settingssearch -import androidx.core.os.bundleOf -import androidx.navigation.NavController -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch +import android.content.Context import mozilla.components.lib.state.Middleware import mozilla.components.lib.state.MiddlewareContext /** * [Middleware] for the settings search screen. * - * @param initialDependencies [Dependencies] to use for navigation. - * @property fenixSettingsIndexer [SettingsIndexer] to use for indexing and querying settings. + * @param initialDependencies [Dependencies] for the middleware. */ class SettingsSearchMiddleware( initialDependencies: Dependencies, - val fenixSettingsIndexer: SettingsIndexer, ) : Middleware<SettingsSearchState, SettingsSearchAction> { var dependencies = initialDependencies - init { - fenixSettingsIndexer.indexAllSettings() - } - override fun invoke( context: MiddlewareContext<SettingsSearchState, SettingsSearchAction>, next: (SettingsSearchAction) -> Unit, action: SettingsSearchAction, ) { - val store = context.store as SettingsSearchStore when (action) { is SettingsSearchAction.SearchQueryUpdated -> { next(action) - CoroutineScope(Dispatchers.Main).launch { - val results = fenixSettingsIndexer.getSettingsWithQuery(action.query) - if (results.isEmpty()) { - store.dispatch(SettingsSearchAction.NoResultsFound(action.query)) - } else { - store.dispatch( - SettingsSearchAction.SearchResultsLoaded( - query = action.query, - results = results, - ), - ) - } - } - } - is SettingsSearchAction.ResultItemClicked -> { - val searchItem = action.item - val bundle = bundleOf( - "preference_to_scroll_to" to searchItem.preferenceKey, - "search_in_progress" to true, - ) - val fragmentId = searchItem.preferenceFileInformation.fragmentId - CoroutineScope(Dispatchers.Main).launch { - dependencies.navController.navigate(fragmentId, bundle) - } } else -> { next(action) - // no op in middleware layer + // no op } } } companion object { - data class Dependencies( - val navController: NavController, - ) + data class Dependencies(val context: Context) } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchResultItem.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchResultItem.kt @@ -1,100 +0,0 @@ -/* 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.settings.settingssearch - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.PreviewLightDark -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import androidx.compose.ui.unit.dp -import org.mozilla.fenix.theme.FirefoxTheme - -/** - * Composable for the settings search result item. - * - * @param item [SettingsSearchItem] to display. - * @param onClick Callback for when the item is clicked. - */ -@Composable -fun SettingsSearchResultItem( - item: SettingsSearchItem, - onClick: () -> Unit, -) { - Column( - modifier = Modifier - .fillMaxWidth() - .clickable(onClick = onClick) - .padding(16.dp), - ) { - if (item.breadcrumbs.isNotEmpty()) { - Text( - text = item.breadcrumbs.joinToString(" > "), - style = FirefoxTheme.typography.caption, - color = FirefoxTheme.colors.textSecondary, - ) - Spacer(modifier = Modifier.height(8.dp)) - } - - Text( - text = item.title, - style = FirefoxTheme.typography.subtitle1, - color = FirefoxTheme.colors.textPrimary, - ) - if (item.summary.isNotBlank()) { - Text( - text = item.summary, - style = FirefoxTheme.typography.caption, - color = FirefoxTheme.colors.textSecondary, - modifier = Modifier.padding(top = 4.dp), - ) - } else { - Spacer(modifier = Modifier.height(8.dp)) - } - } -} - -private class SettingsSearchResultItemParameterProvider : PreviewParameterProvider<SettingsSearchItem> { - override val values: Sequence<SettingsSearchItem> - get() = sequenceOf( - SettingsSearchItem( - title = "Search Engine", - summary = "Set your preferred search engine for browsing.", - preferenceKey = "search_engine_main", - breadcrumbs = listOf("Search", "Default Search Engine"), - preferenceFileInformation = PreferenceFileInformation.SearchSettingsPreferences, - ), - SettingsSearchItem( - title = "Advanced Settings", - summary = "", // Empty or blank summary - preferenceKey = "advanced_stuff", - breadcrumbs = listOf("Developer", "Experiments"), - preferenceFileInformation = PreferenceFileInformation.GeneralPreferences, - ), - ) -} - -/** - * Preview for the Settings Search Result Item. - */ -@PreviewLightDark -@Composable -private fun SettingsSearchResultItemFullPreview( - @PreviewParameter(SettingsSearchResultItemParameterProvider::class) item: SettingsSearchItem, -) { - FirefoxTheme { - SettingsSearchResultItem( - item = item, - onClick = {}, - ) - } -} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchScreen.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchScreen.kt @@ -5,22 +5,30 @@ package org.mozilla.fenix.settings.settingssearch import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import mozilla.components.compose.base.button.IconButton +import mozilla.components.compose.base.textfield.TextField import mozilla.components.lib.state.ext.observeAsComposableState import org.mozilla.fenix.R import org.mozilla.fenix.theme.FirefoxTheme +import mozilla.components.ui.icons.R as iconsR /** * Composable for the settings search screen. @@ -37,79 +45,103 @@ fun SettingsSearchScreen( Scaffold( topBar = { SettingsSearchBar( - store = store, + query = state.searchQuery, + onSearchQueryChanged = { + store.dispatch(SettingsSearchAction.SearchQueryUpdated(it)) + }, + onClearSearchClicked = { + store.dispatch(SettingsSearchAction.SearchQueryUpdated("")) + }, onBackClick = onBackClick, ) }, ) { paddingValues -> - when (state) { - is SettingsSearchState.Default -> { - SettingsSearchMessageContent( - modifier = Modifier - .padding(paddingValues) - .fillMaxSize(), + Column( + modifier = Modifier.padding(paddingValues), + ) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Text( + text = stringResource(R.string.settings_search_empty_query_placeholder), + style = FirefoxTheme.typography.body2, + color = FirefoxTheme.colors.textSecondary, ) } - is SettingsSearchState.NoSearchResults -> { - SettingsSearchMessageContent( - modifier = Modifier - .padding(paddingValues) - .fillMaxSize(), - currentUserQuery = state.searchQuery, - ) - } - is SettingsSearchState.SearchInProgress -> { - LazyColumn( - modifier = Modifier - .padding(paddingValues) - .fillMaxSize(), - ) { - items(state.searchResults.size) { index -> - val settingsSearchItem = state.searchResults[index] - if (index != 0) { - HorizontalDivider() - } - SettingsSearchResultItem( - item = settingsSearchItem, - onClick = { - store.dispatch( - SettingsSearchAction.ResultItemClicked( - settingsSearchItem, - ), - ) - }, - ) - } - } - } } } } +/** + * Composable for the settings search bar. + * + * @param query Current search query [String]. + * @param onSearchQueryChanged Callback for when the search query changes. + * @param onClearSearchClicked Callback for when the clear search button is clicked. + * @param onBackClick Callback for when the back button is clicked. + */ +@OptIn(ExperimentalMaterial3Api::class) @Composable -private fun SettingsSearchMessageContent( - modifier: Modifier = Modifier, - currentUserQuery: String = "", +private fun SettingsSearchBar( + query: String, + onSearchQueryChanged: (String) -> Unit, + onClearSearchClicked: () -> Unit, + onBackClick: () -> Unit, ) { - val displayMessage = if (currentUserQuery.isBlank()) { - stringResource(R.string.settings_search_empty_query_placeholder) - } else { - stringResource( - R.string.setttings_search_no_results_found_message, - currentUserQuery, - ) - } - Box( - modifier = modifier, - contentAlignment = Alignment.Center, - ) { - Text( - text = displayMessage, - textAlign = TextAlign.Center, - style = FirefoxTheme.typography.body2, - color = FirefoxTheme.colors.textSecondary, - ) - } + TopAppBar( + title = { + TextField( + value = query, + onValueChange = { value -> + onSearchQueryChanged(value) + }, + modifier = Modifier + .fillMaxWidth() + .padding(end = 8.dp), + placeholder = stringResource(R.string.settings_search_title), + singleLine = true, + errorText = stringResource(R.string.settings_search_error_message), + trailingIcons = { + if (query.isNotBlank()) { + IconButton( + onClick = onClearSearchClicked, + contentDescription = stringResource( + R.string.content_description_settings_search_clear_search, + ), + ) { + Icon( + painter = painterResource(iconsR.drawable.mozac_ic_cross_24), + contentDescription = null, + tint = FirefoxTheme.colors.textPrimary, + ) + } + } + }, + ) + }, + navigationIcon = { + IconButton( + onClick = onBackClick, + contentDescription = + stringResource( + R.string.content_description_settings_search_navigate_back, + ), + ) { + Icon( + painter = painterResource( + iconsR.drawable.mozac_ic_back_24, + ), + contentDescription = null, + tint = FirefoxTheme.colors.textPrimary, + ) + } + }, + windowInsets = WindowInsets( + top = 0.dp, + bottom = 0.dp, + ), + ) } /** diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchStore.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchStore.kt @@ -39,13 +39,9 @@ private fun reduce(state: SettingsSearchState, action: SettingsSearchAction): Se } } is SettingsSearchAction.NoResultsFound -> { - if (action.query.isBlank()) { - SettingsSearchState.Default - } else { - SettingsSearchState.NoSearchResults( - searchQuery = action.query, - ) - } + SettingsSearchState.NoSearchResults( + searchQuery = action.query, + ) } is SettingsSearchAction.SearchResultsLoaded -> { SettingsSearchState.SearchInProgress( @@ -53,7 +49,6 @@ private fun reduce(state: SettingsSearchState, action: SettingsSearchAction): Se searchResults = action.results, ) } - is SettingsSearchAction.ResultItemClicked -> state } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SiteSettingsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SiteSettingsFragment.kt @@ -7,7 +7,6 @@ package org.mozilla.fenix.settings.sitepermissions import android.os.Bundle import androidx.core.content.ContextCompat import androidx.navigation.findNavController -import androidx.navigation.fragment.navArgs import androidx.preference.Preference import androidx.preference.Preference.OnPreferenceClickListener import androidx.preference.PreferenceFragmentCompat @@ -31,8 +30,6 @@ import org.mozilla.fenix.settings.requirePreference @SuppressWarnings("TooManyFunctions") class SiteSettingsFragment : PreferenceFragmentCompat() { - val args by navArgs<SiteSettingsFragmentArgs>() - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.site_permissions_preferences, rootKey) @@ -44,9 +41,6 @@ class SiteSettingsFragment : PreferenceFragmentCompat() { super.onResume() showToolbar(getString(R.string.preferences_site_settings)) setupPreferences() - args.preferenceToScrollTo?.let { - scrollToPreference(it) - } } private fun setupPreferences() { diff --git a/mobile/android/fenix/app/src/main/res/navigation/nav_graph.xml b/mobile/android/fenix/app/src/main/res/navigation/nav_graph.xml @@ -571,10 +571,6 @@ android:defaultValue="@null" app:argType="string" app:nullable="true" /> - <argument - android:name="search_in_progress" - android:defaultValue="false" - app:argType="boolean" /> <action android:id="@+id/action_settingsFragment_to_settingsSearchFragment" app:destination="@id/settingsSearchFragment" @@ -817,22 +813,11 @@ <fragment android:id="@+id/tabsSettingsFragment" android:name="org.mozilla.fenix.settings.TabsSettingsFragment" - android:label="@string/preferences_tabs" > - <argument - android:name="preference_to_scroll_to" - android:defaultValue="@null" - app:argType="string" - app:nullable="true" /> - </fragment> + android:label="@string/preferences_tabs" /> <fragment android:id="@+id/homeSettingsFragment" android:name="org.mozilla.fenix.settings.HomeSettingsFragment" android:label="@string/preferences_home_2"> - <argument - android:name="preference_to_scroll_to" - android:defaultValue="@null" - app:argType="string" - app:nullable="true" /> <action android:id="@+id/action_homeSettingsFragment_to_wallpaperSettingsFragment" app:destination="@id/wallpaperSettingsFragment" @@ -866,11 +851,6 @@ android:id="@+id/sitePermissionsFragment" android:name="org.mozilla.fenix.settings.sitepermissions.SiteSettingsFragment" android:label="@string/preferences_site_settings"> - <argument - android:name="preference_to_scroll_to" - android:defaultValue="@null" - app:argType="string" - app:nullable="true" /> <action android:id="@+id/action_site_permissions_to_manage_phone_features" app:destination="@id/SitePermissionsManagePhoneFeature" @@ -892,23 +872,11 @@ <fragment android:id="@+id/accessibilityFragment" android:name="org.mozilla.fenix.settings.AccessibilityFragment" - android:label="@string/preferences_accessibility" > - <argument - android:name="preference_to_scroll_to" - android:defaultValue="@null" - app:argType="string" - app:nullable="true" /> - </fragment> - + android:label="@string/preferences_accessibility" /> <fragment android:id="@+id/accountSettingsFragment" android:name="org.mozilla.fenix.settings.account.AccountSettingsFragment" android:label="@string/preferences_account_settings"> - <argument - android:name="preference_to_scroll_to" - android:defaultValue="@null" - app:argType="string" - app:nullable="true" /> <action android:id="@+id/action_accountSettingsFragment_to_signOutFragment" app:destination="@id/signOutFragment" /> @@ -980,11 +948,6 @@ android:id="@+id/customizationFragment" android:name="org.mozilla.fenix.settings.CustomizationFragment" android:label="@string/preferences_customize"> - <argument - android:name="preference_to_scroll_to" - android:defaultValue="@null" - app:argType="string" - app:nullable="true" /> <action android:id="@+id/action_customizationFragment_appIconSelectionFragment" app:destination="@+id/appIconSelectionFragment" @@ -996,13 +959,7 @@ <fragment android:id="@+id/privateBrowsingFragment" android:name="org.mozilla.fenix.settings.PrivateBrowsingFragment" - android:label="@string/preferences_private_browsing_options" > - <argument - android:name="preference_to_scroll_to" - android:defaultValue="@null" - app:argType="string" - app:nullable="true" /> - </fragment> + android:label="@string/preferences_private_browsing_options" /> <fragment android:id="@+id/httpsOnlyFragment" android:name="org.mozilla.fenix.settings.HttpsOnlyFragment" @@ -1010,21 +967,10 @@ <fragment android:id="@+id/sponsoredStoriesSettings" android:name="org.mozilla.fenix.settings.SponsoredStoriesSettingsFragment" - android:label="@string/preferences_debug_settings_custom_sponsored_stories_parameters" > - <argument - android:name="preference_to_scroll_to" - android:defaultValue="@null" - app:argType="string" - app:nullable="true" /> - </fragment> + android:label="@string/preferences_debug_settings_custom_sponsored_stories_parameters" /> <fragment android:id="@+id/trackingProtectionFragment" android:name="org.mozilla.fenix.settings.TrackingProtectionFragment"> - <argument - android:name="preference_to_scroll_to" - android:defaultValue="@null" - app:argType="string" - app:nullable="true" /> <action android:id="@+id/action_trackingProtectionFragment_to_exceptionsFragment" app:destination="@id/trackingProtectionExceptionsFragment" @@ -1042,22 +988,10 @@ </fragment> <fragment android:id="@+id/openLinksInAppsFragment" - android:name="org.mozilla.fenix.settings.OpenLinksInAppsFragment" > - <argument - android:name="preference_to_scroll_to" - android:defaultValue="@null" - app:argType="string" - app:nullable="true" /> - </fragment> + android:name="org.mozilla.fenix.settings.OpenLinksInAppsFragment" /> <fragment android:id="@+id/openDownloadsSettingsFragment" - android:name="org.mozilla.fenix.settings.DownloadsSettingsFragment"> - <argument - android:name="preference_to_scroll_to" - android:defaultValue="@null" - app:argType="string" - app:nullable="true" /> - </fragment> + android:name="org.mozilla.fenix.settings.DownloadsSettingsFragment" /> <fragment android:id="@+id/linkSharingFragment" android:name="org.mozilla.fenix.settings.LinkSharingFragment" /> diff --git a/mobile/android/fenix/app/src/main/res/values/static_strings.xml b/mobile/android/fenix/app/src/main/res/values/static_strings.xml @@ -274,7 +274,4 @@ <string name="settings_search_empty_query_placeholder">Search for settings</string> <!-- Message when error happens with Settings Search --> <string name="settings_search_error_message">Error occurred.</string> - <!-- Message displayed when the search field is not empty but search results is empty. - %s will be replaced by user query in search field. --> - <string name="setttings_search_no_results_found_message">No results found for %s</string> </resources> diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchMiddlewareTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchMiddlewareTest.kt @@ -27,39 +27,14 @@ class SettingsSearchMiddlewareTest { private fun buildMiddleware(): SettingsSearchMiddleware { return SettingsSearchMiddleware( - initialDependencies = SettingsSearchMiddleware.Companion.Dependencies( - context, - ), - fenixSettingsIndexer = TestSettingsIndexer(), - ) - } - - @Test - fun `WHEN the settings search query is updated and results are not found THEN the state is updated`() { - val middleware = SettingsSearchMiddleware( SettingsSearchMiddleware.Companion.Dependencies( context, ), - fenixSettingsIndexer = EmptyTestSettingsIndexer(), ) - val capture = CaptureActionsMiddleware<SettingsSearchState, SettingsSearchAction>() - val query = "test" - val store = SettingsSearchStore( - middleware = listOf( - middleware, - capture, - ), - ) - - store.dispatch(SettingsSearchAction.SearchQueryUpdated(query)) - store.waitUntilIdle() - - assert(store.state is SettingsSearchState.NoSearchResults) - assert(store.state.searchQuery == query) } @Test - fun `WHEN the settings search query is updated and results are found THEN the state is updated`() { + fun `WHEN the settings search query is updated and results are not found THEN the state is updated`() { val middleware = buildMiddleware() val capture = CaptureActionsMiddleware<SettingsSearchState, SettingsSearchAction>() val query = "test" @@ -72,51 +47,8 @@ class SettingsSearchMiddlewareTest { store.dispatch(SettingsSearchAction.SearchQueryUpdated(query)) store.waitUntilIdle() - capture.assertLastAction(SettingsSearchAction.SearchResultsLoaded::class) + assert(store.state is SettingsSearchState.SearchInProgress) assert(store.state.searchQuery == query) - assert(store.state.searchResults == testList) - } -} - -val testList = listOf( - SettingsSearchItem( - title = "Search Engine", - summary = "Set your preferred search engine for browsing.", - preferenceKey = "search_engine_main", - breadcrumbs = listOf("Search", "Default Search Engine"), - ), - SettingsSearchItem( - title = "Advanced Settings", - summary = "", // Empty or blank summary - preferenceKey = "advanced_stuff", - breadcrumbs = listOf("Developer", "Experiments"), - ), - SettingsSearchItem( - title = "Do not collect usage data", - summary = "", // Empty or blank summary - preferenceKey = "do_not_collect_data", - breadcrumbs = listOf("Privacy", "Usage Data"), - ), -) - -class TestSettingsIndexer : SettingsIndexer { - - override fun indexAllSettings() { - // no op - } - - override fun getSettingsWithQuery(query: String): List<SettingsSearchItem> { - return testList - } -} - -class EmptyTestSettingsIndexer : SettingsIndexer { - override fun indexAllSettings() { - // no op - } - - override fun getSettingsWithQuery(query: String): List<SettingsSearchItem> { - return emptyList() } }