tor-browser

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

commit d82a9eb7735877f411448a8226b3ebbb318a1776
parent 688e7b30dcf80b580018bee8efc323510f010780
Author: Harrison Oglesby <oglesby.harrison@gmail.com>
Date:   Thu, 23 Oct 2025 19:12:49 +0000

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

Differential Revision: https://phabricator.services.mozilla.com/D267902

Diffstat:
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/DefaultFenixSettingsIndexer.kt | 100+++++++++++++++++++++++++------------------------------------------------------
Amobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/PreferenceFileInformation.kt | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchAction.kt | 7+++++++
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchFragment.kt | 3+++
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 | 18++++++++++++++++++
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchResultItem.kt | 2++
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchScreen.kt | 9+++++++--
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchStore.kt | 1+
Mmobile/android/fenix/app/src/main/res/navigation/nav_graph.xml | 8+++++++-
Mmobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchMiddlewareTest.kt | 13++++++++++++-
11 files changed, 233 insertions(+), 72 deletions(-)

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 @@ -9,7 +9,6 @@ import android.content.res.Resources import android.content.res.XmlResourceParser import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import org.mozilla.fenix.R import java.io.IOException /** @@ -32,7 +31,7 @@ class DefaultFenixSettingsIndexer(private val context: Context) : SettingsIndexe val settingFileParser = getXmlParserForFile(preferenceFileInformation.xmlResourceId) breadcrumbs.add(context.getString(preferenceFileInformation.topBreadcrumbResourceId)) if (settingFileParser != null) { - parseXmlFile(settingFileParser) + parseXmlFile(settingFileParser, preferenceFileInformation) } } } @@ -71,6 +70,7 @@ class DefaultFenixSettingsIndexer(private val context: Context) : SettingsIndexe @Suppress("NestedBlockDepth") private fun parseXmlFile( parser: XmlResourceParser, + preferenceFileInformation: PreferenceFileInformation, ) { try { var eventType = parser.next() @@ -83,7 +83,10 @@ class DefaultFenixSettingsIndexer(private val context: Context) : SettingsIndexe when (parser.name) { PREFERENCE_CATEGORY_TAG -> { addCategoryToBreadcrumbs(parser) - categoryItem = createCategoryItem(parser) + categoryItem = createCategoryItem( + parser, + preferenceFileInformation, + ) } CHECKBOX_PREFERENCE_TAG, CUSTOM_CBH_SWITCH_PREFERENCE_TAG, @@ -93,7 +96,10 @@ class DefaultFenixSettingsIndexer(private val context: Context) : SettingsIndexe TEXT_PERCENTAGE_SEEK_BAR_PREFERENCE_TAG, TOGGLE_RADIO_BUTTON_PREFERENCE_TAG, -> { - val item = createSettingsSearchItemFromAttributes(parser) + val item = createSettingsSearchItemFromAttributes( + parser = parser, + preferenceFileInformation = preferenceFileInformation, + ) if (item != null) { settings.add(item) } @@ -107,7 +113,10 @@ class DefaultFenixSettingsIndexer(private val context: Context) : SettingsIndexe settings.add(categoryItem.copy(preferenceKey = preferenceKey ?: "")) categoryItem = null } else { - val item = createSettingsSearchItemFromAttributes(parser) + val item = createSettingsSearchItemFromAttributes( + parser, + preferenceFileInformation, + ) if (item != null) { settings.add(item) } @@ -156,6 +165,7 @@ class DefaultFenixSettingsIndexer(private val context: Context) : SettingsIndexe private fun createSettingsSearchItemFromAttributes( parser: XmlResourceParser, + preferenceFileInformation: PreferenceFileInformation, ): SettingsSearchItem? { var key: String? = null var title: String? = null @@ -197,6 +207,7 @@ class DefaultFenixSettingsIndexer(private val context: Context) : SettingsIndexe title = title, summary = summary, breadcrumbs = breadcrumbs.toList(), + preferenceFileInformation = preferenceFileInformation, ) } @@ -207,9 +218,11 @@ class DefaultFenixSettingsIndexer(private val context: Context) : SettingsIndexe * 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 @@ -236,6 +249,7 @@ class DefaultFenixSettingsIndexer(private val context: Context) : SettingsIndexe title = title ?: "", summary = summary, breadcrumbs = breadcrumbs.toList(), + preferenceFileInformation = preferenceFileInformation, ) } @@ -303,69 +317,19 @@ class DefaultFenixSettingsIndexer(private val context: Context) : SettingsIndexe * In a [List] of [PreferenceFileInformation]s. */ val preferenceFileInformationList = listOf( - PreferenceFileInformation( - xmlResourceId = R.xml.preferences, - topBreadcrumbResourceId = R.string.settings_title, - ), - PreferenceFileInformation( - xmlResourceId = R.xml.accessibility_preferences, - topBreadcrumbResourceId = R.string.preferences_accessibility, - ), - PreferenceFileInformation( - xmlResourceId = R.xml.autofill_preferences, - topBreadcrumbResourceId = R.string.preferences_autofill, - ), - PreferenceFileInformation( - xmlResourceId = R.xml.customization_preferences, - topBreadcrumbResourceId = R.string.preferences_customize, - ), - PreferenceFileInformation( - xmlResourceId = R.xml.default_search_engine_preferences, - topBreadcrumbResourceId = R.string.preferences_default_search_engine, - ), - PreferenceFileInformation( - xmlResourceId = R.xml.downloads_settings_preferences, - topBreadcrumbResourceId = R.string.preferences_downloads, - ), - PreferenceFileInformation( - xmlResourceId = R.xml.home_preferences, - topBreadcrumbResourceId = R.string.preferences_home_2, - ), - PreferenceFileInformation( - xmlResourceId = R.xml.open_links_in_apps_preferences, - topBreadcrumbResourceId = R.string.preferences_open_links_in_apps, - ), - PreferenceFileInformation( - xmlResourceId = R.xml.private_browsing_preferences, - topBreadcrumbResourceId = R.string.preferences_private_browsing_options, - ), - PreferenceFileInformation( - xmlResourceId = R.xml.search_settings_preferences, - topBreadcrumbResourceId = R.string.preferences_search, - ), - PreferenceFileInformation( - xmlResourceId = R.xml.tabs_preferences, - topBreadcrumbResourceId = R.string.preferences_tabs, - ), - PreferenceFileInformation( - xmlResourceId = R.xml.tracking_protection_preferences, - topBreadcrumbResourceId = R.string.preference_enhanced_tracking_protection, - ), - PreferenceFileInformation( - xmlResourceId = R.xml.save_logins_preferences, - topBreadcrumbResourceId = R.string.preferences_passwords_save_logins_2, - ), + 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, ) } } - -/** - * 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. - */ -data class PreferenceFileInformation( - val xmlResourceId: Int, - val topBreadcrumbResourceId: Int, -) 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 @@ -0,0 +1,138 @@ +/* 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/SettingsSearchAction.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchAction.kt @@ -34,4 +34,11 @@ 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/SettingsSearchFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/settingssearch/SettingsSearchFragment.kt @@ -54,6 +54,9 @@ class SettingsSearchFragment : Fragment() { initialState = SettingsSearchState.Default, middleware = listOf( SettingsSearchMiddleware( + SettingsSearchMiddleware.Companion.Dependencies( + navController = findNavController(), + ), 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,12 +14,14 @@ 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, ) { /** @@ -29,18 +31,22 @@ 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,6 +4,8 @@ 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 @@ -13,11 +15,14 @@ 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. */ class SettingsSearchMiddleware( + initialDependencies: Dependencies, val fenixSettingsIndexer: SettingsIndexer, ) : Middleware<SettingsSearchState, SettingsSearchAction> { + var dependencies = initialDependencies init { fenixSettingsIndexer.indexAllSettings() @@ -46,10 +51,23 @@ class SettingsSearchMiddleware( } } } + is SettingsSearchAction.ResultItemClicked -> { + val bundle = bundleOf("preference_to_scroll_to" to action.item.preferenceKey) + val fragmentId = action.item.preferenceFileInformation.fragmentId + CoroutineScope(Dispatchers.Main).launch { + dependencies.navController.navigate(fragmentId, bundle) + } + } else -> { next(action) // no op in middleware layer } } } + + companion object { + data class Dependencies( + val navController: NavController, + ) + } } 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 @@ -71,12 +71,14 @@ private class SettingsSearchResultItemParameterProvider : PreviewParameterProvid 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, ), ) } 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 @@ -65,13 +65,18 @@ fun SettingsSearchScreen( .fillMaxSize(), ) { items(state.searchResults.size) { index -> + val settingsSearchItem = state.searchResults[index] if (index != 0) { HorizontalDivider() } SettingsSearchResultItem( - item = state.searchResults[index], + item = settingsSearchItem, onClick = { - // dispatch navigation click event + store.dispatch( + SettingsSearchAction.ResultItemClicked( + settingsSearchItem, + ), + ) }, ) } 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 @@ -53,6 +53,7 @@ private fun reduce(state: SettingsSearchState, action: SettingsSearchAction): Se searchResults = action.results, ) } + is SettingsSearchAction.ResultItemClicked -> state } } 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 @@ -992,7 +992,13 @@ <fragment android:id="@+id/privateBrowsingFragment" android:name="org.mozilla.fenix.settings.PrivateBrowsingFragment" - android:label="@string/preferences_private_browsing_options" /> + android:label="@string/preferences_private_browsing_options" > + <argument + android:name="preference_to_scroll_to" + android:defaultValue="@null" + app:argType="string" + app:nullable="true" /> + </fragment> <fragment android:id="@+id/httpsOnlyFragment" android:name="org.mozilla.fenix.settings.HttpsOnlyFragment" 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 @@ -5,7 +5,9 @@ package org.mozilla.fenix.settings.settingssearch import android.content.Context +import androidx.navigation.NavController import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi import mozilla.components.support.test.libstate.ext.waitUntilIdle import mozilla.components.support.test.middleware.CaptureActionsMiddleware @@ -23,10 +25,13 @@ class SettingsSearchMiddlewareTest { @get:Rule val coroutineRule = MainCoroutineRule() - private var context: Context = mock() + private val navController: NavController = mockk(relaxed = true) private fun buildMiddleware(): SettingsSearchMiddleware { return SettingsSearchMiddleware( + initialDependencies = SettingsSearchMiddleware.Companion.Dependencies( + navController = navController, + ), fenixSettingsIndexer = TestSettingsIndexer(), ) } @@ -34,6 +39,9 @@ class SettingsSearchMiddlewareTest { @Test fun `WHEN the settings search query is updated and results are not found THEN the state is updated`() { val middleware = SettingsSearchMiddleware( + initialDependencies = SettingsSearchMiddleware.Companion.Dependencies( + navController = navController, + ), fenixSettingsIndexer = EmptyTestSettingsIndexer(), ) val capture = CaptureActionsMiddleware<SettingsSearchState, SettingsSearchAction>() @@ -79,18 +87,21 @@ val testList = listOf( 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, ), SettingsSearchItem( title = "Do not collect usage data", summary = "", // Empty or blank summary preferenceKey = "do_not_collect_data", breadcrumbs = listOf("Privacy", "Usage Data"), + preferenceFileInformation = PreferenceFileInformation.GeneralPreferences, ), )