morgan pushed to branch tor-browser-128.5.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
- 
938f751c
by clairehurst at 2024-12-04T11:50:34-07:00
9 changed files:
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.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/TorControllerGV.kt
- mobile/android/fenix/app/src/main/res/layout/fenix_snackbar.xml
- mobile/android/fenix/app/src/main/res/navigation/nav_graph.xml
- mobile/android/fenix/app/src/main/res/values/torbrowser_strings.xml
Changes:
| ... | ... | @@ -25,6 +25,7 @@ import android.view.MotionEvent | 
| 25 | 25 |  import android.view.View
 | 
| 26 | 26 |  import android.view.ViewConfiguration
 | 
| 27 | 27 |  import android.view.WindowManager.LayoutParams.FLAG_SECURE
 | 
| 28 | +import androidx.activity.viewModels
 | |
| 28 | 29 |  import androidx.annotation.CallSuper
 | 
| 29 | 30 |  import androidx.annotation.IdRes
 | 
| 30 | 31 |  import androidx.annotation.RequiresApi
 | 
| ... | ... | @@ -32,7 +33,6 @@ import androidx.annotation.VisibleForTesting | 
| 32 | 33 |  import androidx.appcompat.app.ActionBar
 | 
| 33 | 34 |  import androidx.appcompat.widget.Toolbar
 | 
| 34 | 35 |  import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
 | 
| 35 | -import androidx.core.content.ContentProviderCompat.requireContext
 | |
| 36 | 36 |  import androidx.lifecycle.lifecycleScope
 | 
| 37 | 37 |  import androidx.navigation.NavController
 | 
| 38 | 38 |  import androidx.navigation.fragment.NavHostFragment
 | 
| ... | ... | @@ -151,10 +151,10 @@ import org.mozilla.fenix.utils.Settings | 
| 151 | 151 |  import java.lang.ref.WeakReference
 | 
| 152 | 152 |  import java.util.Locale
 | 
| 153 | 153 | |
| 154 | -import androidx.navigation.fragment.findNavController
 | |
| 155 | 154 |  import mozilla.components.browser.engine.gecko.GeckoEngine
 | 
| 156 | -import mozilla.components.browser.state.selector.findCustomTab
 | |
| 155 | +import org.mozilla.fenix.components.FenixSnackbar
 | |
| 157 | 156 |  import org.mozilla.fenix.home.HomeFragment
 | 
| 157 | +import org.mozilla.fenix.tor.TorConnectionAssistViewModel
 | |
| 158 | 158 |  import org.mozilla.geckoview.TorIntegrationAndroid
 | 
