tor-commits
  Threads by month 
                
            - ----- 2025 -----
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2024 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2023 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2022 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2021 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2020 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2019 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2018 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2017 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2016 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2015 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2014 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2013 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2012 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 - January
 - ----- 2011 -----
 - December
 - November
 - October
 - September
 - August
 - July
 - June
 - May
 - April
 - March
 - February
 
March 2025
- 1 participants
 - 235 discussions
 
                    
                        
boklm pushed to branch main at The Tor Project / Applications / RBM
Commits:
e8f3ffa4 by Pier Angelo Vendrame at 2025-03-20T12:00:08+01:00
Fix a typo in README.md.
- - - - -
1 changed file:
- README.md
Changes:
=====================================
README.md
=====================================
@@ -3,7 +3,7 @@ rbm: Reproducible Build Manager
 
 rbm is a packaging tool that helps you create and build packages for
 multiple Linux distributions, and automate the parts that can be
-automated. It includes options to run the build in a defined environement
+automated. It includes options to run the build in a defined environment
 to allow reproducing the build.
 
 For more information, visit the website :
View it on GitLab: https://gitlab.torproject.org/tpo/applications/rbm/-/commit/e8f3ffa48716b91…
-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/rbm/-/commit/e8f3ffa48716b91…
You're receiving this email because of your account on gitlab.torproject.org.
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                        
                            
                                
                            
                            [Git][tpo/applications/tor-browser][tor-browser-128.8.0esr-14.5-1] 5 commits: fixup! [android] Implement Android-native Connection Assist UI
                        
                        
by Dan Ballard (@dan) 20 Mar '25
                    by Dan Ballard (@dan) 20 Mar '25
