tor-browser

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

commit 852cb6e02c130d030c9b1c7f645f389e21a32b50
parent 11fd6a9e9ea76c5f000e187303ed6a8342fc1ad9
Author: mcarare <48995920+mcarare@users.noreply.github.com>
Date:   Wed, 15 Oct 2025 08:08:41 +0000

Bug 1993482 - Migrate settings screen to Jetpack Compose. r=android-reviewers,rebecatudor273

This patch migrates the main settings screen from the legacy preference XML to a Jetpack Compose implementation.

The changes include:
- Creating a new `SettingsScreen.kt` composable to build the settings menu.
- Updating `SettingsFragment` to use Jetpack Compose for rendering its content, replacing the XML-based `addPreferencesFromResource`.
- Adding `preferenceTitle` and `preferenceSummary` text styles to `FocusTypography` for styling the new Compose screen.
- Removing obsolete preference keys from `preference_keys.xml` and settings.xml.
- Remove unnecessary PreferenceManager.setDefaultValues from FocusApplication.

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

Diffstat:
Mmobile/android/focus-android/app/src/androidTest/java/org/mozilla/focus/activity/robots/SettingsRobot.kt | 28+++++++++++-----------------
Mmobile/android/focus-android/app/src/main/java/org/mozilla/focus/FocusApplication.kt | 3---
Mmobile/android/focus-android/app/src/main/java/org/mozilla/focus/settings/SettingsFragment.kt | 46+++++++++++++++-------------------------------
Amobile/android/focus-android/app/src/main/java/org/mozilla/focus/settings/SettingsScreen.kt | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmobile/android/focus-android/app/src/main/java/org/mozilla/focus/ui/theme/FocusTypography.kt | 12++++++++++++
Mmobile/android/focus-android/app/src/main/res/values/preference_keys.xml | 6------
Dmobile/android/focus-android/app/src/main/res/xml/settings.xml | 44--------------------------------------------
Mmobile/android/focus-android/quality/detekt-baseline.xml | 1-
8 files changed, 127 insertions(+), 102 deletions(-)