| 159 | 159 | |
| 160 | 160 |  /**
 | 
| ... | ... | @@ -238,6 +238,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, TorIn | 
| 238 | 238 | |
| 239 | 239 |      private var dialog: RedirectDialogFragment? = null
 | 
| 240 | 240 | |
| 241 | +    private val torConnectionAssistViewModel: TorConnectionAssistViewModel by viewModels()
 | |
| 242 | + | |
| 241 | 243 |      @Suppress("ComplexMethod")
 | 
| 242 | 244 |      final override fun onCreate(savedInstanceState: Bundle?) {
 | 
| 243 | 245 |          // DO NOT MOVE ANYTHING ABOVE THIS getProfilerTime CALL.
 | 
| ... | ... | @@ -1115,6 +1117,25 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, TorIn | 
| 1115 | 1117 |          historyMetadata: HistoryMetadataKey? = null,
 | 
| 1116 | 1118 |          additionalHeaders: Map<String, String>? = null,
 | 
| 1117 | 1119 |      ) {
 | 
| 1120 | +        if (!components.torController.isBootstrapped && !searchTermOrURL.startsWith("about:")) {
 | |
| 1121 | +            FenixSnackbar.make(
 | |
| 1122 | +                view = binding.root,
 | |
| 1123 | +                isDisplayedWithBrowserToolbar = true,
 | |
| 1124 | +            )
 | |
| 1125 | +                .setText(getString(R.string.connection_assist_connect_to_tor_before_opening_links))
 | |
| 1126 | +                .setAction(getString(R.string.connection_assist_connect_to_tor_before_opening_links_confirmation)) {
 | |
| 1127 | +                    torConnectionAssistViewModel.handleConnect(searchTermOrURL)
 | |
| 1128 | +                    if (navHost.navController.previousBackStackEntry?.destination?.id == R.id.torConnectionAssistFragment) {
 | |
| 1129 | +                        supportFragmentManager.popBackStack()
 | |
| 1130 | +                    } else {
 | |
| 1131 | +                        navHost.navController.navigate(
 | |
| 1132 | +                            TorConnectionAssistFragmentDirections.actionConnectToTorBeforeOpeningLinks()
 | |
| 1133 | +                        )
 | |
| 1134 | +                    }
 | |
| 1135 | +                }
 | |
| 1136 | +                .show()
 | |
| 1137 | +            return
 | |
| 1138 | +        }
 | |
| 1118 | 1139 |          openToBrowser(from, customTabSessionId)
 | 
| 1119 | 1140 |          load(
 | 
| 1120 | 1141 |              searchTermOrURL = searchTermOrURL,
 | 
| ... | ... | @@ -1434,8 +1455,10 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, TorIn | 
| 1434 | 1455 |      override fun onBootstrapStateChange(state: String) = Unit
 | 
| 1435 | 1456 |      override fun onBootstrapProgress(progress: Double, hasWarnings: Boolean) = Unit
 | 
| 1436 | 1457 |      override fun onBootstrapComplete() {
 | 
| 1437 | -        components.useCases.tabsUseCases.removeAllTabs()
 | |
| 1438 | -        navHost.navController.navigate(NavGraphDirections.actionStartupHome())
 | |
| 1458 | +        if (settings().useHtmlConnectionUi) {
 | |
| 1459 | +            components.useCases.tabsUseCases.removeAllTabs()
 | |
| 1460 | +            navHost.navController.navigate(NavGraphDirections.actionStartupHome())
 | |
| 1461 | +        }
 | |
| 1439 | 1462 |      }
 | 
| 1440 | 1463 |      override fun onBootstrapError(code: String?, message: String?, phase: String?, reason: String?) = Unit
 | 
| 1441 | 1464 |  } | 
| ... | ... | @@ -160,6 +160,7 @@ import org.mozilla.fenix.tabstray.TabsTrayAccessPoint | 
| 160 | 160 |  import org.mozilla.fenix.theme.FirefoxTheme
 | 
| 161 | 161 |  import org.mozilla.fenix.tor.TorBootstrapFragmentDirections
 | 
| 162 | 162 |  import org.mozilla.fenix.tor.TorBootstrapStatus
 | 
| 163 | +import org.mozilla.fenix.tor.TorConnectionAssistViewModel
 | |
| 163 | 164 |  import org.mozilla.fenix.utils.Settings.Companion.TOP_SITES_PROVIDER_MAX_THRESHOLD
 | 
| 164 | 165 |  import org.mozilla.fenix.utils.allowUndo
 | 
| 165 | 166 |  import org.mozilla.fenix.wallpapers.Wallpaper
 | 
| ... | ... | @@ -179,6 +180,7 @@ class HomeFragment : Fragment(), UserInteractionHandler { | 
| 179 | 180 |      private val binding get() = _binding!!
 | 
| 180 | 181 | |
| 181 | 182 |      private val homeViewModel: HomeScreenViewModel by activityViewModels()
 | 
| 183 | +    private val torConnectionAssistViewModel: TorConnectionAssistViewModel by activityViewModels()
 | |
| 182 | 184 | |
| 183 | 185 |      private val snackbarAnchorView: View?
 | 
| 184 | 186 |          get() = when (requireContext().settings().toolbarPosition) {
 | 
| ... | ... | @@ -899,6 +901,17 @@ class HomeFragment : Fragment(), UserInteractionHandler { | 
| 899 | 901 |              view = view,
 | 
| 900 | 902 |          )
 | 
| 901 | 903 | |
| 904 | +        torConnectionAssistViewModel.urlToLoadAfterConnecting.also {
 | |
| 905 | +            if(!it.isNullOrBlank()){
 | |
| 906 | +                (requireActivity() as HomeActivity).openToBrowserAndLoad(
 | |
| 907 | +                    searchTermOrURL = it,
 | |
| 908 | +                    newTab = true,
 | |
| 909 | +                    from = BrowserDirection.FromHome,
 | |
| 910 | +                )
 | |
| 911 | +                torConnectionAssistViewModel.urlToLoadAfterConnecting = null // Only load this url once
 | |
| 912 | +            }
 | |
| 913 | +        }
 | |
| 914 | + | |
| 902 | 915 |          // DO NOT MOVE ANYTHING BELOW THIS addMarker CALL!
 | 
| 903 | 916 |          requireComponents.core.engine.profiler?.addMarker(
 | 
| 904 | 917 |              MarkersFragmentLifecycleCallbacks.MARKER_NAME,
 | 
| ... | ... | @@ -25,6 +25,7 @@ import androidx.lifecycle.Observer | 
| 25 | 25 |  import androidx.lifecycle.lifecycleScope
 | 
| 26 | 26 |  import androidx.navigation.NavDirections
 | 
| 27 | 27 |  import androidx.navigation.findNavController
 | 
| 28 | +import androidx.navigation.fragment.findNavController
 | |
| 28 | 29 |  import androidx.navigation.fragment.navArgs
 | 
| 29 | 30 |  import androidx.preference.Preference
 | 
| 30 | 31 |  import androidx.preference.PreferenceFragmentCompat
 | 
| ... | ... | @@ -40,6 +41,7 @@ import mozilla.components.concept.sync.OAuthAccount | 
| 40 | 41 |  import mozilla.components.concept.sync.Profile
 | 
| 41 | 42 |  import mozilla.components.feature.addons.ui.AddonFilePicker
 | 
| 42 | 43 |  import mozilla.components.service.glean.private.NoExtras
 | 
| 44 | +import mozilla.components.support.base.feature.UserInteractionHandler
 | |
| 43 | 45 |  import mozilla.components.support.ktx.android.view.showKeyboard
 | 
| 44 | 46 |  import mozilla.components.ui.widgets.withCenterAlignedButtons
 | 
| 45 | 47 |  import org.mozilla.fenix.BrowserDirection
 | 
| ... | ... | @@ -77,7 +79,7 @@ import kotlin.system.exitProcess | 
| 77 | 79 |  import org.mozilla.fenix.GleanMetrics.Settings as SettingsMetrics
 | 
| 78 | 80 | |
| 79 | 81 |  @Suppress("LargeClass", "TooManyFunctions")
 | 
| 80 | -class SettingsFragment : PreferenceFragmentCompat() {
 | |
| 82 | +class SettingsFragment : PreferenceFragmentCompat(), UserInteractionHandler {
 | |
| 81 | 83 | |
| 82 | 84 |      private val args by navArgs<SettingsFragmentArgs>()
 | 
| 83 | 85 |      private lateinit var accountUiView: AccountUiView
 | 
| ... | ... | @@ -883,4 +885,18 @@ class SettingsFragment : PreferenceFragmentCompat() { | 
| 883 | 885 |          private const val FXA_SYNC_OVERRIDE_EXIT_DELAY = 2000L
 | 
| 884 | 886 |          private const val AMO_COLLECTION_OVERRIDE_EXIT_DELAY = 3000L
 | 
| 885 | 887 |      }
 | 
| 888 | + | |
| 889 | +    override fun onBackPressed(): Boolean {
 | |
| 890 | +        // If tor is already bootstrapped, skip going back to [TorConnectionAssistFragment] and instead go directly to [HomeFragment]
 | |
| 891 | +        if (requireComponents.torController.isBootstrapped) {
 | |
| 892 | +            val navController = findNavController()
 | |
| 893 | +            if (navController.previousBackStackEntry?.destination?.id == R.id.torConnectionAssistFragment) {
 | |
| 894 | +                navController.navigate(
 | |
| 895 | +                    SettingsFragmentDirections.actionGlobalHomeFragment(),
 | |
| 896 | +                )
 | |
| 897 | +                return true
 | |
| 898 | +            }
 | |
| 899 | +        }
 | |
| 900 | +        return false
 | |
| 901 | +    }
 | |
| 886 | 902 |  } | 
| ... | ... | @@ -19,7 +19,7 @@ import android.view.View | 
| 19 | 19 |  import android.view.ViewGroup
 | 
| 20 | 20 |  import androidx.appcompat.content.res.AppCompatResources
 | 
| 21 | 21 |  import androidx.fragment.app.Fragment
 | 
| 22 | -import androidx.fragment.app.viewModels
 | |
| 22 | +import androidx.fragment.app.activityViewModels
 | |
| 23 | 23 |  import androidx.lifecycle.Lifecycle
 | 
| 24 | 24 |  import androidx.lifecycle.lifecycleScope
 | 
| 25 | 25 |  import androidx.lifecycle.repeatOnLifecycle
 | 
| ... | ... | @@ -34,7 +34,7 @@ import org.mozilla.fenix.ext.hideToolbar | 
| 34 | 34 |  class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
 | 
| 35 | 35 | |
| 36 | 36 |      private val TAG = "TorConnectionAssistFrag"
 | 
| 37 | -    private val viewModel: TorConnectionAssistViewModel by viewModels()
 | |
| 37 | +    private val viewModel: TorConnectionAssistViewModel by activityViewModels()
 | |
| 38 | 38 |      private var _binding: FragmentTorConnectionAssistBinding? = null
 | 
| 39 | 39 |      private val binding get() = _binding!!
 | 
| 40 | 40 | |
| ... | ... | @@ -46,6 +46,11 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { | 
| 46 | 46 |          _binding = FragmentTorConnectionAssistBinding.inflate(
 | 
| 47 | 47 |              inflater, container, false,
 | 
| 48 | 48 |          )
 | 
| 49 | +        viewLifecycleOwner.lifecycleScope.launch {
 | |
| 50 | +            repeatOnLifecycle(Lifecycle.State.STARTED) {
 | |
| 51 | +                viewModel.collectLastKnownStatus()
 | |
| 52 | +            }
 | |
| 53 | +        }
 | |
| 49 | 54 | |
| 50 | 55 |          viewLifecycleOwner.lifecycleScope.launch {
 | 
| 51 | 56 |              repeatOnLifecycle(Lifecycle.State.STARTED) {
 | 
| ... | ... | @@ -62,7 +67,6 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { | 
| 62 | 67 |      override fun onResume() {
 | 
| 63 | 68 |          super.onResume()
 | 
| 64 | 69 |          hideToolbar()
 | 
| 65 | -        viewModel.handleTorConnectStateToScreen() // Covers the case where the app is backgrounded when the bootstrap finishes
 | |
| 66 | 70 |      }
 | 
| 67 | 71 | |
| 68 | 72 |      override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
| ... | ... | @@ -71,11 +75,7 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { | 
| 71 | 75 |          viewModel.progress().observe(
 | 
| 72 | 76 |              viewLifecycleOwner,
 | 
| 73 | 77 |          ) { progress ->
 | 
| 74 | -            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
 | |
| 75 | -                binding.torBootstrapProgressBar.setProgress(progress, true)
 | |
| 76 | -            } else {
 | |
| 77 | -                binding.torBootstrapProgressBar.progress = progress
 | |
| 78 | -            }
 | |
| 78 | +            setProgressBarCompat(progress)
 | |
| 79 | 79 |          }
 | 
| 80 | 80 | |
| 81 | 81 |          viewModel.quickstartToggle().observe(
 | 
| ... | ... | @@ -95,6 +95,14 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { | 
| 95 | 95 | |
| 96 | 96 |      }
 | 
| 97 | 97 | |
| 98 | +    private fun setProgressBarCompat(progress: Int) {
 | |
| 99 | +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
 | |
| 100 | +            binding.torBootstrapProgressBar.setProgress(progress, true)
 | |
| 101 | +        } else {
 | |
| 102 | +            binding.torBootstrapProgressBar.progress = progress
 | |
| 103 | +        }
 | |
| 104 | +    }
 | |
| 105 | + | |
| 98 | 106 |      private fun showScreen(screen: ConnectAssistUiState) {
 | 
| 99 | 107 |          setProgressBar(screen)
 | 
| 100 | 108 |          setSettingsButton(screen)
 | 
| ... | ... | @@ -269,7 +277,7 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { | 
| 269 | 277 | |
| 270 | 278 |      private fun openHome() {
 | 
| 271 | 279 |          Log.d(TAG, "openHome()")
 | 
| 272 | -        findNavController().navigate(TorConnectionAssistFragmentDirections.actionStartupHome())
 | |
| 280 | +        viewModel.openHome(findNavController())
 | |
| 273 | 281 |      }
 | 
| 274 | 282 | |
| 275 | 283 |      private fun openSettings(preferenceToScrollTo: String? = null) {
 | 
| ... | ... | @@ -10,6 +10,7 @@ import androidx.lifecycle.AndroidViewModel | 
| 10 | 10 |  import androidx.lifecycle.LifecycleCoroutineScope
 | 
| 11 | 11 |  import androidx.lifecycle.LiveData
 | 
| 12 | 12 |  import androidx.lifecycle.MutableLiveData
 | 
| 13 | +import androidx.navigation.NavController
 | |
| 13 | 14 |  import kotlinx.coroutines.flow.MutableStateFlow
 | 
| 14 | 15 |  import kotlinx.coroutines.flow.StateFlow
 | 
| 15 | 16 |  import org.mozilla.fenix.ext.components
 | 
| ... | ... | @@ -44,20 +45,25 @@ class TorConnectionAssistViewModel( | 
| 44 | 45 |      }
 | 
| 45 | 46 | |
| 46 | 47 |      init {
 | 
| 47 | -        Log.d(TAG, "initiating TorConnectionAssistViewModel")
 | |
| 48 | +        Log.d(TAG, "initiating TorConnectionAssistViewModel $this")
 | |
| 48 | 49 |          _torController.registerTorListener(this)
 | 
| 49 | -        handleTorConnectStateToScreen() // should cover the case of when we have an onBootStrapStateChange() event before this is initialized, which lead to being stuck on the splash screen
 | |
| 50 | 50 |      }
 | 
| 51 | 51 | |
| 52 | -    private fun handleConnect(
 | |
| 52 | +    var urlToLoadAfterConnecting: String? = null
 | |
| 53 | + | |
| 54 | +    fun handleConnect(
 | |
| 55 | +        urlToLoadAfterConnecting: String? = null,
 | |
| 53 | 56 |          withDebugLogging: Boolean = false,
 | 
| 54 | 57 |          lifecycleScope: LifecycleCoroutineScope? = null,
 | 
| 55 | 58 |      ) {
 | 
| 56 | -        Log.d(TAG, "handleConnect initiatingTorBootstrap with lifecycleScope = $lifecycleScope")
 | |
| 57 | -        _torController.initiateTorBootstrap(
 | |
| 58 | -            withDebugLogging = withDebugLogging,
 | |
| 59 | -            lifecycleScope = lifecycleScope,
 | |
| 60 | -        )
 | |
| 59 | +        this.urlToLoadAfterConnecting = urlToLoadAfterConnecting
 | |
| 60 | +        if (_torController.lastKnownStatus.value.isOff()) {
 | |
| 61 | +            Log.d(TAG, "handleConnect() triggered, initiatingTorBootstrap")
 | |
| 62 | +            _torController.initiateTorBootstrap(
 | |
| 63 | +                withDebugLogging = withDebugLogging,
 | |
| 64 | +                lifecycleScope = lifecycleScope,
 | |
| 65 | +            )
 | |
| 66 | +        }
 | |
| 61 | 67 |      }
 | 
| 62 | 68 | |
| 63 | 69 |      fun handleQuickstartChecked(checked: Boolean) {
 | 
| ... | ... | @@ -96,18 +102,19 @@ class TorConnectionAssistViewModel( | 
| 96 | 102 |              _progress.value = progress.toInt()
 | 
| 97 | 103 |          }
 | 
| 98 | 104 | |
| 99 | -        handleTorConnectStateToScreen()
 | |
| 100 | 105 |      }
 | 
| 101 | 106 | |
| 102 | -    fun handleTorConnectStateToScreen() {
 | |
| 103 | -        when (_torController.lastKnownStatus) {
 | |
| 104 | -            TorConnectState.Initial -> _torConnectScreen.value = ConnectAssistUiState.Splash
 | |
| 105 | -            TorConnectState.Configuring -> handleConfiguring()
 | |
| 106 | -            TorConnectState.AutoBootstrapping -> handleBootstrap()
 | |
| 107 | -            TorConnectState.Bootstrapping -> handleBootstrap()
 | |
| 108 | -            TorConnectState.Bootstrapped -> _shouldOpenHome.value = true
 | |
| 109 | -            TorConnectState.Disabled -> _shouldOpenHome.value = true
 | |
| 110 | -            TorConnectState.Error -> handleError()
 | |
| 107 | +    suspend fun collectLastKnownStatus() {
 | |
| 108 | +        _torController.lastKnownStatus.collect {
 | |
| 109 | +            when (it) {
 | |
| 110 | +                TorConnectState.Initial -> _torConnectScreen.value = ConnectAssistUiState.Splash
 | |
| 111 | +                TorConnectState.Configuring -> handleConfiguring()
 | |
| 112 | +                TorConnectState.AutoBootstrapping -> handleBootstrap()
 | |
| 113 | +                TorConnectState.Bootstrapping -> handleBootstrap()
 | |
| 114 | +                TorConnectState.Bootstrapped -> _shouldOpenHome.value = true
 | |
| 115 | +                TorConnectState.Disabled -> _shouldOpenHome.value = true
 | |
| 116 | +                TorConnectState.Error -> handleError()
 | |
| 117 | +            }
 | |
| 111 | 118 |          }
 | 
| 112 | 119 |      }
 | 
| 113 | 120 | |
| ... | ... | @@ -254,4 +261,10 @@ class TorConnectionAssistViewModel( | 
| 254 | 261 |          }
 | 
| 255 | 262 |          return true
 | 
| 256 | 263 |      }
 | 
| 264 | + | |
| 265 | +    fun openHome(navController: NavController) {
 | |
| 266 | +        navController.navigate(
 | |
| 267 | +            TorConnectionAssistFragmentDirections.actionHome(),
 | |
| 268 | +        )
 | |
| 269 | +    }
 | |
| 257 | 270 |  } | 
| ... | ... | @@ -4,6 +4,8 @@ package org.mozilla.fenix.tor | 
| 4 | 4 |  import android.content.Context
 | 
| 5 | 5 |  import android.util.Log
 | 
| 6 | 6 |  import androidx.lifecycle.LifecycleCoroutineScope
 | 
| 7 | +import kotlinx.coroutines.flow.MutableStateFlow
 | |
| 8 | +import kotlinx.coroutines.flow.StateFlow
 | |
| 7 | 9 |  import mozilla.components.browser.engine.gecko.GeckoEngine
 | 
| 8 | 10 |  import org.mozilla.fenix.ext.components
 | 
| 9 | 11 |  import org.mozilla.geckoview.TorIntegrationAndroid
 | 
| ... | ... | @@ -54,20 +56,22 @@ class TorControllerGV( | 
| 54 | 56 |      private var torListeners = mutableListOf<TorEvents>()
 | 
| 55 | 57 |      private var torLogListeners = mutableListOf<TorLogs>()
 | 
| 56 | 58 | |
| 57 | -    internal var lastKnownStatus = TorConnectState.Initial
 | |
| 59 | +    private val _lastKnownStatus = MutableStateFlow(TorConnectState.Initial)
 | |
| 60 | +    internal val lastKnownStatus: StateFlow<TorConnectState> = _lastKnownStatus
 | |
| 61 | + | |
| 58 | 62 |      internal var lastKnownError: TorError? = null
 | 
| 59 | 63 |      private var wasTorBootstrapped = false
 | 
| 60 | 64 |      private var isTorRestarting = false
 | 
| 61 | 65 | |
| 62 | 66 |      private var isTorBootstrapped = false
 | 
| 63 | -        get() = ((lastKnownStatus.isStarted()) && wasTorBootstrapped)
 | |
| 67 | +        get() = ((_lastKnownStatus.value.isStarted()) && wasTorBootstrapped)
 | |
| 64 | 68 | |
| 65 | 69 |      private val entries = mutableListOf<Pair<String?, String?>>()
 | 
| 66 | 70 |      override val logEntries get() = entries
 | 
| 67 | -    override val isStarting get() = lastKnownStatus.isStarting()
 | |
| 71 | +    override val isStarting get() = _lastKnownStatus.value.isStarting()
 | |
| 68 | 72 |      override val isRestarting get() = isTorRestarting
 | 
| 69 | 73 |      override val isBootstrapped get() = isTorBootstrapped
 | 
| 70 | -    override val isConnected get() = (lastKnownStatus.isStarted() && !isTorRestarting)
 | |
| 74 | +    override val isConnected get() = (_lastKnownStatus.value.isStarted() && !isTorRestarting)
 | |
| 71 | 75 | |
| 72 | 76 |      override var quickstart: Boolean
 | 
| 73 | 77 |          get() {
 | 
| ... | ... | @@ -267,13 +271,13 @@ class TorControllerGV( | 
| 267 | 271 |      }
 | 
| 268 | 272 | |
| 269 | 273 |      override fun setTorStopped() {
 | 
| 270 | -        lastKnownStatus = TorConnectState.Configuring
 | |
| 274 | +        _lastKnownStatus.value = TorConnectState.Configuring
 | |
| 271 | 275 |          onTorStatusUpdate(null, lastKnownStatus.toString(), 0.0)
 | 
| 272 | 276 |          onTorStopped()
 | 
| 273 | 277 |      }
 | 
| 274 | 278 | |
| 275 | 279 |      override fun restartTor() {
 | 
| 276 | -        if (!lastKnownStatus.isStarted() && wasTorBootstrapped) {
 | |
| 280 | +        if (!_lastKnownStatus.value.isStarted() && wasTorBootstrapped) {
 | |
| 277 | 281 |              // If we aren't started, but we were previously bootstrapped,
 | 
| 278 | 282 |              // then we handle a "restart" request as a "start" restart
 | 
| 279 | 283 |              initiateTorBootstrap()
 | 
| ... | ... | @@ -321,42 +325,22 @@ class TorControllerGV( | 
| 321 | 325 |              }
 | 
| 322 | 326 |          }
 | 
| 323 | 327 | |
| 324 | -        if (lastKnownStatus.isOff() && newState.isStarting()) {
 | |
| 328 | +        if (_lastKnownStatus.value.isOff() && newState.isStarting()) {
 | |
| 325 | 329 |              isTorRestarting = false
 | 
| 326 | 330 |          }
 | 
| 327 | 331 | |
| 328 | -        lastKnownStatus = newState
 | |
| 332 | +        _lastKnownStatus.value = newState
 | |
| 329 | 333 |          onTorStatusUpdate(null, newStateVal, null)
 | 
| 330 | 334 |      }
 | 
| 331 | 335 | |
| 332 | 336 |      // TorEventsBootstrapStateChangeListener
 | 
| 333 | 337 |      override fun onBootstrapProgress(progress: Double, hasWarnings: Boolean) {
 | 
| 334 | -        Log.d(TAG, "onBootstrapProgress($progress, $hasWarnings)")
 | |
| 335 | -	// TODO: onBootstrapProgress should only be used to change the shown
 | |
| 336 | -	// bootstrap percentage or a Tor log option during a "Bootstrapping"
 | |
| 337 | -	// stage.
 | |
| 338 | -	// The progress value should not be used to change the `lastKnownStatus`
 | |
| 339 | -	// value or determine if a bootstrap has started or completed. The
 | |
| 340 | -	// TorConnectStage should be used instead.
 | |
| 341 | -        if (progress == 100.0) {
 | |
| 342 | -            lastKnownStatus = TorConnectState.Bootstrapped
 | |
| 343 | -            wasTorBootstrapped = true
 | |
| 344 | -            onTorConnected()
 | |
| 345 | -        } else if (lastKnownStatus == TorConnectState.Bootstrapping) {
 | |
| 346 | -            onTorConnecting()
 | |
| 347 | -        }
 | |
| 348 | -        onTorStatusUpdate("", lastKnownStatus.toTorStatus().status, progress)
 | |
| 338 | +        Log.d(TAG, "onBootstrapProgress(progress = $progress, hasWarnings = $hasWarnings)")
 | |
| 339 | +        onTorStatusUpdate("", _lastKnownStatus.value.toTorStatus().status, progress)
 | |
| 349 | 340 |      }
 | 
| 350 | 341 | |
| 351 | 342 |      // TorEventsBootstrapStateChangeListener
 | 
| 352 | -    override fun onBootstrapComplete() {
 | |
| 353 | -	// TODO: There should be no need to respond to the BootstrapComplete
 | |
| 354 | -	// event if we are already handling TorConnectStage.Bootstrapped.
 | |
| 355 | -	// In particular, `lastKnownStatus` and onTorConnected should be set in
 | |
| 356 | -	// response to a change in TorConnectStage instead.
 | |
| 357 | -        lastKnownStatus = TorConnectState.Bootstrapped
 | |
| 358 | -        this.onTorConnected()
 | |
| 359 | -    }
 | |
| 343 | +    override fun onBootstrapComplete() = Unit
 | |
| 360 | 344 | |
| 361 | 345 |      // TorEventsBootstrapStateChangeListener
 | 
| 362 | 346 |      override fun onBootstrapError(code: String?, message: String?, phase: String?, reason: String?) {
 | 
| ... | ... | @@ -21,6 +21,12 @@ | 
| 21 | 21 |          android:paddingStart="16dp"
 | 
| 22 | 22 |          android:paddingEnd="16dp">
 | 
| 23 | 23 | |
| 24 | +        <!--
 | |
| 25 | +        TextView below changed as part of tor-browser#43229 to match the designs
 | |
| 26 | +        https://www.figma.com/design/vXrWeiV2IlKx5IIZVLtxBX/Android-Components?node-id=1807-3117&t=8Gc1mpPYPQCLMYH2-1 screenshot shown here
 | |
| 27 | +        Line spacing eyeballed from screenshot here
 | |
| 28 | +        https://gitlab.torproject.org/tpo/applications/tor-browser/-/merge_requests/1275#note_3125666
 | |
| 29 | +        -->
 | |
| 24 | 30 |          <TextView
 | 
| 25 | 31 |              android:id="@+id/snackbar_text"
 | 
| 26 | 32 |              android:layout_width="0dp"
 | 
| ... | ... | @@ -29,8 +35,9 @@ | 
| 29 | 35 |              android:letterSpacing="0.05"
 | 
| 30 | 36 |              android:minHeight="46dp"
 | 
| 31 | 37 |              android:maxLines="2"
 | 
| 32 | -            android:paddingTop="8dp"
 | |
| 33 | -            android:paddingBottom="8dp"
 | |
| 38 | +            android:paddingTop="12dp"
 | |
| 39 | +            android:paddingBottom="12dp"
 | |
| 40 | +            android:lineSpacingExtra="8sp"
 | |
| 34 | 41 |              android:textAlignment="textStart"
 | 
| 35 | 42 |              android:textColor="@color/photonWhite"
 | 
| 36 | 43 |              android:textSize="18sp"
 | 
| ... | ... | @@ -27,6 +27,12 @@ | 
| 27 | 27 |          app:popUpTo="@id/startupFragment"
 | 
| 28 | 28 |          app:popUpToInclusive="true" />
 | 
| 29 | 29 | |
| 30 | +    <action
 | |
| 31 | +        android:id="@+id/action_connect_to_tor_before_opening_links"
 | |
| 32 | +        app:destination="@+id/torConnectionAssistFragment"
 | |
| 33 | +        app:popUpTo="@id/torConnectionAssistFragment"
 | |
| 34 | +        app:popUpToInclusive="true"/>
 | |
| 35 | + | |
| 30 | 36 |      <action
 | 
| 31 | 37 |          android:id="@+id/action_global_home"
 | 
| 32 | 38 |          app:destination="@id/homeFragment"
 | 
| ... | ... | @@ -264,7 +270,7 @@ | 
| 264 | 270 |      <fragment
 | 
| 265 | 271 |          android:id="@+id/torConnectionAssistFragment"
 | 
| 266 | 272 |          android:name="org.mozilla.fenix.tor.TorConnectionAssistFragment"
 | 
| 267 | -        tools:layout="@layout/fragment_home">
 | |
| 273 | +        tools:layout="@layout/fragment_tor_connection_assist">
 | |
| 268 | 274 |          <action
 | 
| 269 | 275 |              android:id="@+id/action_home"
 | 
| 270 | 276 |              app:destination="@id/homeFragment"
 | 
| ... | ... | @@ -149,4 +149,31 @@ | 
| 149 | 149 |      <string name="connection_assist_final_error_troubleshoot_connection_link">troubleshooting your connection</string>
 | 
| 150 | 150 |      <!-- Connection assist. -->
 | 
| 151 | 151 |      <string name="connection_assist_final_error_learn_more_link">Learn more</string>
 | 
| 152 | + | |
| 153 | +    <!-- Connection assist. Description for a shown "Snackbar" (special popup notification) with an action to connect -->
 | |
| 154 | +    <string name="connection_assist_connect_to_tor_before_opening_links">Connect to Tor before opening links</string>
 | |
| 155 | +    <!-- Connection assist. Confirmation button for a shown "Snackbar" (special popup notification) that has a previously mentioned description. Automatically shown in ALL CAPS if available, regardless of the localized string -->
 | |
| 156 | +    <string name="connection_assist_connect_to_tor_before_opening_links_confirmation">CONNECT</string>
 | |
| 157 | + | |
| 158 | +    <!-- 2024 YEC. -->
 | |
| 159 | +    <string name="YEC_2024_right_to_speak">You have a right to SPEAK without uninvited listeners.</string>
 | |
| 160 | +    <!-- 2024 YEC. -->
 | |
| 161 | +    <string name="YEC_2024_right_to_BROWSE">You have a right to BROWSE without being watched.</string>
 | |
| 162 | +    <!-- 2024 YEC. -->
 | |
| 163 | +    <string name="YEC_2024_right_to_SEARCH">You have a right to SEARCH without being followed.</string>
 | |
| 164 | + | |
| 165 | +    <!-- 2024 YEC. -->
 | |
| 166 | +    <string name="YEC_2024_donation_encouragement">Join the thousands of Tor supporters building an internet powered by privacy. Make a donation today.</string>
 | |
| 167 | + | |
| 168 | +    <!-- 2024 YEC. -->
 | |
| 169 | +    <string name="YEC_2024_donation_match_text">Through December 31, your gift will be matched, up to $300,000!</string>
 | |
| 170 | + | |
| 171 | +    <!-- 2024 YEC. %1$s is the app name "Tor Browser". Since this will only ever show on release, it will always be "Tor Browser" (and not "Tor Browser Alpha" for instance) -->
 | |
| 172 | +    <string name="YEC_2024_tor_browser_for_android_will_always_be_free_no_donation_required">%1$s for Android will always be free to use—no donation is required to use this app.</string>
 | |
| 173 | + | |
| 174 | +    <!-- 2024 YEC. Accessible name for the "X" button. -->
 | |
| 175 | +    <string name="YEC_2024_close">Close</string>
 | |
| 176 | + | |
| 177 | +    <!-- 2024 YEC. -->
 | |
| 178 | +    <string name="YEC_2024_donate_now">Donate now</string>
 | |
| 152 | 179 |  </resources> |