20 Mar '25
                    
                        
Dan Ballard pushed to branch tor-browser-128.8.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
52589797 by clairehurst at 2025-03-19T18:33:18-06:00
fixup! [android] Implement Android-native Connection Assist UI
Bug 41188: Wire up stages to UI
- - - - -
f0a7fed9 by clairehurst at 2025-03-19T18:33:18-06:00
fixup! TB 42247: Android helpers for the TorProvider
Bug 41188: Wire up stages to UI
- - - - -
9fe16689 by clairehurst at 2025-03-19T18:33:19-06:00
add to fixup! TB 42247: Android helpers for the TorProvider
- - - - -
c1ec88a7 by clairehurst at 2025-03-19T18:33:19-06:00
add to fixup! [android] Implement Android-native Connection Assist UI
- - - - -
293696bb by Dan Ballard at 2025-03-19T17:58:54-07:00
fixup! fixup! TB 42247: Android helpers for the TorProvider
- - - - -
12 changed files:
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/ConnectAssistUiState.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/QuickstartViewModel.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorBootstrapProgressViewModel.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistFragment.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistViewModel.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/UrlQuickLoadViewModel.kt
- mobile/android/fenix/app/src/main/res/layout/fragment_tor_connection_assist.xml
- mobile/android/fenix/app/src/main/res/values/colors.xml
- mobile/android/fenix/app/src/main/res/values/torbrowser_strings.xml
- mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorAndroidIntegration.java
- toolkit/modules/TorAndroidIntegration.sys.mjs
Changes:
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
=====================================
@@ -1111,6 +1111,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, TorAn
                 .setAllCapsForActionButton(false)
                 .setAction(getString(R.string.connection_assist_connect_to_tor_before_opening_links_confirmation)) {
                     urlQuickLoadViewModel.urlToLoadAfterConnecting.value = searchTermOrURL
+                    urlQuickLoadViewModel.maybeBeginBootstrap()
                     if (navHost.navController.previousBackStackEntry?.destination?.id == R.id.torConnectionAssistFragment) {
                         supportFragmentManager.popBackStack()
                     } else {
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/ConnectAssistUiState.kt
=====================================
@@ -2,14 +2,12 @@ package org.mozilla.fenix.tor
 
 import androidx.annotation.ColorRes
 import androidx.annotation.DrawableRes
-import androidx.annotation.IntRange
 import androidx.annotation.StringRes
 import org.mozilla.fenix.R
 
 enum class ConnectAssistUiState(
     val progressBarVisible: Boolean,
-    @IntRange(0, 100) var progress: Int = 0,
-    @ColorRes val progressTintColorResource: Int? = null,
+    @ColorRes val progressBackgroundTintColorResource: Int = R.color.progress_background_tint,
     val backButtonVisible: Boolean,
     val settingsButtonVisible: Boolean,
     val torConnectImageVisible: Boolean,
@@ -23,18 +21,19 @@ enum class ConnectAssistUiState(
     @StringRes val internetErrorDescription2: Int? = null,
     @StringRes val titleDescriptionTextStringResource: Int? = R.string.preferences_tor_network_settings_explanation,
     val quickstartSwitchVisible: Boolean,
-    val unblockTheInternetInCountryDescriptionVisible: Boolean,
     val countryDropDownVisible: Boolean,
+    @StringRes val countryDropDownDefaultItem: Int = R.string.connection_assist_automatic_country_detection,
     val torBootstrapButton1Visible: Boolean,
     @StringRes val torBootstrapButton1TextStringResource: Int = R.string.tor_bootstrap_connect,
-    val torBootstrapButton1ShouldShowTryingABridge: Boolean = false,
+    val torBootstrapButton1ShouldTryABridge: Boolean = false,
+    val torBootstrapButton1ShouldOpenSettings: Boolean = false,
     val torBootstrapButton2Visible: Boolean,
     @StringRes val torBootstrapButton2TextStringResource: Int? = R.string.connection_assist_configure_connection_button,
     val torBootstrapButton2ShouldOpenSettings: Boolean = true,
     val wordmarkLogoVisible: Boolean = false,
     val torBootstrapButton2ShouldRestartApp: Boolean = false,
 ) {
-    Splash(
+    Loading(
         progressBarVisible = false,
         backButtonVisible = false,
         settingsButtonVisible = false,
@@ -42,15 +41,13 @@ enum class ConnectAssistUiState(
         titleLargeTextViewVisible = false,
         titleDescriptionVisible = false,
         quickstartSwitchVisible = false,
-        unblockTheInternetInCountryDescriptionVisible = false,
         countryDropDownVisible = false,
         torBootstrapButton1Visible = false,
         torBootstrapButton2Visible = false,
         wordmarkLogoVisible = true,
     ),
-    Configuring(
+    Start(
         progressBarVisible = false,
-        progress = 0,
         backButtonVisible = false,
         settingsButtonVisible = true,
         torConnectImageVisible = true,
@@ -60,16 +57,14 @@ enum class ConnectAssistUiState(
         titleDescriptionVisible = true,
         titleDescriptionTextStringResource = R.string.preferences_tor_network_settings_explanation,
         quickstartSwitchVisible = true,
-        unblockTheInternetInCountryDescriptionVisible = false,
         countryDropDownVisible = false,
         torBootstrapButton1Visible = true,
         torBootstrapButton2Visible = true,
         torBootstrapButton2TextStringResource = R.string.connection_assist_configure_connection_button,
         torBootstrapButton2ShouldOpenSettings = true,
     ),
-    Connecting(
+    Bootstrapping(
         progressBarVisible = true,
-        progress = 0,
         backButtonVisible = false,
         settingsButtonVisible = true,
         torConnectImageVisible = true,
@@ -79,17 +74,15 @@ enum class ConnectAssistUiState(
         titleDescriptionVisible = true,
         titleDescriptionTextStringResource = R.string.preferences_tor_network_settings_explanation,
         quickstartSwitchVisible = true,
-        unblockTheInternetInCountryDescriptionVisible = false,
         countryDropDownVisible = false,
         torBootstrapButton1Visible = false,
         torBootstrapButton2Visible = true,
         torBootstrapButton2TextStringResource = R.string.btn_cancel,
         torBootstrapButton2ShouldOpenSettings = false,
     ),
-    InternetError(
+    Offline(
         progressBarVisible = true,
-        progress = 100,
-        progressTintColorResource = R.color.warning_yellow,
+        progressBackgroundTintColorResource = R.color.warning_yellow,
         backButtonVisible = true,
         settingsButtonVisible = true,
         torConnectImageVisible = true,
@@ -101,7 +94,6 @@ enum class ConnectAssistUiState(
         internetErrorDescription = R.string.connection_assist_internet_error_description,
         titleDescriptionTextStringResource = null,
         quickstartSwitchVisible = false,
-        unblockTheInternetInCountryDescriptionVisible = false,
         countryDropDownVisible = false,
         torBootstrapButton1Visible = true,
         torBootstrapButton1TextStringResource = R.string.connection_assist_internet_error_try_again,
@@ -111,8 +103,6 @@ enum class ConnectAssistUiState(
     ),
     TryingAgain(
         progressBarVisible = true,
-        progress = 0,
-        progressTintColorResource = null,
         backButtonVisible = true,
         settingsButtonVisible = true,
         torConnectImageVisible = true,
@@ -124,17 +114,15 @@ enum class ConnectAssistUiState(
         internetErrorDescription = R.string.connection_assist_internet_error_description,
         titleDescriptionTextStringResource = null,
         quickstartSwitchVisible = false,
-        unblockTheInternetInCountryDescriptionVisible = false,
         countryDropDownVisible = false,
         torBootstrapButton1Visible = false,
         torBootstrapButton2Visible = true,
         torBootstrapButton2TextStringResource = R.string.btn_cancel,
         torBootstrapButton2ShouldOpenSettings = false,
     ),
-    ConnectionAssist(
+    ChooseRegion(
         progressBarVisible = true,
-        progress = 100,
-        progressTintColorResource = R.color.warning_yellow,
+        progressBackgroundTintColorResource = R.color.warning_yellow,
         backButtonVisible = true,
         settingsButtonVisible = true,
         torConnectImageVisible = true,
@@ -146,19 +134,16 @@ enum class ConnectAssistUiState(
         internetErrorDescription = R.string.connection_assist_try_a_bridge_description,
         titleDescriptionTextStringResource = null,
         quickstartSwitchVisible = false,
-        unblockTheInternetInCountryDescriptionVisible = true,
         countryDropDownVisible = true,
         torBootstrapButton1Visible = true,
         torBootstrapButton1TextStringResource = R.string.connection_assist_try_a_bridge_button,
-        torBootstrapButton1ShouldShowTryingABridge = true,
+        torBootstrapButton1ShouldTryABridge = true,
         torBootstrapButton2Visible = false,
         torBootstrapButton2TextStringResource = null,
         torBootstrapButton2ShouldOpenSettings = true,
     ),
     TryingABridge(
         progressBarVisible = true,
-        progress = 0,
-        progressTintColorResource = null,
         backButtonVisible = true,
         settingsButtonVisible = true,
         torConnectImageVisible = true,
@@ -167,20 +152,18 @@ enum class ConnectAssistUiState(
         titleLargeTextViewTextStringResource = R.string.connection_assist_trying_a_bridge_title,
         titleDescriptionVisible = true,
         learnMoreStringResource = R.string.connection_assist_internet_error_learn_more,
-        internetErrorDescription = R.string.connection_assist_try_a_bridge_description,
+        internetErrorDescription = ChooseRegion.internetErrorDescription,
         titleDescriptionTextStringResource = null,
         quickstartSwitchVisible = true,
-        unblockTheInternetInCountryDescriptionVisible = false,
         countryDropDownVisible = false,
         torBootstrapButton1Visible = false,
         torBootstrapButton2Visible = true,
         torBootstrapButton2TextStringResource = R.string.btn_cancel,
         torBootstrapButton2ShouldOpenSettings = false,
     ),
-    LocationError(
+    RegionNotFound(
         progressBarVisible = true,
-        progress = 100,
-        progressTintColorResource = R.color.warning_yellow,
+        progressBackgroundTintColorResource = R.color.warning_yellow,
         backButtonVisible = true,
         settingsButtonVisible = true,
         torConnectImageVisible = true,
@@ -194,19 +177,39 @@ enum class ConnectAssistUiState(
         internetErrorDescription2 = R.string.connection_assist_select_country_try_again,
         titleDescriptionTextStringResource = null,
         quickstartSwitchVisible = false,
-        unblockTheInternetInCountryDescriptionVisible = true,
         countryDropDownVisible = true,
+        countryDropDownDefaultItem = R.string.connection_assist_select_country_or_region,
         torBootstrapButton1Visible = true,
         torBootstrapButton1TextStringResource = R.string.connection_assist_try_a_bridge_button,
-        torBootstrapButton1ShouldShowTryingABridge = true,
+        torBootstrapButton1ShouldTryABridge = true,
         torBootstrapButton2Visible = false,
         torBootstrapButton2TextStringResource = null,
         torBootstrapButton2ShouldOpenSettings = true,
     ),
-    LocationCheck(
+    TryingABridgeRegionNotFound(
+        progressBarVisible = true,
+        backButtonVisible = true,
+        settingsButtonVisible = true,
+        torConnectImageVisible = true,
+        torConnectImageResource = R.drawable.connect,
+        titleLargeTextViewVisible = true,
+        titleLargeTextViewTextStringResource = R.string.connection_assist_trying_a_bridge_title,
+        titleDescriptionVisible = true,
+        learnMoreStringResource = R.string.connection_assist_internet_error_learn_more,
+        internetErrorDescription = RegionNotFound.internetErrorDescription,
+        internetErrorDescription1 = RegionNotFound.internetErrorDescription1,
+        internetErrorDescription2 = RegionNotFound.internetErrorDescription2,
+        titleDescriptionTextStringResource = null,
+        quickstartSwitchVisible = true,
+        countryDropDownVisible = false,
+        torBootstrapButton1Visible = false,
+        torBootstrapButton2Visible = true,
+        torBootstrapButton2TextStringResource = R.string.btn_cancel,
+        torBootstrapButton2ShouldOpenSettings = false,
+    ),
+    ConfirmRegion(
         progressBarVisible = true,
-        progress = 100,
-        progressTintColorResource = R.color.warning_yellow,
+        progressBackgroundTintColorResource = R.color.warning_yellow,
         backButtonVisible = true,
         settingsButtonVisible = true,
         torConnectImageVisible = true,
@@ -220,18 +223,38 @@ enum class ConnectAssistUiState(
         internetErrorDescription2 = R.string.connection_assist_select_country_try_again,
         titleDescriptionTextStringResource = null,
         quickstartSwitchVisible = false,
-        unblockTheInternetInCountryDescriptionVisible = true,
         countryDropDownVisible = true,
+        countryDropDownDefaultItem = R.string.connection_assist_select_country_or_region,
         torBootstrapButton1Visible = true,
         torBootstrapButton1TextStringResource = R.string.connection_assist_try_a_bridge_button,
-        torBootstrapButton1ShouldShowTryingABridge = true,
+        torBootstrapButton1ShouldTryABridge = true,
         torBootstrapButton2Visible = false,
         torBootstrapButton2TextStringResource = null,
         torBootstrapButton2ShouldOpenSettings = true,
     ),
+    TryingABridgeConfirmRegion(
+        progressBarVisible = true,
+        backButtonVisible = true,
+        settingsButtonVisible = true,
+        torConnectImageVisible = true,
+        torConnectImageResource = R.drawable.connect,
+        titleLargeTextViewVisible = true,
+        titleLargeTextViewTextStringResource = R.string.connection_assist_trying_a_bridge_title,
+        titleDescriptionVisible = true,
+        learnMoreStringResource = R.string.connection_assist_internet_error_learn_more,
+        internetErrorDescription = ConfirmRegion.internetErrorDescription,
+        internetErrorDescription1 = ConfirmRegion.internetErrorDescription1,
+        internetErrorDescription2 = ConfirmRegion.internetErrorDescription2,
+        titleDescriptionTextStringResource = null,
+        quickstartSwitchVisible = true,
+        countryDropDownVisible = false,
+        torBootstrapButton1Visible = false,
+        torBootstrapButton2Visible = true,
+        torBootstrapButton2TextStringResource = R.string.btn_cancel,
+        torBootstrapButton2ShouldOpenSettings = false,
+    ),
     LastTry(
         progressBarVisible = true,
-        progress = 0,
         backButtonVisible = true,
         settingsButtonVisible = true,
         torConnectImageVisible = true,
@@ -245,7 +268,6 @@ enum class ConnectAssistUiState(
         internetErrorDescription2 = R.string.connection_assist_select_country_try_again,
         titleDescriptionTextStringResource = null,
         quickstartSwitchVisible = true,
-        unblockTheInternetInCountryDescriptionVisible = false,
         countryDropDownVisible = false,
         torBootstrapButton1Visible = false,
         torBootstrapButton2Visible = true,
@@ -254,8 +276,7 @@ enum class ConnectAssistUiState(
     ),
     FinalError(
         progressBarVisible = true,
-        progress = 100,
-        progressTintColorResource = R.color.warning_yellow,
+        progressBackgroundTintColorResource = R.color.warning_yellow,
         backButtonVisible = true,
         settingsButtonVisible = true,
         torConnectImageVisible = true,
@@ -268,10 +289,10 @@ enum class ConnectAssistUiState(
         internetErrorDescription1 = R.string.connection_assist_final_error_troubleshoot_connection_link,
         titleDescriptionTextStringResource = null,
         quickstartSwitchVisible = false,
-        unblockTheInternetInCountryDescriptionVisible = false,
         countryDropDownVisible = false,
         torBootstrapButton1Visible = true,
         torBootstrapButton1TextStringResource = R.string.connection_assist_configure_connection_button,
+        torBootstrapButton1ShouldOpenSettings = true,
         torBootstrapButton2Visible = true,
         torBootstrapButton2TextStringResource = R.string.mozac_lib_crash_dialog_button_restart,
         torBootstrapButton2ShouldOpenSettings = false,
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/QuickstartViewModel.kt
=====================================
@@ -12,19 +12,19 @@ class QuickstartViewModel(
 ) : AndroidViewModel(application) {
 
     private val components = getApplication<Application>().components
-    private val torIntegrationAndroid =
+    private val torAndroidIntegration =
         (components.core.engine as GeckoEngine).getTorIntegrationController()
 
     /**
      * NOTE: Whilst the initial value for _quickstart is fetched from
-     * TorIntegrationAndroid.quickstartGet (which is surfaced from TorConnect.quickstart), and we
+     * TorAndroidIntegration.quickstartGet (which is surfaced from TorConnect.quickstart), and we
      * pass on any changes in value up to TorConnect.quickstart (via quickstartSet()), we do not
      * listen for any changes to the TorConnect.quickstart value via "QuickstartChange" because we
      * do not expect anything outside of TorConnectViewModel to change its value, so we expect its
      * value to remain in sync with our local value.
      */
     init {
-        torIntegrationAndroid.quickstartGet {
+        torAndroidIntegration.quickstartGet {
             _quickstart.value = it
             components.settings.quickStart = it
         }
@@ -36,7 +36,7 @@ class QuickstartViewModel(
     }
 
     fun quickstartSet(value: Boolean) {
-        torIntegrationAndroid.quickstartSet(value)
+        torAndroidIntegration.quickstartSet(value)
         _quickstart.value = value
         components.settings.quickStart = value
     }
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorBootstrapProgressViewModel.kt
=====================================
@@ -11,7 +11,7 @@ class TorBootstrapProgressViewModel(
     application: Application,
 ) : AndroidViewModel(application), BootstrapStateChangeListener {
 
-    private val torIntegrationAndroid =
+    private val torAndroidIntegration =
         application.components.core.geckoRuntime.torIntegrationController
 
     val progress: MutableLiveData<Int> by lazy {
@@ -19,11 +19,11 @@ class TorBootstrapProgressViewModel(
     }
 
     init {
-        torIntegrationAndroid.registerBootstrapStateChangeListener(this)
+        torAndroidIntegration.registerBootstrapStateChangeListener(this)
     }
 
     override fun onCleared() {
-        torIntegrationAndroid.unregisterBootstrapStateChangeListener(this)
+        torAndroidIntegration.unregisterBootstrapStateChangeListener(this)
         super.onCleared()
     }
 
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistFragment.kt
=====================================
@@ -4,6 +4,8 @@
 
 package org.mozilla.fenix.tor
 
+import android.content.BroadcastReceiver
+import android.content.Context
 import android.content.Intent
 import android.graphics.Color
 import android.os.Build
@@ -17,7 +19,10 @@ import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
 import androidx.appcompat.content.res.AppCompatResources
+import androidx.core.view.isEmpty
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
 import androidx.fragment.app.viewModels
@@ -38,7 +43,6 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
     private val progressViewModel: TorBootstrapProgressViewModel by viewModels()
     private val quickstartViewModel: QuickstartViewModel by activityViewModels()
     private val torConnectionAssistViewModel : TorConnectionAssistViewModel by viewModels()
-    private val urlQuickLoadViewModel : UrlQuickLoadViewModel by activityViewModels()
 
     private var _binding: FragmentTorConnectionAssistBinding? = null
     private val binding get() = _binding!!
@@ -52,9 +56,18 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
             inflater, container, false,
         )
 
+        object : BroadcastReceiver() {
+            override fun onReceive(context: Context, intent: Intent) {
+                if (intent.action === Intent.ACTION_LOCALE_CHANGED) {
+                    Log.v("LocaleReceiver", "received ACTION_LOCALE_CHANGED")
+                    torConnectionAssistViewModel.fetchCountryNamesGet()
+                }
+            }
+        }
+
         viewLifecycleOwner.lifecycleScope.launch {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
-                torConnectionAssistViewModel.collectLastKnownStatus()
+                torConnectionAssistViewModel.collectTorConnectStage()
             }
         }
 
@@ -65,12 +78,6 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
             }
         }
 
-        urlQuickLoadViewModel.urlToLoadAfterConnecting.observe(viewLifecycleOwner) { url ->
-            if (!url.isNullOrBlank()) {
-                torConnectionAssistViewModel.handleConnect()
-            }
-        }
-
         return binding.root
     }
 
@@ -129,14 +136,10 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
     private fun setProgressBar(screen: ConnectAssistUiState) {
         binding.torBootstrapProgressBar.visibility =
             if (screen.progressBarVisible) View.VISIBLE else View.GONE
-        binding.torBootstrapProgressBar.progress = screen.progress
-        binding.torBootstrapProgressBar.progressTintList =
-            screen.progressTintColorResource?.let {
-                AppCompatResources.getColorStateList(
-                    requireContext(),
-                    it,
-                )
-            }
+        binding.torBootstrapProgressBar.progressBackgroundTintList = AppCompatResources.getColorStateList(
+            requireContext(),
+            screen.progressBackgroundTintColorResource,
+        )
     }
 
     private fun setSettingsButton(screen: ConnectAssistUiState) {
@@ -201,17 +204,126 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
     }
 
     private fun setCountryDropDown(screen: ConnectAssistUiState) {
-        binding.unblockTheInternetInCountryDescription.visibility =
-            if (screen.unblockTheInternetInCountryDescriptionVisible) View.VISIBLE else View.GONE
-        binding.countryDropDown.visibility = if (screen.countryDropDownVisible) View.VISIBLE else View.GONE
+        if (screen.countryDropDownVisible) {
+            val spinnerAdapter: ArrayAdapter<String> = initializeSpinner()
+            if (binding.countryDropDown.isEmpty()) {
+                populateCountryDropDown(spinnerAdapter)
+                setOnItemSelectedListener()
+            }
+
+            setFirstItemInCountryDropDown(spinnerAdapter, getString(screen.countryDropDownDefaultItem))
+
+            if (screen == ConnectAssistUiState.ChooseRegion || screen == ConnectAssistUiState.ConfirmRegion || screen == ConnectAssistUiState.RegionNotFound) {
+                torConnectionAssistViewModel.selectDefaultRegion()
+                binding.countryDropDown.setSelection(spinnerAdapter.getPosition(torConnectionAssistViewModel.selectedCountryCode.value))
+            }
+
+            binding.unblockTheInternetInCountryDescription.visibility = View.VISIBLE
+            binding.countryDropDown.visibility = View.VISIBLE
+        } else {
+            binding.unblockTheInternetInCountryDescription.visibility = View.GONE
+            binding.countryDropDown.visibility = View.GONE
+        }
+    }
+
+    private fun setFirstItemInCountryDropDown(
+        spinnerAdapter: ArrayAdapter<String>,
+        item: String,
+    ) {
+        if (!spinnerAdapter.isEmpty) {
+            spinnerAdapter.remove(spinnerAdapter.getItem(0))
+        }
+        spinnerAdapter.insert(item, 0)
+    }
+
+    private fun initializeSpinner(): ArrayAdapter<String> {
+        val spinnerAdapter: ArrayAdapter<String> =
+            ArrayAdapter<String>(
+                requireContext(),
+                android.R.layout.simple_spinner_item,
+                android.R.id.text1,
+            )
+        spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+        binding.countryDropDown.adapter = spinnerAdapter
+        return spinnerAdapter
+    }
+
+    private fun populateCountryDropDown(spinnerAdapter: ArrayAdapter<String>) {
+        torConnectionAssistViewModel.fetchCountryNamesGet()
+        viewLifecycleOwner.lifecycleScope.launch {
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                torConnectionAssistViewModel.countryCodeNameMap.collect {
+                    Log.d(TAG, "countryCodeNameMap: $it")
+                    if (it != null) {
+                        spinnerAdapter.clear()
+                        spinnerAdapter.add(getString(torConnectionAssistViewModel.torConnectScreen.value.countryDropDownDefaultItem))
+                        spinnerAdapter.addAll(it.values)
+                    }
+                }
+            }
+        }
+    }
+
+    private fun setOnItemSelectedListener() {
+        binding.countryDropDown.onItemSelectedListener =
+            object : AdapterView.OnItemSelectedListener {
+                override fun onItemSelected(
+                    parent: AdapterView<*>?,
+                    view: View?,
+                    position: Int,
+                    id: Long,
+                ) {
+                    torConnectionAssistViewModel.setCountryCodeToSelectedItem(position)
+                    updateButton1(torConnectionAssistViewModel.torConnectScreen.value)
+                }
+
+                override fun onNothingSelected(parent: AdapterView<*>?) {}
+            }
     }
 
     private fun setButton1(screen: ConnectAssistUiState) {
-        binding.torBootstrapButton1.visibility =
-            if (screen.torBootstrapButton1Visible) View.VISIBLE else View.GONE
-        binding.torBootstrapButton1.text = getString(screen.torBootstrapButton1TextStringResource)
-        binding.torBootstrapButton1.setOnClickListener {
-            torConnectionAssistViewModel.handleConnect()
+        binding.torBootstrapButton1.apply {
+            visibility =
+                if (screen.torBootstrapButton1Visible) View.VISIBLE else View.GONE
+            text = getString(screen.torBootstrapButton1TextStringResource)
+            setOnClickListener {
+                if (screen.torBootstrapButton1ShouldOpenSettings) {
+                    openTorConnectionSettings()
+                } else {
+                    torConnectionAssistViewModel.handleConnect()
+                }
+            }
+            updateButton1(screen)
+        }
+    }
+
+    private fun updateButton1(screen: ConnectAssistUiState) {
+        binding.torBootstrapButton1.apply {
+            if (!torConnectionAssistViewModel.button1ShouldBeDisabled(screen)) {
+                isEnabled = true
+                backgroundTintList = AppCompatResources.getColorStateList(
+                    requireContext(),
+                    R.color.connect_button_purple,
+                )
+                setTextColor(
+                    AppCompatResources.getColorStateList(
+                        requireContext(),
+                        R.color.photonLightGrey05,
+                    ),
+                )
+            } else {
+                isEnabled = false
+                backgroundTintList = AppCompatResources.getColorStateList(
+                    requireContext(),
+                    R.color.disabled_connect_button_purple,
+                )
+                setTextColor(
+                    AppCompatResources.getColorStateList(
+                        requireContext(),
+                        R.color.disabled_text_gray_purple,
+                    ),
+                )
+            }
         }
     }
 
@@ -235,13 +347,12 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
                 }
         }
         binding.torBootstrapButton2.setOnClickListener {
-            torConnectionAssistViewModel.cancelTorBootstrap()
             if (screen.torBootstrapButton2ShouldOpenSettings) {
                 openTorConnectionSettings()
             } else if (screen.torBootstrapButton2ShouldRestartApp) {
                 restartApplication()
             } else {
-                showScreen(ConnectAssistUiState.Configuring)
+                torConnectionAssistViewModel.cancelTorBootstrap()
             }
         }
     }
@@ -297,11 +408,7 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
     }
 
     private fun openTorConnectionSettings() {
-        findNavController().navigate(
-            TorConnectionAssistFragmentDirections.actionTorConnectionAssistFragmentToSettingsFragment(
-                requireContext().getString(R.string.pref_key_connection)
-            ),
-        )
+        openSettings(requireContext().getString(R.string.pref_key_connection))
     }
 
     private fun restartApplication() {
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistViewModel.kt
=====================================
@@ -11,186 +11,148 @@ import androidx.lifecycle.MutableLiveData
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import org.mozilla.fenix.HomeActivity
+import org.mozilla.fenix.R
 import org.mozilla.fenix.ext.components
+import org.mozilla.gecko.util.GeckoBundle
+import org.mozilla.geckoview.TorAndroidIntegration.BootstrapStateChangeListener
+import org.mozilla.geckoview.TorConnectStage
+import org.mozilla.geckoview.TorConnectStageName
 
 class TorConnectionAssistViewModel(
     application: Application,
-) : AndroidViewModel(application) {
+) : AndroidViewModel(application), BootstrapStateChangeListener {
 
     private val TAG = "torConnectionAssistVM"
-    private val torIntegrationAndroid =
+    private val torAndroidIntegration =
         application.components.core.geckoRuntime.torIntegrationController
-    private val _torController: TorControllerGV = application.components.torController
 
-    private val _torConnectScreen = MutableStateFlow(ConnectAssistUiState.Splash)
-    internal val torConnectScreen: StateFlow<ConnectAssistUiState> = _torConnectScreen
-
-    val shouldOpenHome: MutableLiveData<Boolean> by lazy {
-        MutableLiveData(false)
+    init {
+        torAndroidIntegration.registerBootstrapStateChangeListener(this)
     }
 
-    fun handleConnect() {
-        if (_torConnectScreen.value.torBootstrapButton1ShouldShowTryingABridge) {
-            tryABridge()
-        } else {
-            if (_torController.lastKnownStatus.value.isOff()) {
-                torIntegrationAndroid.beginBootstrap()
+    fun fetchCountryNamesGet() {
+        torAndroidIntegration.countryNamesGet { countryNames : GeckoBundle? ->
+            if (countryNames != null) {
+                val codes: Array<String> = countryNames.keys()
+                val regions = mutableMapOf<String, String>()
+                for (code in codes) {
+                    regions[code] = countryNames.getString(code)
+                }
+                countryCodeNameMap.value = regions
             }
         }
     }
 
-    fun cancelTorBootstrap() {
-        torIntegrationAndroid.cancelBootstrap()
-        _torController.setTorStopped()
-    }
-
-    suspend fun collectLastKnownStatus() {
-        _torController.lastKnownStatus.collect {
-            when (it) {
-                TorConnectState.Initial -> _torConnectScreen.value = ConnectAssistUiState.Splash
-                TorConnectState.Configuring -> handleConfiguring()
-                TorConnectState.AutoBootstrapping -> handleBootstrap()
-                TorConnectState.Bootstrapping -> handleBootstrap()
-                TorConnectState.Bootstrapped -> shouldOpenHome.value = true
-                TorConnectState.Disabled -> shouldOpenHome.value = true
-                TorConnectState.Error -> handleError()
-            }
-        }
+    override fun onCleared() {
+        torAndroidIntegration.unregisterBootstrapStateChangeListener(this)
+        super.onCleared()
     }
 
-    private fun handleConfiguring() {
-        if (_torController.lastKnownError == null) {
-            _torConnectScreen.value = ConnectAssistUiState.Configuring
-        } else {
-            handleError()
-        }
+    private val torConnectStage: MutableStateFlow<TorConnectStage?> by lazy {
+        MutableStateFlow(torAndroidIntegration.lastKnowStage.value)
     }
 
-    private fun handleBootstrap() {
-        when (_torConnectScreen.value) {
-            ConnectAssistUiState.InternetError -> {
-                _torConnectScreen.value = ConnectAssistUiState.TryingAgain
-            }
-
-            ConnectAssistUiState.TryingAgain -> {
-                /** stay here */
-            }
-
-            ConnectAssistUiState.ConnectionAssist -> {
-                _torConnectScreen.value = ConnectAssistUiState.TryingABridge
-            }
-
-            ConnectAssistUiState.LocationError -> {
-                _torConnectScreen.value = ConnectAssistUiState.TryingABridge
-            }
+    private val _torConnectScreen = MutableStateFlow(ConnectAssistUiState.Loading)
+    internal val torConnectScreen: StateFlow<ConnectAssistUiState> = _torConnectScreen
 
-            ConnectAssistUiState.TryingABridge -> {
-                /** stay here */
-            }
+    val countryCodeNameMap: MutableStateFlow<Map<String, String>?> by lazy {
+        MutableStateFlow(null)
+    }
 
-            ConnectAssistUiState.LocationCheck -> {
-                _torConnectScreen.value = ConnectAssistUiState.LastTry
-            }
+    val selectedCountryCode: MutableStateFlow<String> by lazy {
+        MutableStateFlow("automatic")
+    }
 
-            ConnectAssistUiState.LastTry -> {
-                /** stay here */
-            }
+    fun selectDefaultRegion() {
+        selectedCountryCode.value = torConnectStage.value?.defaultRegion ?: "automatic"
+    }
 
-            else -> _torConnectScreen.value =
-                ConnectAssistUiState.Connecting
-        }
+    fun setCountryCodeToSelectedItem(position: Int) {
+        selectedCountryCode.value =
+            countryCodeNameMap.value?.keys?.toList()
+                ?.getOrNull(position - 1) ?: "automatic"
+        // position - 1 since we have the default/first value of automatic
+        Log.d(TAG, "selectedCountryCode = ${selectedCountryCode.value}")
     }
 
-    private fun handleError() {
-        _torController.lastKnownError?.apply {
-            Log.d(
-                TAG,
-                "TorError(message = $message, details = $details, phase = $phase, reason = $reason",
-            )
-            // TODO better error handling
-            when (reason) {
-//                "noroute" -> handleNoRoute() TODO re-add when working better
-                else -> handleUnknownError()
-            }
-        }
+    val shouldOpenHome: MutableLiveData<Boolean> by lazy {
+        MutableLiveData(false)
     }
 
-    private fun handleNoRoute() {
-        Log.d(TAG, "handleNoRoute(), _torConnectScreen.value = ${_torConnectScreen.value}")
-        when (_torConnectScreen.value) {
-            ConnectAssistUiState.Connecting -> _torConnectScreen.value = ConnectAssistUiState.ConnectionAssist
-            ConnectAssistUiState.ConnectionAssist -> {/** no op, likely a duplicate error */}
-            ConnectAssistUiState.TryingABridge -> _torConnectScreen.value = ConnectAssistUiState.LocationCheck
-            ConnectAssistUiState.LocationCheck -> {/** no op, likely a duplicate error */}
-            ConnectAssistUiState.LastTry -> _torConnectScreen.value = ConnectAssistUiState.FinalError
-            ConnectAssistUiState.FinalError -> {/** no op, likely a duplicate error */}
-            else -> _torConnectScreen.value = ConnectAssistUiState.InternetError
+    fun handleConnect() {
+        val screen = _torConnectScreen.value
+        if (screen.torBootstrapButton1ShouldTryABridge && !button1ShouldBeDisabled(screen)) {
+            Log.d(TAG, "beginAutoBootstrap with countryCode: ${selectedCountryCode.value}")
+            torAndroidIntegration.beginAutoBootstrap(selectedCountryCode.value)
+        } else {
+            torAndroidIntegration.beginBootstrap()
         }
     }
 
-    private fun handleUnknownError() {
-        // TODO should we have a dedicated screen for unknown errors?
-        _torConnectScreen.value = ConnectAssistUiState.InternetError
+    fun cancelTorBootstrap() {
+        torAndroidIntegration.cancelBootstrap()
     }
 
-    private fun tryABridge() {
-        if (!locationFound()) {
-            _torConnectScreen.value = ConnectAssistUiState.LocationError
-            return
-        }
-        if (!_torController.bridgesEnabled) {
-            _torController.bridgesEnabled = true
-            _torController.bridgeTransport =
-                TorBridgeTransportConfig.BUILTIN_SNOWFLAKE // TODO select based on country
+    suspend fun collectTorConnectStage() {
+        torConnectStage.collect {
+            Log.d(TAG, "torConnectStageName: ${it?.name}")
+            when (it?.name) {
+                TorConnectStageName.Disabled       -> shouldOpenHome.value = true // TODO use TorConnect.enabled instead to determine this
+                TorConnectStageName.Loading        -> _torConnectScreen.value = ConnectAssistUiState.Loading
+                TorConnectStageName.Start          -> _torConnectScreen.value = ConnectAssistUiState.Start
+                TorConnectStageName.Bootstrapping  -> _torConnectScreen.value = handleBootstrapTrigger(it.bootstrapTrigger)
+                TorConnectStageName.Offline        -> _torConnectScreen.value = ConnectAssistUiState.Offline
+                TorConnectStageName.ChooseRegion   -> _torConnectScreen.value = ConnectAssistUiState.ChooseRegion
+                TorConnectStageName.RegionNotFound -> _torConnectScreen.value = ConnectAssistUiState.RegionNotFound
+                TorConnectStageName.ConfirmRegion  -> _torConnectScreen.value = ConnectAssistUiState.ConfirmRegion
+                TorConnectStageName.FinalError     -> _torConnectScreen.value = ConnectAssistUiState.FinalError
+                TorConnectStageName.Bootstrapped   -> shouldOpenHome.value = true
+                null                               -> {}
+            }
         }
-        torIntegrationAndroid.beginBootstrap()
     }
 
-    private fun locationFound(): Boolean {
-        // TODO try to find location
-        return true
+    private fun handleBootstrapTrigger(bootstrapTrigger: TorConnectStageName) : ConnectAssistUiState {
+        Log.d(TAG, "bootstrapTrigger: $bootstrapTrigger")
+        return when (bootstrapTrigger) {
+            TorConnectStageName.Start          -> ConnectAssistUiState.Bootstrapping
+            TorConnectStageName.Offline        -> ConnectAssistUiState.TryingAgain
+            TorConnectStageName.ChooseRegion   -> ConnectAssistUiState.TryingABridge
+            TorConnectStageName.RegionNotFound -> ConnectAssistUiState.TryingABridgeRegionNotFound
+            TorConnectStageName.ConfirmRegion  -> ConnectAssistUiState.TryingABridgeConfirmRegion
+            else                               -> {
+                Log.e(TAG, "Unexpected bootstrapTrigger of $bootstrapTrigger")
+                ConnectAssistUiState.TryingAgain
+            }
+        }
     }
 
     fun handleBackButtonPressed(homeActivity: HomeActivity) {
         when (torConnectScreen.value) {
-            ConnectAssistUiState.Splash -> homeActivity.shutDown()
-            ConnectAssistUiState.Configuring -> homeActivity.shutDown()
-            ConnectAssistUiState.Connecting -> cancelTorBootstrap()
-            ConnectAssistUiState.InternetError -> {
-                _torController.lastKnownError = null
-                _torConnectScreen.value = ConnectAssistUiState.Configuring
-            }
-
-            ConnectAssistUiState.TryingAgain -> {
-                cancelTorBootstrap()
-            }
+            ConnectAssistUiState.Loading -> homeActivity.shutDown()
+            ConnectAssistUiState.Start   -> homeActivity.shutDown()
+            else                         -> torAndroidIntegration.startAgain()
+        }
+    }
 
-            ConnectAssistUiState.ConnectionAssist -> {
-                _torController.lastKnownError = null
-                _torConnectScreen.value = ConnectAssistUiState.Configuring
-            }
+    override fun onBootstrapStateChange(state: String?) {}
 
-            ConnectAssistUiState.TryingABridge -> {
-                _torController.stopTor()
-                _torConnectScreen.value = ConnectAssistUiState.ConnectionAssist
-            }
+    override fun onBootstrapStageChange(stage: TorConnectStage?) {
+        torConnectStage.value = stage
+    }
 
-            ConnectAssistUiState.LocationError -> {
-                _torConnectScreen.value = ConnectAssistUiState.ConnectionAssist
-            }
+    override fun onBootstrapProgress(progress: Double, hasWarnings: Boolean) {}
 
-            ConnectAssistUiState.LocationCheck -> {
-                _torConnectScreen.value = ConnectAssistUiState.LocationError
-            }
+    override fun onBootstrapComplete() {}
 
-            ConnectAssistUiState.LastTry -> {
-                _torController.stopTor()
-                _torConnectScreen.value = ConnectAssistUiState.LocationCheck
-            }
+    override fun onBootstrapError(
+        code: String?,
+        message: String?,
+        phase: String?,
+        reason: String?,
+    ) {}
 
-            ConnectAssistUiState.FinalError -> {
-                _torConnectScreen.value = ConnectAssistUiState.LocationCheck
-            }
-        }
+    fun button1ShouldBeDisabled(screen: ConnectAssistUiState): Boolean {
+        return selectedCountryCode.value == "automatic" && screen.countryDropDownDefaultItem == R.string.connection_assist_select_country_or_region
     }
 }
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/UrlQuickLoadViewModel.kt
=====================================
@@ -1,10 +1,26 @@
 package org.mozilla.fenix.tor
 
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
 import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
+import org.mozilla.fenix.ext.components
+import org.mozilla.geckoview.TorConnectStageName
+
+class UrlQuickLoadViewModel(application: Application) : AndroidViewModel(application) {
+
+    private val torAndroidIntegration =
+        application.components.core.geckoRuntime.torIntegrationController
 
-class UrlQuickLoadViewModel : ViewModel() {
     val urlToLoadAfterConnecting: MutableLiveData<String?> by lazy {
         MutableLiveData<String?>(null)
     }
+
+    fun maybeBeginBootstrap() {
+        when (torAndroidIntegration.lastKnowStage.value?.name) {
+            TorConnectStageName.Offline -> torAndroidIntegration.beginBootstrap()
+            TorConnectStageName.Start -> torAndroidIntegration.beginBootstrap()
+            else -> {}
+        }
+    }
+
 }
=====================================
mobile/android/fenix/app/src/main/res/layout/fragment_tor_connection_assist.xml
=====================================
@@ -45,6 +45,7 @@
         android:layout_marginStart="8dp"
         android:layout_marginTop="8dp"
         android:visibility="invisible"
+        android:contentDescription="@string/connection_assist_back_button_content_description_start_again"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent">
 
=====================================
mobile/android/fenix/app/src/main/res/values/colors.xml
=====================================
@@ -380,6 +380,8 @@
 
     <!-- Connection Assist -->
     <color name="connect_button_purple">#9059FF</color>
+    <color name="disabled_connect_button_purple">#5C42A9</color>
+    <color name="disabled_text_gray_purple">#8782A9</color>
     <color name="configure_connection_button_white">#E1E0E7</color>
     <color name="warning_yellow">#FFA436</color>
     <color name="progress_background_tint">#55148C</color>
=====================================
mobile/android/fenix/app/src/main/res/values/torbrowser_strings.xml
=====================================
@@ -78,7 +78,9 @@
     <!-- Connection assist. -->
     <string name="connection_assist_unblock_the_internet_in_country_or_region">Unblock the internet in:</string>
     <!-- Connection assist. -->
-    <string name="connection_assist_share_my_location_country_or_region">Share my location</string>
+    <string name="connection_assist_automatic_country_detection">Automatic</string>
+    <!-- Connection assist, -->
+    <string name="connection_assist_select_country_or_region">Select country or region</string>
     <!-- Connection assist. -->
     <string name="connection_assist_try_a_bridge_button">Try a bridge</string>
 
@@ -122,5 +124,7 @@
     <string name="connection_assist_connect_to_tor_before_opening_links">Connect to Tor before opening links</string>
     <!-- Connection assist. Confirmation button for a shown "Snackbar" (special popup notification). -->
     <string name="connection_assist_connect_to_tor_before_opening_links_confirmation">CONNECT</string>
+    <!-- Connection assist. Content Description for back button. Button will start the connection assist process again -->
+    <string name="connection_assist_back_button_content_description_start_again">Start again</string>
 
 </resources>
=====================================
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorAndroidIntegration.java
=====================================
@@ -10,6 +10,8 @@ import android.content.Context;
 import android.os.AsyncTask;
 import android.util.Log;
 import androidx.annotation.NonNull;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -53,8 +55,10 @@ public class TorAndroidIntegration implements BundleEventListener {
   private static final String EVENT_BOOTSTRAP_BEGIN_AUTO = "GeckoView:Tor:BootstrapBeginAuto";
   private static final String EVENT_BOOTSTRAP_CANCEL = "GeckoView:Tor:BootstrapCancel";
   private static final String EVENT_BOOTSTRAP_GET_STATE = "GeckoView:Tor:BootstrapGetState";
+  private static final String EVENT_START_AGAIN = "GeckoView:Tor:StartAgain";
   private static final String EVENT_QUICKSTART_GET = "GeckoView:Tor:QuickstartGet";
   private static final String EVENT_QUICKSTART_SET = "GeckoView:Tor:QuickstartSet";
+  private static final String EVENT_COUNTRY_NAMES_GET = "GeckoView:Tor:CountryNamesGet";
 
   private static final String CONTROL_PORT_FILE = "/control-ipc";
   private static final String SOCKS_FILE = "/socks-ipc";
@@ -82,6 +86,9 @@ public class TorAndroidIntegration implements BundleEventListener {
 
   private int mMeekCounter;
 
+  private final MutableLiveData<TorConnectStage> _lastKnownStage = new MutableLiveData<>(null);
+  public LiveData<TorConnectStage> lastKnowStage = _lastKnownStage;
+
   /**
    * mSettings is a Java-side copy of the authoritative settings in the JS code. It's useful to
    * maintain as the UI may be fetching these options often and we don't watch each fetch to be a
@@ -154,6 +161,7 @@ public class TorAndroidIntegration implements BundleEventListener {
       }
     } else if (EVENT_CONNECT_STAGE_CHANGED.equals(event)) {
       TorConnectStage stage = new TorConnectStage(message.getBundle("stage"));
+      _lastKnownStage.setValue(stage);
       for (BootstrapStateChangeListener listener : mBootstrapStateListeners) {
         listener.onBootstrapStageChange(stage);
       }
@@ -693,6 +701,10 @@ public class TorAndroidIntegration implements BundleEventListener {
     return EventDispatcher.getInstance().queryVoid(EVENT_SETTINGS_SET, bundle);
   }
 
+  public @NonNull GeckoResult<Void> startAgain() {
+    return EventDispatcher.getInstance().queryVoid(EVENT_START_AGAIN);
+  }
+
   public interface QuickstartGetter {
     void onValue(boolean enabled);
   }
@@ -710,6 +722,17 @@ public class TorAndroidIntegration implements BundleEventListener {
     return EventDispatcher.getInstance().queryVoid(EVENT_QUICKSTART_SET, bundle);
   }
 
+  public interface CountryNamesGetter {
+    void onValue(GeckoBundle regions);
+  }
+
+  public void countryNamesGet(CountryNamesGetter countryNamesGetter) {
+    EventDispatcher.getInstance().queryBundle(EVENT_COUNTRY_NAMES_GET).then(countryNames -> {
+      countryNamesGetter.onValue(countryNames);
+      return new GeckoResult<Void>();
+    });
+  }
+
   public @NonNull GeckoResult<Void> beginBootstrap() {
     return EventDispatcher.getInstance().queryVoid(EVENT_BOOTSTRAP_BEGIN);
   }
=====================================
toolkit/modules/TorAndroidIntegration.sys.mjs
=====================================
@@ -42,8 +42,10 @@ const ListenedEvents = Object.freeze({
   bootstrapBeginAuto: "GeckoView:Tor:BootstrapBeginAuto",
   bootstrapCancel: "GeckoView:Tor:BootstrapCancel",
   bootstrapGetState: "GeckoView:Tor:BootstrapGetState",
+  startAgain: "GeckoView:Tor:StartAgain",
   quickstartGet: "GeckoView:Tor:QuickstartGet",
   quickstartSet: "GeckoView:Tor:QuickstartSet",
+  countryNamesGet: "GeckoView:Tor:CountryNamesGet",
 });
 
 class TorAndroidIntegrationImpl {
@@ -190,14 +192,18 @@ class TorAndroidIntegrationImpl {
         case ListenedEvents.bootstrapGetState:
           callback?.onSuccess(lazy.TorConnect.state);
           return;
-        // TODO: Expose TorConnect.startAgain() to allow users to begin
-        // from the start again.
+        case ListenedEvents.startAgain:
+          lazy.TorConnect.startAgain();
+          break;
         case ListenedEvents.quickstartGet:
           callback?.onSuccess(lazy.TorConnect.quickstart);
           return;
         case ListenedEvents.quickstartSet:
           lazy.TorConnect.quickstart = data.enabled;
           break;
+        case ListenedEvents.countryNamesGet:
+          callback?.onSuccess(lazy.TorConnect.countryNames);
+          return;
       }
       callback?.onSuccess();
     } catch (e) {
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/797d5a…
-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/797d5a…
You're receiving this email because of your account on gitlab.torproject.org.
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                        
                            
                                
                            
                            [Git][tpo/applications/tor-browser][tor-browser-128.8.0esr-14.5-1] fixup! TB 2176: Rebrand Firefox to TorBrowser
                        
                        
by morgan (@morgan) 19 Mar '25
                    by morgan (@morgan) 19 Mar '25
19 Mar '25
                    
                        
morgan pushed to branch tor-browser-128.8.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
797d5a05 by Pier Angelo Vendrame at 2025-03-19T18:58:58+01:00
fixup! TB 2176: Rebrand Firefox to TorBrowser
TB 43575: Clean up more channel-related prefs.
app.update.promptWaitTime is already in 001-base-browser.js.
- - - - -
3 changed files:
- browser/branding/tb-alpha/pref/firefox-branding.js
- browser/branding/tb-nightly/pref/firefox-branding.js
- browser/branding/tb-release/pref/firefox-branding.js
Changes:
=====================================
browser/branding/tb-alpha/pref/firefox-branding.js
=====================================
@@ -17,8 +17,6 @@ pref("app.releaseNotesURL.aboutDialog", "https://blog.torproject.org/new-alpha-r
 
 // The time interval between checks for a new version (in seconds)
 pref("app.update.interval", 43200); // 12 hours
-// Give the user x seconds to react before showing the big UI. default=12 hours
-pref("app.update.promptWaitTime", 43200);
 // The number of days a binary is permitted to be old
 // without checking for an update.  This assumes that
 // app.update.checkInstallTime is true.
=====================================
browser/branding/tb-nightly/pref/firefox-branding.js
=====================================
@@ -17,8 +17,6 @@ pref("app.releaseNotesURL.aboutDialog", "about:blank");
 
 // The time interval between checks for a new version (in seconds)
 pref("app.update.interval", 14400); // 4 hours
-// Give the user x seconds to react before showing the big UI. default=12 hours
-pref("app.update.promptWaitTime", 43200);
 // The number of days a binary is permitted to be old
 // without checking for an update.  This assumes that
 // app.update.checkInstallTime is true.
=====================================
browser/branding/tb-release/pref/firefox-branding.js
=====================================
@@ -17,8 +17,6 @@ pref("app.releaseNotesURL.aboutDialog", "https://blog.torproject.org/new-release
 
 // Interval: Time between checks for a new version (in seconds)
 pref("app.update.interval", 43200); // 12 hours
-// Give the user x seconds to react before showing the big UI. default=192 hours
-pref("app.update.promptWaitTime", 691200);
 // The number of days a binary is permitted to be old
 // without checking for an update.  This assumes that
 // app.update.checkInstallTime is true.
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/797d5a0…
-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/797d5a0…
You're receiving this email because of your account on gitlab.torproject.org.
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                        
                            
                                
                            
                            [Git][tpo/applications/mullvad-browser][mullvad-browser-128.8.0esr-14.5-1] 3 commits: fixup! MB 1: Mullvad Browser branding
                        
                        
by Pier Angelo Vendrame (@pierov) 19 Mar '25
                    by Pier Angelo Vendrame (@pierov) 19 Mar '25
19 Mar '25
                    
                        
Pier Angelo Vendrame pushed to branch mullvad-browser-128.8.0esr-14.5-1 at The Tor Project / Applications / Mullvad Browser
Commits:
b086d25d by Pier Angelo Vendrame at 2025-03-19T18:39:52+01:00
fixup! MB 1: Mullvad Browser branding
MB 412: Link the release page in the about dialog and clean brand-related preferences.
- - - - -
4ac981eb by Pier Angelo Vendrame at 2025-03-19T18:41:32+01:00
fixup! MB 112: Updater customization for Mullvad Browser
MB 412: Link the release page in the about dialog and clean brand-related preferences.
- - - - -
979f4ea4 by Pier Angelo Vendrame at 2025-03-19T18:43:08+01:00
fixup! MB 38: Mullvad Browser configuration
MB 412: Link the release page in the about dialog and clean brand-related preferences.
- - - - -
4 changed files:
- browser/app/profile/000-mullvad-browser.js
- browser/branding/mb-alpha/pref/firefox-branding.js
- browser/branding/mb-nightly/pref/firefox-branding.js
- browser/branding/mb-release/pref/firefox-branding.js
Changes:
=====================================
browser/app/profile/000-mullvad-browser.js
=====================================
@@ -15,9 +15,6 @@ pref("doh-rollout.provider-list", "[{\"UIName\":\"Mullvad\",\"autoDefault\":true
 // mullvad-browser#122: Audit DoH heuristics
 pref("doh-rollout.disable-heuristics", true);
 
-// mullvad-browser#37: Customization for the about dialog
-pref("app.releaseNotesURL.aboutDialog", "about:blank");
-
 // mullvad-browser#87: Windows and Linux need additional work to make the
 // default browser choice working.
 // We are shipping only the portable versions for the initial release anyway, so
@@ -40,9 +37,5 @@ pref("app.update.notifyDuringDownload", true);
 pref("app.update.url.manual", "https://mullvad.net/download/browser");
 pref("app.update.url.details", "https://mullvad.net/download/browser");
 pref("app.update.badgeWaitTime", 0);
-pref("app.releaseNotesURL", "https://github.com/mullvad/mullvad-browser/releases");
-// disables the 'What's New?' link in the about dialog, otherwise we need to
-// duplicate logic for generating the url to the github releases page
-pref("app.releaseNotesURL.aboutDialog", "about:blank");
 // point to our feedback url rather than Mozilla's
 pref("app.feedback.baseURL", "https://mullvad.net/help/tag/browser/");
=====================================
browser/branding/mb-alpha/pref/firefox-branding.js
=====================================
@@ -5,31 +5,23 @@
 // This file contains branding-specific prefs.
 
 pref("startup.homepage_override_url", "");
-pref("startup.homepage_welcome_url", "about:welcome");
-pref("startup.homepage_welcome_url.additional", "");
-// Interval: Time between checks for a new version (in seconds)
-pref("app.update.interval", 43200); // 12 hours
-// Give the user x seconds to react before showing the big UI. default=192 hours
-pref("app.update.promptWaitTime", 691200);
 // app.update.url.manual: URL user can browse to manually if for some reason
 // all update installation attempts fail.
 // app.update.url.details: a default value for the "More information about this
 // update" link supplied in the "An update is available" page of the update
 // wizard.
-pref("app.update.url.manual", "https://www.mozilla.org/%LOCALE%/firefox/new?reason=manual-update");
-pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/notes");
-pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source…");
-pref("app.releaseNotesURL.aboutDialog", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source…");
+pref("app.update.url.manual", "https://mullvad.net/download/browser#alpha");
+pref("app.update.url.details", "https://mullvad.net/download/browser#alpha");
+pref("app.releaseNotesURL", "https://github.com/mullvad/mullvad-browser/releases/tag/%BB_VERSION%");
+pref("app.releaseNotesURL.aboutDialog", "https://github.com/mullvad/mullvad-browser/releases/tag/%BB_VERSION%");
 
+// Interval: Time between checks for a new version (in seconds)
+pref("app.update.interval", 43200); // 12 hours
 // The number of days a binary is permitted to be old
 // without checking for an update.  This assumes that
 // app.update.checkInstallTime is true.
 pref("app.update.checkInstallTime.days", 63);
 
-// Give the user x seconds to reboot before showing a badge on the hamburger
-// button. default=4 days
-pref("app.update.badgeWaitTime", 345600);
-
 // Number of usages of the web console.
 // If this is less than 5, then pasting code into the web console is disabled
 pref("devtools.selfxss.count", 0);
=====================================
browser/branding/mb-nightly/pref/firefox-branding.js
=====================================
@@ -5,31 +5,23 @@
 // This file contains branding-specific prefs.
 
 pref("startup.homepage_override_url", "");
-pref("startup.homepage_welcome_url", "about:welcome");
-pref("startup.homepage_welcome_url.additional", "");
-// Interval: Time between checks for a new version (in seconds)
-pref("app.update.interval", 43200); // 12 hours
-// Give the user x seconds to react before showing the big UI. default=192 hours
-pref("app.update.promptWaitTime", 691200);
 // app.update.url.manual: URL user can browse to manually if for some reason
 // all update installation attempts fail.
 // app.update.url.details: a default value for the "More information about this
 // update" link supplied in the "An update is available" page of the update
 // wizard.
-pref("app.update.url.manual", "https://www.mozilla.org/%LOCALE%/firefox/new?reason=manual-update");
-pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/notes");
-pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source…");
-pref("app.releaseNotesURL.aboutDialog", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source…");
+pref("app.update.url.manual", "https://nightlies.tbb.torproject.org/");
+pref("app.update.url.details", "https://nightlies.tbb.torproject.org/");
+pref("app.releaseNotesURL", "about:blank");
+pref("app.releaseNotesURL.aboutDialog", "about:blank");
 
+// Interval: Time between checks for a new version (in seconds)
+pref("app.update.interval", 14400); // 4 hours
 // The number of days a binary is permitted to be old
 // without checking for an update.  This assumes that
 // app.update.checkInstallTime is true.
-pref("app.update.checkInstallTime.days", 63);
-
-// Give the user x seconds to reboot before showing a badge on the hamburger
-// button. default=4 days
-pref("app.update.badgeWaitTime", 345600);
+pref("app.update.checkInstallTime.days", 2);
 
 // Number of usages of the web console.
 // If this is less than 5, then pasting code into the web console is disabled
-pref("devtools.selfxss.count", 0);
+pref("devtools.selfxss.count", 5);
=====================================
browser/branding/mb-release/pref/firefox-branding.js
=====================================
@@ -5,31 +5,23 @@
 // This file contains branding-specific prefs.
 
 pref("startup.homepage_override_url", "");
-pref("startup.homepage_welcome_url", "about:welcome");
-pref("startup.homepage_welcome_url.additional", "");
-// Interval: Time between checks for a new version (in seconds)
-pref("app.update.interval", 43200); // 12 hours
-// Give the user x seconds to react before showing the big UI. default=192 hours
-pref("app.update.promptWaitTime", 691200);
 // app.update.url.manual: URL user can browse to manually if for some reason
 // all update installation attempts fail.
 // app.update.url.details: a default value for the "More information about this
 // update" link supplied in the "An update is available" page of the update
 // wizard.
-pref("app.update.url.manual", "https://www.mozilla.org/%LOCALE%/firefox/new?reason=manual-update");
-pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/notes");
-pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source…");
-pref("app.releaseNotesURL.aboutDialog", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source…");
+pref("app.update.url.manual", "https://mullvad.net/download/browser");
+pref("app.update.url.details", "https://mullvad.net/download/browser");
+pref("app.releaseNotesURL", "https://github.com/mullvad/mullvad-browser/releases/tag/%BB_VERSION%");
+pref("app.releaseNotesURL.aboutDialog", "https://github.com/mullvad/mullvad-browser/releases/tag/%BB_VERSION%");
 
+// Interval: Time between checks for a new version (in seconds)
+pref("app.update.interval", 43200); // 12 hours
 // The number of days a binary is permitted to be old
 // without checking for an update.  This assumes that
 // app.update.checkInstallTime is true.
 pref("app.update.checkInstallTime.days", 63);
 
-// Give the user x seconds to reboot before showing a badge on the hamburger
-// button. default=4 days
-pref("app.update.badgeWaitTime", 345600);
-
 // Number of usages of the web console.
 // If this is less than 5, then pasting code into the web console is disabled
 pref("devtools.selfxss.count", 0);
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/a4…
-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/compare/a4…
You're receiving this email because of your account on gitlab.torproject.org.
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                        
                            
                                
                            
                            [Git][tpo/applications/mullvad-browser][mullvad-browser-128.8.0esr-14.5-1] fixup! BB 4234: Use the Firefox Update Process for Base Browser.
                        
                        
by Pier Angelo Vendrame (@pierov) 19 Mar '25
                    by Pier Angelo Vendrame (@pierov) 19 Mar '25
19 Mar '25
                    
                        
Pier Angelo Vendrame pushed to branch mullvad-browser-128.8.0esr-14.5-1 at The Tor Project / Applications / Mullvad Browser
Commits:
a4f6cb3b by Pier Angelo Vendrame at 2025-03-19T17:23:18+01:00
fixup! BB 4234: Use the Firefox Update Process for Base Browser.
TB 42720: Show what's new in the about:dialog.
Add a BB_VERSION replacement to the URLFormatter service.
- - - - -
1 changed file:
- toolkit/components/urlformatter/URLFormatter.sys.mjs
Changes:
=====================================
toolkit/components/urlformatter/URLFormatter.sys.mjs
=====================================
@@ -134,6 +134,9 @@ nsURLFormatterService.prototype = {
     DISTRIBUTION_VERSION() {
       return this.distribution.version;
     },
+    BB_VERSION() {
+      return AppConstants.BASE_BROWSER_VERSION;
+    },
   },
 
   formatURL: function uf_formatURL(aFormat) {
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/a4f…
-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/mullvad-browser/-/commit/a4f…
You're receiving this email because of your account on gitlab.torproject.org.
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                        
                            
                                
                            
                            [Git][tpo/applications/tor-browser][base-browser-128.8.0esr-14.5-1] fixup! BB 4234: Use the Firefox Update Process for Base Browser.
                        
                        
by Pier Angelo Vendrame (@pierov) 19 Mar '25
                    by Pier Angelo Vendrame (@pierov) 19 Mar '25
19 Mar '25
                    
                        
Pier Angelo Vendrame pushed to branch base-browser-128.8.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
23c0b249 by Pier Angelo Vendrame at 2025-03-19T17:22:52+01:00
fixup! BB 4234: Use the Firefox Update Process for Base Browser.
TB 42720: Show what's new in the about:dialog.
Add a BB_VERSION replacement to the URLFormatter service.
- - - - -
1 changed file:
- toolkit/components/urlformatter/URLFormatter.sys.mjs
Changes:
=====================================
toolkit/components/urlformatter/URLFormatter.sys.mjs
=====================================
@@ -134,6 +134,9 @@ nsURLFormatterService.prototype = {
     DISTRIBUTION_VERSION() {
       return this.distribution.version;
     },
+    BB_VERSION() {
+      return AppConstants.BASE_BROWSER_VERSION;
+    },
   },
 
   formatURL: function uf_formatURL(aFormat) {
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/23c0b24…
-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/23c0b24…
You're receiving this email because of your account on gitlab.torproject.org.
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                        
                            
                                
                            
                            [Git][tpo/applications/tor-browser][tor-browser-128.8.0esr-14.5-1] fixup! TB 2176: Rebrand Firefox to TorBrowser
                        
                        
by Pier Angelo Vendrame (@pierov) 19 Mar '25
                    by Pier Angelo Vendrame (@pierov) 19 Mar '25
19 Mar '25
                    
                        
Pier Angelo Vendrame pushed to branch tor-browser-128.8.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
cf350084 by Pier Angelo Vendrame at 2025-03-19T17:09:21+01:00
fixup! TB 2176: Rebrand Firefox to TorBrowser
TB 42720: Show what's new in the about:dialog.
Improve some comments.
- - - - -
1 changed file:
- browser/base/content/aboutDialog.xhtml
Changes:
=====================================
browser/base/content/aboutDialog.xhtml
=====================================
@@ -117,6 +117,9 @@
                 <label id="version" class="update"/>
               </hbox>
               <description class="text-blurb">
+                <!-- Place release notes on the same line as the help and feedback links.
+                   - We do not want the release notes on the same line as the version because it may overflow.
+                   - See tor-browerr#42720. -->
                 <label id="releasenotes" is="text-link" hidden="true" data-l10n-id="releaseNotes-link"/>
                 <label is="text-link" onclick="openHelpLink('firefox-help')" data-l10n-id="aboutdialog-help-user"/>
                 <label id="submit-feedback" is="text-link" onclick="openFeedbackPage()" data-l10n-id="aboutdialog-submit-feedback"/>
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/cf35008…
-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/cf35008…
You're receiving this email because of your account on gitlab.torproject.org.
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                        
                            
                                
                            
                            [Git][tpo/applications/tor-browser][tor-browser-128.8.0esr-14.5-1] 4 commits: fixup! TB 40562: Added Tor Browser preferences to 000-tor-browser.js
                        
                        
by Pier Angelo Vendrame (@pierov) 19 Mar '25
                    by Pier Angelo Vendrame (@pierov) 19 Mar '25
19 Mar '25
                    
                        
Pier Angelo Vendrame pushed to branch tor-browser-128.8.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
231850ac by Pier Angelo Vendrame at 2025-03-19T16:59:00+01:00
fixup! TB 40562: Added Tor Browser preferences to 000-tor-browser.js
TB 42720: Show what's new in the about:dialog.
Update our comment about app.releaseNotesURL.aboutDialog.
- - - - -
de58bd66 by Pier Angelo Vendrame at 2025-03-19T16:59:03+01:00
fixup! BB 4234: Use the Firefox Update Process for Base Browser.
TB 42720: Show what's new in the about:dialog.
Add a BB_VERSION replacement to the URLFormatter service.
- - - - -
8f35f42f by Pier Angelo Vendrame at 2025-03-19T16:59:04+01:00
fixup! TB 2176: Rebrand Firefox to TorBrowser
TB 42720: Show what's new in the about:dialog.
Customize the "What's new" link and cleanup other channel-specific
prefs.
- - - - -
8d7ba10b by Pier Angelo Vendrame at 2025-03-19T16:59:04+01:00
fixup! TB 41668: Tweaks to the Base Browser updater for Tor Browser
TB 42720: Show what's new in the about:dialog.
Add a variable for the base browser version without dot, as we use it
in blog post URLs.
- - - - -
7 changed files:
- browser/app/profile/000-tor-browser.js
- browser/base/content/aboutDialog.xhtml
- browser/base/content/aboutDialogTor.css
- browser/branding/tb-alpha/pref/firefox-branding.js
- browser/branding/tb-nightly/pref/firefox-branding.js
- browser/branding/tb-release/pref/firefox-branding.js
- toolkit/components/urlformatter/URLFormatter.sys.mjs
Changes:
=====================================
browser/app/profile/000-tor-browser.js
=====================================
@@ -1,14 +1,7 @@
 #include 001-base-profile.js
 
 pref("app.update.notifyDuringDownload", true);
-pref("app.update.url.manual", "https://www.torproject.org/download/languages/");
-pref("app.update.url.details", "https://www.torproject.org/download/");
 pref("app.update.badgeWaitTime", 0);
-pref("app.releaseNotesURL", "about:blank");
-// disables the 'What's New?' link in the about dialog, otherwise we need to
-// duplicate logic for generating the url to the blog post that is already more
-// easily found in about:tor
-pref("app.releaseNotesURL.aboutDialog", "about:blank");
 // point to our feedback url rather than Mozilla's
 pref("app.feedback.baseURL", "https://support.torproject.org/%LOCALE%/misc/bug-or-feedback/");
 
=====================================
browser/base/content/aboutDialog.xhtml
=====================================
@@ -115,9 +115,9 @@
               <!-- This HBOX is duplicated above without class="update" -->
               <hbox align="baseline">
                 <label id="version" class="update"/>
-                <label id="releasenotes" is="text-link" hidden="true" data-l10n-id="releaseNotes-link"/>
               </hbox>
               <description class="text-blurb">
+                <label id="releasenotes" is="text-link" hidden="true" data-l10n-id="releaseNotes-link"/>
                 <label is="text-link" onclick="openHelpLink('firefox-help')" data-l10n-id="aboutdialog-help-user"/>
                 <label id="submit-feedback" is="text-link" onclick="openFeedbackPage()" data-l10n-id="aboutdialog-submit-feedback"/>
               </description>
=====================================
browser/base/content/aboutDialogTor.css
=====================================
@@ -26,6 +26,10 @@
   margin-block: 10px;
 }
 
+#releasenotes {
+  margin-inline-end: .9em; /* Same as #submit-feedback */
+}
+
 #contributeDesc {
   display: none;
 }
=====================================
browser/branding/tb-alpha/pref/firefox-branding.js
=====================================
@@ -10,30 +10,19 @@
 //   actions="showURL"
 //   openURL="https://blog.torproject.org/tor-browser-55a2-released"
 pref("startup.homepage_override_url", "https://blog.torproject.org/category/applications");
-pref("startup.homepage_welcome_url", "about:welcome");
-pref("startup.homepage_welcome_url.additional", "");
+pref("app.update.url.details", "https://www.torproject.org/download/alpha/");
+pref("app.update.url.manual", "https://www.torproject.org/download/alpha/");
+pref("app.releaseNotesURL", "https://blog.torproject.org/new-alpha-release-tor-browser-%BB_VERSION_FOR_U…");
+pref("app.releaseNotesURL.aboutDialog", "https://blog.torproject.org/new-alpha-release-tor-browser-%BB_VERSION_FOR_U…");
+
 // The time interval between checks for a new version (in seconds)
 pref("app.update.interval", 43200); // 12 hours
 // Give the user x seconds to react before showing the big UI. default=12 hours
 pref("app.update.promptWaitTime", 43200);
-// URL user can browse to manually if for some reason all update installation
-// attempts fail.
-pref("app.update.url.manual", "https://www.mozilla.org/%LOCALE%/firefox/nightly/");
-// A default value for the "More information about this update" link
-// supplied in the "An update is available" page of the update wizard.
-pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/nightly/notes/");
-
-pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source…");
-
 // The number of days a binary is permitted to be old
 // without checking for an update.  This assumes that
 // app.update.checkInstallTime is true.
 pref("app.update.checkInstallTime.days", 2);
-
-// Give the user x seconds to reboot before showing a badge on the hamburger
-// button. default=immediately
-pref("app.update.badgeWaitTime", 0);
-
 // Number of usages of the web console.
 // If this is less than 5, then pasting code into the web console is disabled
 pref("devtools.selfxss.count", 5);
=====================================
browser/branding/tb-nightly/pref/firefox-branding.js
=====================================
@@ -9,32 +9,20 @@
 // each update manifest should contain attributes similar to:
 //   actions="showURL"
 //   openURL="https://blog.torproject.org/tor-browser-55a2-released"
-pref("startup.homepage_override_url", "https://blog.torproject.org/category/applications");
-pref("startup.homepage_welcome_url", "about:welcome");
-pref("startup.homepage_welcome_url.additional", "");
+pref("startup.homepage_override_url", "https://nightlies.tbb.torproject.org/");
+pref("app.update.url.details", "https://nightlies.tbb.torproject.org/nightly-builds/tor-browser-builds/");
+pref("app.update.url.manual", "https://nightlies.tbb.torproject.org/nightly-builds/tor-browser-builds/");
+pref("app.releaseNotesURL", "about:blank");
+pref("app.releaseNotesURL.aboutDialog", "about:blank");
+
 // The time interval between checks for a new version (in seconds)
 pref("app.update.interval", 14400); // 4 hours
 // Give the user x seconds to react before showing the big UI. default=12 hours
 pref("app.update.promptWaitTime", 43200);
-// URL user can browse to manually if for some reason all update installation
-// attempts fail.
-pref("app.update.url.manual", "https://www.mozilla.org/%LOCALE%/firefox/nightly/?reason=manual-update");
-// A default value for the "More information about this update" link
-// supplied in the "An update is available" page of the update wizard.
-pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/nightly/notes/");
-
-pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source…");
-pref("app.releaseNotesURL.aboutDialog", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source…");
-
 // The number of days a binary is permitted to be old
 // without checking for an update.  This assumes that
 // app.update.checkInstallTime is true.
 pref("app.update.checkInstallTime.days", 2);
-
-// Give the user x seconds to reboot before showing a badge on the hamburger
-// button. default=immediately
-pref("app.update.badgeWaitTime", 0);
-
 // Number of usages of the web console.
 // If this is less than 5, then pasting code into the web console is disabled
 pref("devtools.selfxss.count", 5);
=====================================
browser/branding/tb-release/pref/firefox-branding.js
=====================================
@@ -10,43 +10,19 @@
 //   actions="showURL"
 //   openURL="https://blog.torproject.org/tor-browser-55a2-released"
 pref("startup.homepage_override_url", "https://blog.torproject.org/category/applications");
-pref("startup.homepage_welcome_url", "about:welcome");
-pref("startup.homepage_welcome_url.additional", "");
+pref("app.update.url.details", "https://www.torproject.org/download/");
+pref("app.update.url.manual", "https://www.torproject.org/download/");
+pref("app.releaseNotesURL", "https://blog.torproject.org/new-release-tor-browser-%BB_VERSION_FOR_URLS%/");
+pref("app.releaseNotesURL.aboutDialog", "https://blog.torproject.org/new-release-tor-browser-%BB_VERSION_FOR_URLS%/");
+
 // Interval: Time between checks for a new version (in seconds)
 pref("app.update.interval", 43200); // 12 hours
 // Give the user x seconds to react before showing the big UI. default=192 hours
 pref("app.update.promptWaitTime", 691200);
-// app.update.url.manual: URL user can browse to manually if for some reason
-// all update installation attempts fail.
-// app.update.url.details: a default value for the "More information about this
-// update" link supplied in the "An update is available" page of the update
-// wizard.
-#if MOZ_UPDATE_CHANNEL == beta
-  pref("app.update.url.manual", "https://www.mozilla.org/%LOCALE%/firefox/beta?reason=manual-update");
-  pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/beta/notes");
-  pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%beta/releasenotes/?utm_so…");
-  pref("app.releaseNotesURL.aboutDialog", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%beta/releasenotes/?utm_so…");
-#elifdef MOZ_ESR
-  pref("app.update.url.manual", "https://www.mozilla.org/%LOCALE%/firefox/enterprise?reason=manual-update");
-  pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/organizations/notes");
-  pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source…");
-  pref("app.releaseNotesURL.aboutDialog", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source…");
-#else
-  pref("app.update.url.manual", "https://www.mozilla.org/%LOCALE%/firefox/new?reason=manual-update");
-  pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/notes");
-  pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source…");
-  pref("app.releaseNotesURL.aboutDialog", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source…");
-#endif
-
 // The number of days a binary is permitted to be old
 // without checking for an update.  This assumes that
 // app.update.checkInstallTime is true.
 pref("app.update.checkInstallTime.days", 63);
-
-// Give the user x seconds to reboot before showing a badge on the hamburger
-// button. default=4 days
-pref("app.update.badgeWaitTime", 345600);
-
 // Number of usages of the web console.
 // If this is less than 5, then pasting code into the web console is disabled
 pref("devtools.selfxss.count", 0);
=====================================
toolkit/components/urlformatter/URLFormatter.sys.mjs
=====================================
@@ -134,6 +134,16 @@ nsURLFormatterService.prototype = {
     DISTRIBUTION_VERSION() {
       return this.distribution.version;
     },
+    BB_VERSION() {
+      return AppConstants.BASE_BROWSER_VERSION;
+    },
+    BB_VERSION_FOR_URLS() {
+      let version = AppConstants.BASE_BROWSER_VERSION;
+      if (/^[0-9a\.]+$/.test(version)) {
+        version = version.replaceAll(".", "");
+      }
+      return version;
+    },
   },
 
   formatURL: function uf_formatURL(aFormat) {
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/b0a250…
-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/b0a250…
You're receiving this email because of your account on gitlab.torproject.org.
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                        
                            
                                
                            
                            [Git][tpo/applications/tor-browser][tor-browser-128.8.0esr-14.5-1] fixup! TB 27476: Implement about:torconnect captive portal within Tor Browser
                        
                        
by morgan (@morgan) 19 Mar '25
                    by morgan (@morgan) 19 Mar '25
19 Mar '25
                    
                        
morgan pushed to branch tor-browser-128.8.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
b0a250cd by Henry Wilkes at 2025-03-19T11:15:37+00:00
fixup! TB 27476: Implement about:torconnect captive portal within Tor Browser
TB 42656: Drop maybeUpdateOpenLocationForTorConnect.
- - - - -
1 changed file:
- browser/components/urlbar/UrlbarInput.sys.mjs
Changes:
=====================================
browser/components/urlbar/UrlbarInput.sys.mjs
=====================================
@@ -20,7 +20,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
   ReaderMode: "resource://gre/modules/ReaderMode.sys.mjs",
   SearchUIUtils: "resource:///modules/SearchUIUtils.sys.mjs",
   SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs",
-  TorConnect: "resource://gre/modules/TorConnect.sys.mjs",
   UrlbarController: "resource:///modules/UrlbarController.sys.mjs",
   UrlbarEventBufferer: "resource:///modules/UrlbarEventBufferer.sys.mjs",
   UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
@@ -276,36 +275,6 @@ export class UrlbarInput {
     );
   }
 
-  // in certain scenarios we want user input uris to open in a new tab if they do so from the
-  // about:torconnect tab
-  #maybeUpdateOpenLocationForTorConnect(
-    openUILinkWhere,
-    currentURI,
-    destinationURI
-  ) {
-    try {
-      // only open in new tab if:
-      if (
-        // user is navigating away from about:torconnect
-        currentURI === "about:torconnect" &&
-        // we are trying to open in same tab
-        openUILinkWhere === "current" &&
-        // only if user still has not bootstrapped
-        lazy.TorConnect.shouldShowTorConnect &&
-        // and user is not just navigating to about:torconnect
-        destinationURI !== "about:torconnect"
-      ) {
-        return "tab";
-      }
-    } catch (e) {
-      // swallow exception and fall through returning original so we don't accidentally break
-      // anything if an exception is thrown
-      this.logger.error(e?.message ? e.message : e);
-    }
-
-    return openUILinkWhere;
-  }
-
   /**
    * Applies styling to the text in the urlbar input, depending on the text.
    */
@@ -3017,11 +2986,6 @@ export class UrlbarInput {
       this.inputField.setSelectionRange(0, 0);
     }
 
-    openUILinkWhere = this.#maybeUpdateOpenLocationForTorConnect(
-      openUILinkWhere,
-      this.window.gBrowser.currentURI.asciiSpec,
-      url
-    );
     if (openUILinkWhere != "current") {
       this.handleRevert();
     }
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/b0a250c…
-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/b0a250c…
You're receiving this email because of your account on gitlab.torproject.org.
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0
                            
                          
                          
                            
    
                          
                        
                    
                        
                            
                                
                            
                            [Git][tpo/applications/tor-browser][tor-browser-128.8.0esr-14.5-1] 2 commits: fixup! [android] TBA strings
                        
                        
by Dan Ballard (@dan) 19 Mar '25
                    by Dan Ballard (@dan) 19 Mar '25
19 Mar '25
                    
                        
Dan Ballard pushed to branch tor-browser-128.8.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
9078db0b by Dan Ballard at 2025-03-19T00:06:35+00:00
fixup! [android] TBA strings
Bug 43505 [android]: Add 2025 UX Survey Campaign
- - - - -
359eed56 by Dan Ballard at 2025-03-18T17:10:05-07:00
Bug 43505 [android]: Add 2025 UX Survey Campaign
- - - - -
6 changed files:
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
- + mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/CampaignStrings.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt
- + mobile/android/fenix/app/src/main/res/drawable/campaign_hand.xml
- mobile/android/fenix/app/src/main/res/layout/fragment_home.xml
- mobile/android/fenix/app/src/main/res/values/preference_keys.xml
Changes:
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
=====================================
@@ -12,33 +12,65 @@ import android.content.res.Configuration
 import android.graphics.drawable.ColorDrawable
 import android.net.Uri
 import android.os.Bundle
+import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.ImageView
 import androidx.activity.result.ActivityResultLauncher
 import androidx.annotation.VisibleForTesting
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxWithConstraints
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.Button
 import androidx.compose.material.ButtonDefaults
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
 import androidx.compose.material.Text
 import androidx.compose.material.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.drawBehind
+import androidx.compose.ui.draw.scale
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.semantics.heading
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.testTag
 import androidx.compose.ui.semantics.testTagsAsResourceId
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.coordinatorlayout.widget.CoordinatorLayout
 import androidx.core.content.ContextCompat
 import androidx.core.content.ContextCompat.getColor
-import androidx.core.view.children
-import androidx.core.view.doOnLayout
 import androidx.core.view.isGone
 import androidx.core.view.isVisible
 import androidx.fragment.app.Fragment
@@ -108,6 +140,7 @@ import org.mozilla.fenix.components.FenixSnackbar
 import org.mozilla.fenix.components.PrivateShortcutCreateManager
 import org.mozilla.fenix.components.TabCollectionStorage
 import org.mozilla.fenix.components.appstate.AppAction
+import org.mozilla.fenix.components.components
 import org.mozilla.fenix.components.menu.MenuAccessPoint
 import org.mozilla.fenix.components.toolbar.IncompleteRedesignToolbarFeature
 import org.mozilla.fenix.components.toolbar.ToolbarPosition
@@ -151,13 +184,13 @@ import org.mozilla.fenix.messaging.DefaultMessageController
 import org.mozilla.fenix.messaging.FenixMessageSurfaceId
 import org.mozilla.fenix.messaging.MessagingFeature
 import org.mozilla.fenix.microsurvey.ui.MicrosurveyRequestPrompt
-import org.mozilla.fenix.nimbus.FxNimbus
 import org.mozilla.fenix.perf.MarkersFragmentLifecycleCallbacks
 import org.mozilla.fenix.search.toolbar.DefaultSearchSelectorController
 import org.mozilla.fenix.search.toolbar.SearchSelectorMenu
 import org.mozilla.fenix.tabstray.Page
 import org.mozilla.fenix.tabstray.TabsTrayAccessPoint
 import org.mozilla.fenix.theme.FirefoxTheme
+import org.mozilla.fenix.tor.CampaignStrings
 import org.mozilla.fenix.utils.Settings.Companion.TOP_SITES_PROVIDER_MAX_THRESHOLD
 import org.mozilla.fenix.utils.allowUndo
 import org.mozilla.fenix.wallpapers.Wallpaper
@@ -165,6 +198,9 @@ import java.lang.ref.WeakReference
 import org.mozilla.fenix.GleanMetrics.TabStrip as TabStripMetrics
 
 import org.mozilla.fenix.tor.UrlQuickLoadViewModel
+import java.text.SimpleDateFormat
+import java.time.LocalDate
+import java.util.Date
 
 @Suppress("TooManyFunctions", "LargeClass")
 class HomeFragment : Fragment(), UserInteractionHandler {
@@ -507,6 +543,8 @@ class HomeFragment : Fragment(), UserInteractionHandler {
 
         activity.themeManager.applyStatusBarTheme(activity)
 
+        tryShowUX2025Survey()
+
         // FxNimbus.features.homescreen.recordExposure()
 
         // DO NOT MOVE ANYTHING BELOW THIS addMarker CALL!
@@ -518,6 +556,31 @@ class HomeFragment : Fragment(), UserInteractionHandler {
         return binding.root
     }
 
+    private fun tryShowUX2025Survey() {
+        val allowedLocales = arrayListOf("en", "es", "ru", "fr", "pt")
+        val locale = CampaignStrings.getLocale()
+
+        val dateFormat = SimpleDateFormat("yyyy-MM-dd-hh-zzz")
+        val startDate = dateFormat.parse("2025-04-14-12-UTC")
+
+        val endDate = dateFormat.parse("2025-04-28-00-UTC")
+        val currentDate = Date()
+
+        if (currentDate.before(startDate) || currentDate.after(endDate)) {
+            return // comment out to test
+        }
+
+        if (allowedLocales.contains(locale) && !requireContext().settings().hideCampaign) {
+            binding.onionPatternImage.visibility = View.GONE
+            binding.campaignBox.apply {
+                setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+                setContent {
+                    CampaignBox()
+                }
+            }
+        }
+    }
+
     private fun reinitializeNavBar() {
         initializeNavBar(activity = requireActivity() as HomeActivity)
     }
@@ -1396,4 +1459,213 @@ class HomeFragment : Fragment(), UserInteractionHandler {
     override fun onBackPressed(): Boolean {
         (requireActivity() as HomeActivity).shutDown()
     }
+
+    @Composable
+    fun CampaignBox() {
+        BoxWithConstraints(
+            contentAlignment = Alignment.Center,
+            modifier = Modifier
+                .fillMaxWidth()
+                .wrapContentHeight()
+        ) {
+            val alternateLayout = this.maxWidth >= 500.dp
+
+            CampaignLayout(
+                alternateLayout,
+                maxWidth = this.maxWidth,
+                modifier = Modifier
+                    .padding(top = if (alternateLayout) 65.dp else 55.dp, bottom = 56.dp),
+            )
+        }
+    }
+
+    @Composable
+    private fun CampaignLayout(
+        alternateLayout: Boolean,
+        maxWidth: Dp,
+        modifier: Modifier
+    ) {
+        Column(
+            modifier = modifier
+                .padding(horizontal = 22.dp)
+                .verticalScroll(rememberScrollState())
+                .fillMaxWidth(getVariableWidth(maxWidth)),
+            horizontalAlignment = Alignment.CenterHorizontally,
+        ) {
+            PurpleBox(alternateLayout)
+        }
+    }
+
+    private fun getVariableWidth(width: Dp): Float = (500.dp / width).coerceIn(0.75f, 1.0f)
+
+    @Composable
+    private fun PurpleBox(
+        alternateLayout: Boolean,
+    ) {
+        Box(
+            modifier = Modifier.background(PhotonColors.Violet90, shape = RoundedCornerShape(8.dp))
+                .padding(16.dp),
+        ) {
+            Row(
+                modifier = Modifier.fillMaxWidth(),
+            ) {
+                Emoji()
+                Spacer(Modifier.weight(1f))
+                ExitIcon()
+            }
+            DynamicCampaignContent(alternateLayout)
+        }
+    }
+
+    @Composable
+    private fun Emoji() {
+        val alpha38Violet40 = Color(PhotonColors.Violet40.red, PhotonColors.Violet40.green, PhotonColors.Violet40.blue, 0.38f)
+        Image(
+            painter = painterResource(id = R.drawable.campaign_hand),
+            contentDescription = null,
+            modifier = Modifier
+                .size(64.dp)
+                .padding(16.dp)
+                .drawBehind {
+                    drawCircle(
+                        color = alpha38Violet40,
+                        radius = this.size.maxDimension
+                    )
+                }
+        )
+    }
+
+    @Composable
+    private fun ExitIcon() {
+        IconButton(
+            onClick = {
+                binding.campaignBox.visibility = View.GONE
+                binding.onionPatternImage.visibility = View.VISIBLE
+                context?.components?.settings?.hideCampaign = true
+            },
+        ) {
+            Icon(
+                painter = painterResource(id = R.drawable.ic_close),
+                tint = Color(
+                    getColor(
+                        requireContext(),
+                        R.color.photonWhite,
+                    ),
+                ),
+                contentDescription = CampaignStrings.get(CampaignStrings.CloseKey),
+                modifier = Modifier
+                    .size(48.dp)
+                    .padding(8.dp)
+            )
+        }
+    }
+
+
+    @Composable
+    private fun DynamicCampaignContent(
+        alternateLayout: Boolean
+    ) {
+        Row(verticalAlignment = Alignment.CenterVertically) {
+            Column(
+                modifier = Modifier.fillMaxWidth()
+                    .padding( top = 88.dp),
+                horizontalAlignment = Alignment.Start,
+            ) {
+                TitleText()
+                MainText()
+
+                if (alternateLayout) {
+                    Row(modifier = Modifier.fillMaxWidth()) {
+                        Button1(alternateLayout)
+                        Button2()
+                    }
+                } else {
+                    Button1(alternateLayout)
+                    Button2()
+                }
+
+            }
+        }
+    }
+
+    @Composable
+    private fun TitleText() {
+
+        Text(text = CampaignStrings.get(CampaignStrings.HeaderKey),
+            color = PhotonColors.LightGrey05,
+            textAlign = TextAlign.Left,
+            fontWeight = FontWeight.Bold,
+            fontSize = 24.sp,
+            lineHeight = 34.sp,
+            modifier =  Modifier.padding(bottom = 16.dp)
+        )
+    }
+
+    @Composable
+    private fun MainText() {
+
+        Text(text =  CampaignStrings.get(CampaignStrings.BodyKey),
+            modifier = Modifier
+                .fillMaxWidth()
+                .padding(
+                    start = 0.dp,
+                    end = 0.dp,
+                    bottom = 18.dp,
+                ),
+            color = PhotonColors.LightGrey05,
+            fontSize = 18.sp,
+            textAlign = TextAlign.Left,
+            )
+    }
+
+    @Composable
+    private fun Button1(alternateLayout: Boolean) {
+        Button(
+            onClick = {
+                var locale = CampaignStrings.getLocale()
+                if (locale == "pt") {
+                    locale = "pt-BR"
+                }
+                (activity as HomeActivity).openToBrowserAndLoad(
+                    searchTermOrURL = "https://survey.torproject.org/index.php/923269?lang=${locale}",
+                    newTab = true,
+                    from = BrowserDirection.FromHome,
+                )
+            },
+            colors = ButtonDefaults.buttonColors(
+                backgroundColor = PhotonColors.Violet60),
+            shape = RoundedCornerShape(4.dp),
+            modifier = Modifier.padding(0.dp)
+                .fillMaxWidth(fraction = if (alternateLayout) 0.5f else 1f),
+
+        ) {
+            Text(text = CampaignStrings.get(CampaignStrings.CTAKey),
+                color = PhotonColors.LightGrey05,
+                textAlign = TextAlign.Center,
+                fontSize = 18.sp,
+                modifier = Modifier.padding(8.dp))
+        }
+    }
+
+    @Composable
+    private fun Button2() {
+        Button(
+            onClick = {
+                binding.campaignBox.visibility = View.GONE
+                binding.onionPatternImage.visibility = View.VISIBLE
+                context?.components?.settings?.hideCampaign = true
+            },
+            colors = ButtonDefaults.buttonColors(
+            backgroundColor = PhotonColors.Violet90),
+            shape = RoundedCornerShape(4.dp),
+            modifier = Modifier.padding(0.dp)
+                .fillMaxWidth()
+        ) {
+            Text(text = CampaignStrings.get(CampaignStrings.DismissKey),
+            color = PhotonColors.Violet20,
+            textAlign = TextAlign.Center,
+            fontSize = 18.sp,
+            modifier = Modifier.padding(8.dp))
+        }
+    }
 }
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/CampaignStrings.kt
=====================================
@@ -0,0 +1,64 @@
+package org.mozilla.fenix.tor
+
+import java.util.Locale
+
+object CampaignStrings {
+
+    val HeaderKey = "key_header"
+    val BodyKey = "key_body"
+    val CTAKey = "key_cta"
+    val DismissKey = "key_dismiss"
+    val CloseKey = "key_close"
+
+    private val translations: HashMap<String, HashMap<String, String>> = hashMapOf(
+        "en" to hashMapOf(
+            HeaderKey to "We’d love your feedback",
+            BodyKey to "Help us improve Tor Browser by completing this 10-minute survey.",
+            CTAKey to "Launch the survey",
+            DismissKey to "Dismiss",
+            CloseKey to "Close",
+        ),
+        "es" to hashMapOf(
+            HeaderKey to "Danos tu opinión",
+            BodyKey to "Ayúdanos a mejorar el Navegador Tor completando esta encuesta de 10 minutos.",
+            CTAKey to "Iniciar la encuesta",
+            DismissKey to "Cerrar",
+            CloseKey to "Cerrar",
+        ),
+        "ru" to hashMapOf(
+            HeaderKey to "Мы будем рады вашим отзывам",
+            BodyKey to "Помогите нам улучшить браузер Tor, пройдя 10-минутный опрос.",
+            CTAKey to "Начать опрос",
+            DismissKey to "Закрыть",
+            CloseKey to "Закрыть",
+        ),
+        "fr" to hashMapOf(
+            HeaderKey to "Nous serions ravis d’avoir votre avis !",
+            BodyKey to "Aidez-nous à améliorer le navigateur Tor en répondant à cette enquête de 10 minutes.",
+            CTAKey to "Lancer l'enquête",
+            DismissKey to "Fermer",
+            CloseKey to "Fermer",
+        ),
+        "pt" to hashMapOf(
+            HeaderKey to "Adoraríamos ouvir sua opinião",
+            BodyKey to "Ajude-nos a melhorar o Navegador Tor respondendo a esta pesquisa de 10 minutos.",
+            CTAKey to "Iniciar a pesquisa",
+            DismissKey to "Dispensar",
+            CloseKey to "Fechar"
+        ),
+    )
+
+    fun getLocale(): String {
+        // TODO: do we care about spoofEnglish setting?
+        return Locale.getDefault().getLanguage();
+    }
+
+
+    fun get(key: String): String {
+        val localeStrings = translations.get(getLocale())
+        if (localeStrings == null) {
+            return ""
+        }
+        return localeStrings.get(key) ?: ""
+    }
+}
=====================================
mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt
=====================================
@@ -43,15 +43,7 @@ import org.mozilla.fenix.components.settings.lazyFeatureFlagPreference
 import org.mozilla.fenix.components.toolbar.ToolbarPosition
 import org.mozilla.fenix.ext.components
 import org.mozilla.fenix.ext.getPreferenceKey
-import org.mozilla.fenix.nimbus.CookieBannersSection
 import org.mozilla.fenix.nimbus.FxNimbus
-import org.mozilla.fenix.nimbus.HomeScreenSection
-import org.mozilla.fenix.nimbus.Mr2022Section
-import org.mozilla.fenix.nimbus.QueryParameterStrippingSection
-import org.mozilla.fenix.nimbus.QueryParameterStrippingSection.QUERY_PARAMETER_STRIPPING
-import org.mozilla.fenix.nimbus.QueryParameterStrippingSection.QUERY_PARAMETER_STRIPPING_ALLOW_LIST
-import org.mozilla.fenix.nimbus.QueryParameterStrippingSection.QUERY_PARAMETER_STRIPPING_PMB
-import org.mozilla.fenix.nimbus.QueryParameterStrippingSection.QUERY_PARAMETER_STRIPPING_STRIP_LIST
 import org.mozilla.fenix.settings.PhoneFeature
 import org.mozilla.fenix.settings.deletebrowsingdata.DeleteBrowsingDataOnQuitType
 import org.mozilla.fenix.settings.logins.SavedLoginsSortingStrategyMenu
@@ -2116,4 +2108,9 @@ class Settings(private val appContext: Context) : PreferencesHolder {
         appContext.getPreferenceKey(R.string.pref_key_quick_start),
         default = false,
     )
+
+    var hideCampaign by booleanPreference(
+        appContext.getPreferenceKey(R.string.pref_key_hide_campaign_2025_ux_survey),
+        default = false,
+    )
 }
=====================================
mobile/android/fenix/app/src/main/res/drawable/campaign_hand.xml
=====================================
@@ -0,0 +1,21 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
+    android:viewportWidth="36"
+    android:viewportHeight="36"
+    android:width="36dp"
+    android:height="36dp">
+    <path
+        android:pathData="M4.861 9.147c0.94 -0.657 2.357 -0.531 3.201 0.166l-0.968 -1.407c-0.779 -1.111 -0.5 -2.313 0.612 -3.093 1.112 -0.777 4.263 1.312 4.263 1.312 -0.786 -1.122 -0.639 -2.544 0.483 -3.331 1.122 -0.784 2.67 -0.513 3.456 0.611l10.42 14.72L25 31l-11.083 -4.042L4.25 12.625c-0.793 -1.129 -0.519 -2.686 0.611 -3.478z"
+        android:fillColor="#EF9645" />
+    <path
+        android:pathData="M2.695 17.336s-1.132 -1.65 0.519 -2.781c1.649 -1.131 2.78 0.518 2.78 0.518l5.251 7.658c0.181 -0.302 0.379 -0.6 0.6 -0.894L4.557 11.21s-1.131 -1.649 0.519 -2.78c1.649 -1.131 2.78 0.518 2.78 0.518l6.855 9.997c0.255 -0.208 0.516 -0.417 0.785 -0.622L7.549 6.732s-1.131 -1.649 0.519 -2.78c1.649 -1.131 2.78 0.518 2.78 0.518l7.947 11.589c0.292 -0.179 0.581 -0.334 0.871 -0.498L12.238 4.729s-1.131 -1.649 0.518 -2.78c1.649 -1.131 2.78 0.518 2.78 0.518l7.854 11.454 1.194 1.742c-4.948 3.394 -5.419 9.779 -2.592 13.902 0.565 0.825 1.39 0.26 1.39 0.26 -3.393 -4.949 -2.357 -10.51 2.592 -13.903L24.515 8.62s-0.545 -1.924 1.378 -2.47c1.924 -0.545 2.47 1.379 2.47 1.379l1.685 5.004c0.668 1.984 1.379 3.961 2.32 5.831 2.657 5.28 1.07 11.842 -3.94 15.279 -5.465 3.747 -12.936 2.354 -16.684 -3.11L2.695 17.336z"
+        android:fillColor="#FFDC5D" />
+    <path
+        android:pathData="M12 32.042C8 32.042 3.958 28 3.958 24c0 -0.553 -0.405 -1 -0.958 -1s-1.042 0.447 -1.042 1C1.958 30 6 34.042 12 34.042c0.553 0 1 -0.489 1 -1.042s-0.447 -0.958 -1 -0.958z"
+        android:fillColor="#5DADEC" />
+    <path
+        android:pathData="M7 34c-3 0 -5 -2 -5 -5 0 -0.553 -0.447 -1 -1 -1s-1 0.447 -1 1c0 4 3 7 7 7 0.553 0 1 -0.447 1 -1s-0.447 -1 -1 -1zM24 2c-0.552 0 -1 0.448 -1 1s0.448 1 1 1c4 0 8 3.589 8 8 0 0.552 0.448 1 1 1s1 -0.448 1 -1c0 -5.514 -4 -10 -10 -10z"
+        android:fillColor="#5DADEC" />
+    <path
+        android:pathData="M29 0.042c-0.552 0 -1 0.406 -1 0.958s0.448 1.042 1 1.042c3 0 4.958 2.225 4.958 4.958 0 0.552 0.489 1 1.042 1s0.958 -0.448 0.958 -1C35.958 3.163 33 0.042 29 0.042z"
+        android:fillColor="#5DADEC" />
+</vector>
\ No newline at end of file
=====================================
mobile/android/fenix/app/src/main/res/layout/fragment_home.xml
=====================================
@@ -134,6 +134,12 @@
 
     </com.google.android.material.appbar.AppBarLayout>
 
+    <androidx.compose.ui.platform.ComposeView
+        android:id="@+id/campaignBox"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"/>
+
     <androidx.recyclerview.widget.RecyclerView
         android:id="@+id/sessionControlRecyclerView"
         android:layout_width="match_parent"
=====================================
mobile/android/fenix/app/src/main/res/values/preference_keys.xml
=====================================
@@ -430,4 +430,5 @@
     <string name="pref_key_tor_network_settings_bridges_enabled">pref_key_tor_network_settings_bridges_enabled</string>
 
     <string name="pref_key_spoof_english" translatable="false">pref_key_spoof_english</string>
+    <string name="pref_key_hide_campaign_2025_ux_survey" translatable="false">pref_key_hide_campaign_2025_ux_survey_test</string>
 </resources>
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/b23a82…
-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/b23a82…
You're receiving this email because of your account on gitlab.torproject.org.
                    
                  
                  
                          
                            
                            1
                            
                          
                          
                            
                            0