Dan Ballard pushed to branch tor-browser-128.8.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits: d3a9fcd2 by clairehurst at 2025-03-10T10:52:05-06:00 fixup! [android] Implement Android-native Connection Assist UI
tor-browser#43480
- - - - -
6 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/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
Changes:
===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt ===================================== @@ -155,7 +155,7 @@ import java.util.Locale import mozilla.components.browser.engine.gecko.GeckoEngine import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.home.HomeFragment -import org.mozilla.fenix.tor.TorConnectionAssistViewModel +import org.mozilla.fenix.tor.UrlQuickLoadViewModel import org.mozilla.geckoview.TorAndroidIntegration
/** @@ -237,7 +237,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, TorAn
private var dialog: RedirectDialogFragment? = null
- private val torConnectionAssistViewModel: TorConnectionAssistViewModel by viewModels() + private val urlQuickLoadViewModel: UrlQuickLoadViewModel by viewModels()
@Suppress("ComplexMethod") final override fun onCreate(savedInstanceState: Bundle?) { @@ -1124,7 +1124,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, TorAn .setText(getString(R.string.connection_assist_connect_to_tor_before_opening_links)) .setAllCapsForActionButton(false) .setAction(getString(R.string.connection_assist_connect_to_tor_before_opening_links_confirmation)) { - torConnectionAssistViewModel.handleConnect(searchTermOrURL) + urlQuickLoadViewModel.urlToLoadAfterConnecting.value = searchTermOrURL if (navHost.navController.previousBackStackEntry?.destination?.id == R.id.torConnectionAssistFragment) { supportFragmentManager.popBackStack() } else {
===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt ===================================== @@ -158,13 +158,14 @@ 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.TorConnectionAssistViewModel import org.mozilla.fenix.utils.Settings.Companion.TOP_SITES_PROVIDER_MAX_THRESHOLD import org.mozilla.fenix.utils.allowUndo import org.mozilla.fenix.wallpapers.Wallpaper import java.lang.ref.WeakReference import org.mozilla.fenix.GleanMetrics.TabStrip as TabStripMetrics
+import org.mozilla.fenix.tor.UrlQuickLoadViewModel + @Suppress("TooManyFunctions", "LargeClass") class HomeFragment : Fragment(), UserInteractionHandler { private val args by navArgs<HomeFragmentArgs>() @@ -178,7 +179,7 @@ class HomeFragment : Fragment(), UserInteractionHandler { private val binding get() = _binding!!
private val homeViewModel: HomeScreenViewModel by activityViewModels() - private val torConnectionAssistViewModel: TorConnectionAssistViewModel by activityViewModels() + private val urlQuickLoadViewModel: UrlQuickLoadViewModel by activityViewModels()
private val snackbarAnchorView: View? get() = when (requireContext().settings().toolbarPosition) { @@ -892,14 +893,15 @@ class HomeFragment : Fragment(), UserInteractionHandler { view = view, )
- torConnectionAssistViewModel.urlToLoadAfterConnecting.also { - if(!it.isNullOrBlank()){ + urlQuickLoadViewModel.urlToLoadAfterConnecting.observe(viewLifecycleOwner) { + if (!it.isNullOrBlank()) { (requireActivity() as HomeActivity).openToBrowserAndLoad( searchTermOrURL = it, newTab = true, from = BrowserDirection.FromHome, ) - torConnectionAssistViewModel.urlToLoadAfterConnecting = null // Only load this url once + // Only load this url once + urlQuickLoadViewModel.urlToLoadAfterConnecting.value = null } }
===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorBootstrapProgressViewModel.kt ===================================== @@ -0,0 +1,44 @@ +package org.mozilla.fenix.tor + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import org.mozilla.fenix.ext.components +import org.mozilla.geckoview.TorAndroidIntegration.BootstrapStateChangeListener + +class TorBootstrapProgressViewModel( + application: Application, +) : AndroidViewModel(application), BootstrapStateChangeListener { + + private val torIntegrationAndroid = + application.components.core.geckoRuntime.torIntegrationController + + val progress: MutableLiveData<Int> by lazy { + MutableLiveData<Int>(0) + } + + init { + torIntegrationAndroid.registerBootstrapStateChangeListener(this) + } + + override fun onCleared() { + torIntegrationAndroid.unregisterBootstrapStateChangeListener(this) + super.onCleared() + } + + override fun onBootstrapStateChange(state: String?) {} + + override fun onBootstrapProgress(progress: Double, hasWarnings: Boolean) { + this.progress.value = progress.toInt() + } + + override fun onBootstrapComplete() {} + + override fun onBootstrapError( + code: String?, + message: String?, + phase: String?, + reason: String?, + ) { + } +}
===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistFragment.kt ===================================== @@ -35,12 +35,14 @@ import org.mozilla.fenix.ext.hideToolbar class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
private val TAG = "TorConnectionAssistFrag" - private val viewModel: TorConnectionAssistViewModel by activityViewModels() + 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!!
- private val quickstartViewModel: QuickstartViewModel by activityViewModels() - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -49,18 +51,23 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { _binding = FragmentTorConnectionAssistBinding.inflate( inflater, container, false, ) + viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.collectLastKnownStatus() + torConnectionAssistViewModel.collectLastKnownStatus() } }
- viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.torConnectScreen.collect { screen -> - Log.d(TAG, "torConnectScreen is $screen") - showScreen(screen) - } + torConnectionAssistViewModel.shouldOpenHome.observe(viewLifecycleOwner) { + Log.d(TAG, "shouldOpenHome = $it") + if (it) { + openHome() + } + } + + urlQuickLoadViewModel.urlToLoadAfterConnecting.observe(viewLifecycleOwner) { url -> + if (!url.isNullOrBlank()) { + torConnectionAssistViewModel.handleConnect() } }
@@ -75,10 +82,13 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState)
- viewModel.progress().observe( - viewLifecycleOwner, - ) { progress -> - setProgressBarCompat(progress) + viewLifecycleOwner.lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + torConnectionAssistViewModel.torConnectScreen.collect { screen -> + Log.d(TAG, "torConnectScreen is $screen") + showScreen(screen) + } + } }
quickstartViewModel.quickstart().observe( @@ -87,13 +97,10 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { binding.quickstartSwitch.isChecked = it }
- viewModel.shouldOpenHome().observe( + progressViewModel.progress.observe( viewLifecycleOwner, - ) { - Log.d(TAG, "shouldOpenHome() = $it") - if (it) { - openHome() - } + ) { progress -> + setProgressBarCompat(progress) }
} @@ -142,7 +149,7 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { private fun setBackButton(screen: ConnectAssistUiState) { binding.backButton.visibility = if (screen.backButtonVisible) View.VISIBLE else View.INVISIBLE binding.backButton.setOnClickListener { - viewModel.handleBackButtonPressed() + torConnectionAssistViewModel.handleBackButtonPressed() } }
@@ -204,10 +211,7 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { if (screen.torBootstrapButton1Visible) View.VISIBLE else View.GONE binding.torBootstrapButton1.text = getString(screen.torBootstrapButton1TextStringResource) binding.torBootstrapButton1.setOnClickListener { - viewModel.handleButton1Pressed( - screen, - lifecycleScope, - ) + torConnectionAssistViewModel.handleConnect() } }
@@ -231,7 +235,7 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { } } binding.torBootstrapButton2.setOnClickListener { - viewModel.cancelTorBootstrap() + torConnectionAssistViewModel.cancelTorBootstrap() if (screen.torBootstrapButton2ShouldOpenSettings) { openTorConnectionSettings() } else if (screen.torBootstrapButton2ShouldRestartApp) { @@ -279,7 +283,9 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
private fun openHome() { Log.d(TAG, "openHome()") - viewModel.openHome(findNavController()) + findNavController().navigate( + TorConnectionAssistFragmentDirections.actionHome(), + ) }
private fun openSettings(preferenceToScrollTo: String? = null) { @@ -308,7 +314,7 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { }
override fun onBackPressed(): Boolean { - return viewModel.handleBackButtonPressed() + return torConnectionAssistViewModel.handleBackButtonPressed() }
}
===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistViewModel.kt ===================================== @@ -7,91 +7,42 @@ package org.mozilla.fenix.tor import android.app.Application import android.util.Log import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.LifecycleCoroutineScope -import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.navigation.NavController import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import org.mozilla.fenix.ext.components
class TorConnectionAssistViewModel( application: Application, -) : AndroidViewModel(application), TorEvents { +) : AndroidViewModel(application) {
private val TAG = "torConnectionAssistVM" - private val components = getApplication<Application>().components - - private val _torController: TorControllerGV = components.torController + private val torIntegrationAndroid = + application.components.core.geckoRuntime.torIntegrationController + private val _torController: TorControllerGV = application.components.torController
private val _torConnectScreen = MutableStateFlow(ConnectAssistUiState.Splash) internal val torConnectScreen: StateFlow<ConnectAssistUiState> = _torConnectScreen
- private val _shouldOpenHome = MutableLiveData(false) - fun shouldOpenHome(): LiveData<Boolean> { - return _shouldOpenHome - } - - private val _progress = MutableLiveData(0) - fun progress(): LiveData<Int> { - return _progress - } - - init { - Log.d(TAG, "initiating TorConnectionAssistViewModel $this") - _torController.registerTorListener(this) - } - - var urlToLoadAfterConnecting: String? = null - - fun handleConnect( - urlToLoadAfterConnecting: String? = null, - withDebugLogging: Boolean = false, - lifecycleScope: LifecycleCoroutineScope? = null, - ) { - this.urlToLoadAfterConnecting = urlToLoadAfterConnecting - if (_torController.lastKnownStatus.value.isOff()) { - Log.d(TAG, "handleConnect() triggered, initiatingTorBootstrap") - _torController.initiateTorBootstrap( - withDebugLogging = withDebugLogging, - lifecycleScope = lifecycleScope, - ) - } + val shouldOpenHome: MutableLiveData<Boolean> by lazy { + MutableLiveData(false) }
- fun handleButton1Pressed( - screen: ConnectAssistUiState, - lifecycleScope: LifecycleCoroutineScope?, - ) { - if (screen.torBootstrapButton1ShouldShowTryingABridge) { + fun handleConnect() { + if (_torConnectScreen.value.torBootstrapButton1ShouldShowTryingABridge) { tryABridge() } else { - handleConnect(lifecycleScope = lifecycleScope) + if (_torController.lastKnownStatus.value.isOff()) { + torIntegrationAndroid.beginBootstrap() + } } }
fun cancelTorBootstrap() { - _torController.stopTor() + torIntegrationAndroid.cancelBootstrap() _torController.setTorStopped() }
- override fun onTorConnecting() { - Log.d(TAG, "onTorConnecting()") - } - - override fun onTorConnected() { - Log.d(TAG, "onTorConnected()") - _torController.unregisterTorListener(this) - } - - override fun onTorStatusUpdate(entry: String?, status: String?, progress: Double?) { - Log.d(TAG, "onTorStatusUpdate($entry, $status, $progress)") - if (progress != null) { - _progress.value = progress.toInt() - } - - } - suspend fun collectLastKnownStatus() { _torController.lastKnownStatus.collect { when (it) { @@ -99,8 +50,8 @@ class TorConnectionAssistViewModel( TorConnectState.Configuring -> handleConfiguring() TorConnectState.AutoBootstrapping -> handleBootstrap() TorConnectState.Bootstrapping -> handleBootstrap() - TorConnectState.Bootstrapped -> _shouldOpenHome.value = true - TorConnectState.Disabled -> _shouldOpenHome.value = true + TorConnectState.Bootstrapped -> shouldOpenHome.value = true + TorConnectState.Disabled -> shouldOpenHome.value = true TorConnectState.Error -> handleError() } } @@ -145,10 +96,7 @@ class TorConnectionAssistViewModel( }
else -> _torConnectScreen.value = - ConnectAssistUiState.Connecting.also { connectAssistUiState -> - // covers the case of when the bootstrap is already in progress when the UiState "catches up" - connectAssistUiState.progress = _progress.value ?: 0 - } + ConnectAssistUiState.Connecting } }
@@ -184,10 +132,6 @@ class TorConnectionAssistViewModel( _torConnectScreen.value = ConnectAssistUiState.InternetError }
- override fun onTorStopped() { - Log.d(TAG, "onTorStopped()") - } - private fun tryABridge() { if (!locationFound()) { _torConnectScreen.value = ConnectAssistUiState.LocationError @@ -198,7 +142,7 @@ class TorConnectionAssistViewModel( _torController.bridgeTransport = TorBridgeTransportConfig.BUILTIN_SNOWFLAKE // TODO select based on country } - handleConnect(withDebugLogging = true) + torIntegrationAndroid.beginBootstrap() }
private fun locationFound(): Boolean { @@ -249,10 +193,4 @@ class TorConnectionAssistViewModel( } return true } - - fun openHome(navController: NavController) { - navController.navigate( - TorConnectionAssistFragmentDirections.actionHome(), - ) - } }
===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/UrlQuickLoadViewModel.kt ===================================== @@ -0,0 +1,10 @@ +package org.mozilla.fenix.tor + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel + +class UrlQuickLoadViewModel : ViewModel() { + val urlToLoadAfterConnecting: MutableLiveData<String?> by lazy { + MutableLiveData<String?>(null) + } +}
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/commit/d3a9fcd2...
tbb-commits@lists.torproject.org