Commit d3a9fcd2 authored by clairehurst's avatar clairehurst 🌱
Browse files

fixup! [android] Implement Android-native Connection Assist UI

parent cbe2d643
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -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 {
+6 −4
Original line number Diff line number Diff line
@@ -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 {
        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
            }
        }

+44 −0
Original line number Diff line number Diff line
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?,
    ) {
    }
}
+33 −27
Original line number Diff line number Diff line
@@ -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()
    }

}
+16 −78
Original line number Diff line number Diff line
@@ -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(),
        )
    }
}
Loading