diff --git a/mobile/android/focus-android/app/src/androidTest/java/org/mozilla/focus/activity/robots/SettingsRobot.kt b/mobile/android/focus-android/app/src/androidTest/java/org/mozilla/focus/activity/robots/SettingsRobot.kt @@ -3,24 +3,21 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.focus.activity.robots -import androidx.test.uiautomator.UiScrollable import androidx.test.uiautomator.UiSelector import org.junit.Assert.assertTrue import org.mozilla.focus.R import org.mozilla.focus.helpers.TestHelper.getStringResource import org.mozilla.focus.helpers.TestHelper.mDevice -import org.mozilla.focus.helpers.TestHelper.packageName import org.mozilla.focus.helpers.TestHelper.waitingTime class SettingsRobot { fun verifySettingsMenuItems() { - settingsMenuList.waitForExists(waitingTime) - assertTrue(generalSettingsMenu().exists()) - assertTrue(searchSettingsMenu.exists()) - assertTrue(privacySettingsMenu.exists()) - assertTrue(advancedSettingsMenu.exists()) - assertTrue(mozillaSettingsMenu.exists()) + assertTrue("General settings item not found", generalSettingsMenu().waitForExists(waitingTime)) + assertTrue("Search settings item not found", searchSettingsMenu.waitForExists(waitingTime)) + assertTrue("Privacy settings item not found", privacySettingsMenu.waitForExists(waitingTime)) + assertTrue("Advanced settings item not found", advancedSettingsMenu.waitForExists(waitingTime)) + assertTrue("Mozilla settings item not found", mozillaSettingsMenu.waitForExists(waitingTime)) } class Transition { @@ -84,31 +81,28 @@ class SettingsRobot { } } -private val settingsMenuList = - UiScrollable(UiSelector().resourceId("$packageName:id/recycler_view")) - private fun generalSettingsMenu(localizedText: String = getStringResource(R.string.preference_category_general)) = - settingsMenuList.getChild( + mDevice.findObject( UiSelector() .text(localizedText), ) -private val searchSettingsMenu = settingsMenuList.getChild( +private val searchSettingsMenu = mDevice.findObject( UiSelector() .text(getStringResource(R.string.preference_category_search)), ) -private val privacySettingsMenu = settingsMenuList.getChild( +private val privacySettingsMenu = mDevice.findObject( UiSelector() .text(getStringResource(R.string.preference_privacy_and_security_header)), ) -private val advancedSettingsMenu = settingsMenuList.getChild( +private val advancedSettingsMenu = mDevice.findObject( UiSelector() .text(getStringResource(R.string.preference_category_advanced)), ) -private val mozillaSettingsMenu = settingsMenuList.getChild( +private val mozillaSettingsMenu = mDevice.findObject( UiSelector() - .text(getStringResource(R.string.preference_mozilla_summary)), + .text(getStringResource(R.string.preference_category_mozilla)), ) diff --git a/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/FocusApplication.kt b/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/FocusApplication.kt @@ -12,7 +12,6 @@ import androidx.annotation.OpenForTesting import androidx.annotation.VisibleForTesting import androidx.appcompat.app.AppCompatDelegate import androidx.lifecycle.ProcessLifecycleOwner -import androidx.preference.PreferenceManager import androidx.work.Configuration.Builder import androidx.work.Configuration.Provider import kotlinx.coroutines.CoroutineScope @@ -65,8 +64,6 @@ open class FocusApplication : LocaleAwareApplication(), Provider, CoroutineScope if (isMainProcess()) { initializeNimbus() - PreferenceManager.setDefaultValues(this, R.xml.settings, false) - setTheme(this) components.engine.warmUp() diff --git a/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/settings/SettingsFragment.kt b/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/settings/SettingsFragment.kt @@ -4,41 +4,25 @@ package org.mozilla.focus.settings -import android.os.Bundle +import androidx.compose.runtime.Composable import org.mozilla.focus.R import org.mozilla.focus.ext.requireComponents -import org.mozilla.focus.ext.showToolbar import org.mozilla.focus.state.AppAction -import org.mozilla.focus.state.Screen -class SettingsFragment : BaseSettingsFragment() { - - override fun onCreatePreferences(bundle: Bundle?, s: String?) { - addPreferencesFromResource(R.xml.settings) - } - - override fun onResume() { - super.onResume() - - showToolbar(getString(R.string.menu_settings)) - } - - override fun onPreferenceTreeClick(preference: androidx.preference.Preference): Boolean { - val resources = resources - - val page = when (preference.key) { - resources.getString(R.string.pref_key_general_screen) -> Screen.Settings.Page.General - resources.getString(R.string.pref_key_privacy_security_screen) -> Screen.Settings.Page.Privacy - resources.getString(R.string.pref_key_search_screen) -> Screen.Settings.Page.Search - resources.getString(R.string.pref_key_advanced_screen) -> Screen.Settings.Page.Advanced - resources.getString(R.string.pref_key_mozilla_screen) -> Screen.Settings.Page.Mozilla - else -> throw IllegalStateException("Unknown preference: ${preference.key}") +/** + * A fragment that displays the main settings screen. + * It uses Jetpack Compose to render its UI. + * + * When a user interacts with a setting that requires navigating to a sub-page (e.g., "Search"), + * this fragment dispatches an [AppAction.OpenSettings] action to handle the navigation logic. + */ +class SettingsFragment : BaseComposeFragment() { + override val titleRes: Int = R.string.menu_settings + + @Composable + override fun Content() { + SettingsScreen { page -> + requireComponents.appStore.dispatch(AppAction.OpenSettings(page)) } - - requireComponents.appStore.dispatch( - AppAction.OpenSettings(page), - ) - - return super.onPreferenceTreeClick(preference) } } diff --git a/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/settings/SettingsScreen.kt b/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/settings/SettingsScreen.kt @@ -0,0 +1,89 @@ +/* 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.focus.settings + +import androidx.annotation.StringRes +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +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.res.stringResource +import androidx.compose.ui.unit.dp +import org.mozilla.focus.R +import org.mozilla.focus.state.Screen +import org.mozilla.focus.ui.theme.focusTypography + +/** + * The main settings screen, displaying a list of top-level setting categories. + * Each category, when clicked, navigates to its respective detailed settings page. + * + * @param onSettingClick A lambda function that is invoked when a setting category is clicked. + * It passes the corresponding [Screen.Settings.Page] to the caller, which is responsible + * for handling the navigation to the detailed settings screen. + */ +@Composable +fun SettingsScreen(onSettingClick: (Screen.Settings.Page) -> Unit) { + Column(modifier = Modifier.fillMaxSize()) { + SettingItem( + title = R.string.preference_category_general, + summary = stringResource(id = R.string.preference_general_summary2), + onClick = { onSettingClick(Screen.Settings.Page.General) }, + ) + SettingItem( + title = R.string.preference_privacy_and_security_header, + summary = stringResource(id = R.string.preference_privacy_and_security_summary), + onClick = { onSettingClick(Screen.Settings.Page.Privacy) }, + ) + SettingItem( + title = R.string.preference_category_search, + summary = stringResource(id = R.string.preference_search_summary), + onClick = { onSettingClick(Screen.Settings.Page.Search) }, + ) + SettingItem( + title = R.string.preference_category_advanced, + summary = stringResource(id = R.string.preference_advanced_summary), + onClick = { onSettingClick(Screen.Settings.Page.Advanced) }, + ) + SettingItem( + title = R.string.preference_category_mozilla, + summary = stringResource( + id = R.string.preference_mozilla_summary, + stringResource(id = R.string.app_name), + ), + onClick = { onSettingClick(Screen.Settings.Page.Mozilla) }, + ) + } +} + +@Composable +private fun SettingItem( + @StringRes title: Int, + summary: String, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .fillMaxWidth() + .clickable(onClick = onClick) + .padding(16.dp), + ) { + Column(modifier = Modifier.weight(1f)) { + Text( + text = stringResource(id = title), + style = focusTypography.preferenceTitle, + ) + Text( + text = summary, + style = focusTypography.preferenceSummary, + ) + } + } +} diff --git a/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/ui/theme/FocusTypography.kt b/mobile/android/focus-android/app/src/main/java/org/mozilla/focus/ui/theme/FocusTypography.kt @@ -41,6 +41,8 @@ data class FocusTypography( val onboardingButton: TextStyle, val cfrTextStyle: TextStyle, val cfrCookieBannerTextStyle: TextStyle, + val preferenceTitle: TextStyle, + val preferenceSummary: TextStyle, ) { val displayLarge: TextStyle get() = materialTypography.displayLarge @@ -136,4 +138,14 @@ val focusTypography: FocusTypography letterSpacing = 0.25.sp, lineHeight = 20.sp, ), + preferenceTitle = TextStyle( + fontSize = 16.sp, + lineHeight = 21.sp, // from 16sp textSize + 5sp lineSpacingExtra + color = focusColors.settingsTextColor, + ), + preferenceSummary = TextStyle( + fontSize = 14.sp, + letterSpacing = 0.42.sp, // from 14sp textSize * 0.03 letterSpacing + color = focusColors.settingsTextSummaryColor, + ), ) diff --git a/mobile/android/focus-android/app/src/main/res/values/preference_keys.xml b/mobile/android/focus-android/app/src/main/res/values/preference_keys.xml @@ -62,12 +62,6 @@ <string name="pref_key_autocomplete_custom" translatable="false"><xliff:g id="preference_key">pref_autocomplete_custom</xliff:g></string> <string name="pref_key_screen_custom_domains" translatable="false"><xliff:g id="preference_key">pref_screen_custom_domains</xliff:g></string> - <string name="pref_key_privacy_security_screen" translatable="false"><xliff:g id="preference_key">pref_screen_privacy_security</xliff:g></string> - <string name="pref_key_advanced_screen" translatable="false"><xliff:g id="preference_key">pref_advanced_screen</xliff:g></string> - <string name="pref_key_general_screen" translatable="false"><xliff:g id="preference_key">pref_general_screen</xliff:g></string> - - <string name="pref_key_mozilla_screen" translatable="false"><xliff:g id="preference_key">pref_screen_mozilla</xliff:g></string> - <string name="pref_key_search_screen" translatable="false"><xliff:g id="preference_key">pref_screen_search</xliff:g></string> <string name="pref_key_remote_debugging" translatable="false"><xliff:g id="preference_key">pref_remote_debugging</xliff:g></string> <string name="pref_key_open_links_in_external_app" translatable="false"><xliff:g id="preference_key">pref_key_open_links_in_external_app</xliff:g></string> <string name="pref_key_secret_settings" translatable="false"><xliff:g id="preference_key">pref_key_secret_settings</xliff:g></string> diff --git a/mobile/android/focus-android/app/src/main/res/xml/settings.xml b/mobile/android/focus-android/app/src/main/res/xml/settings.xml @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- 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/. --> -<androidx.preference.PreferenceScreen - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto"> - - <androidx.preference.Preference - app:iconSpaceReserved="false" - android:key="@string/pref_key_general_screen" - android:layout="@layout/focus_preference" - android:summary="@string/preference_general_summary2" - android:title="@string/preference_category_general" /> - - <androidx.preference.Preference - app:iconSpaceReserved="false" - android:key="@string/pref_key_privacy_security_screen" - android:layout="@layout/focus_preference" - android:summary="@string/preference_privacy_and_security_summary" - android:title="@string/preference_privacy_and_security_header" /> - - <androidx.preference.Preference - app:iconSpaceReserved="false" - android:key="@string/pref_key_search_screen" - android:layout="@layout/focus_preference" - android:summary="@string/preference_search_summary" - android:title="@string/preference_category_search" /> - - <androidx.preference.Preference - app:iconSpaceReserved="false" - android:key="@string/pref_key_advanced_screen" - android:layout="@layout/focus_preference" - android:summary="@string/preference_advanced_summary" - android:title="@string/preference_category_advanced" /> - - <org.mozilla.focus.widget.MozillaPreference - app:iconSpaceReserved="false" - android:key="@string/pref_key_mozilla_screen" - android:layout="@layout/focus_preference" - android:summary="@string/preference_mozilla_summary" - android:title="@string/preference_category_mozilla" - app:allowDividerAbove="false"/> - -</androidx.preference.PreferenceScreen> diff --git a/mobile/android/focus-android/quality/detekt-baseline.xml b/mobile/android/focus-android/quality/detekt-baseline.xml @@ -94,7 +94,6 @@ <ID>UndocumentedPublicClass:SearchWidgetProvider.kt$SearchWidgetProvider : AppSearchWidgetProvider</ID> <ID>UndocumentedPublicClass:SearchWidgetUtils.kt$SearchWidgetUtils</ID> <ID>UndocumentedPublicClass:SecretSettingsFragment.kt$SecretSettingsFragment : BaseSettingsFragmentOnSharedPreferenceChangeListener</ID> - <ID>UndocumentedPublicClass:SettingsFragment.kt$SettingsFragment : BaseSettingsFragment</ID> <ID>UndocumentedPublicFunction:SitePermissionOptionsStorage.kt$SitePermissionOptionsStorage$fun getSitePermissionLabel(sitePermission: SitePermission): String</ID> <ID>UndocumentedPublicClass:SitePermission.kt$SitePermission : Parcelable</ID> <ID>UndocumentedPublicClass:SitePermissionOption.kt$AutoplayOption</ID>