[tbb-commits] [Git][tpo/applications/firefox-android][firefox-android-115.2.1-13.5-1] fixup! Implement Android-native Connection Assist UI

Dan Ballard (@dan) git at gitlab.torproject.org
Thu May 9 21:07:43 UTC 2024



Dan Ballard pushed to branch firefox-android-115.2.1-13.5-1 at The Tor Project / Applications / firefox-android


Commits:
a00861a6 by clairehurst at 2024-05-09T14:47:16-06:00
fixup! Implement Android-native Connection Assist UI

- - - - -


4 changed files:

- fenix/app/src/main/java/org/mozilla/fenix/tor/ConnectAssistUiState.kt
- fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistFragment.kt
- fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistViewModel.kt
- fenix/app/src/main/res/layout/fragment_tor_connection_assist.xml


Changes:

=====================================
fenix/app/src/main/java/org/mozilla/fenix/tor/ConnectAssistUiState.kt
=====================================
@@ -31,7 +31,8 @@ enum class ConnectAssistUiState(
     val torBootstrapButton2Visible: Boolean,
     @StringRes val torBootstrapButton2TextStringResource: Int? = R.string.connection_assist_configure_connection_button,
     val torBootstrapButton2ShouldOpenSettings: Boolean = true,
-    val wordmarkLogoVisible: Boolean,
+    val wordmarkLogoVisible: Boolean = false,
+    val torBootstrapButton2ShouldRestartApp: Boolean = false,
 ) {
     Splash(
         progressBarVisible = false,
@@ -65,9 +66,8 @@ enum class ConnectAssistUiState(
         torBootstrapButton2Visible = true,
         torBootstrapButton2TextStringResource = R.string.connection_assist_configure_connection_button,
         torBootstrapButton2ShouldOpenSettings = true,
-        wordmarkLogoVisible = false,
     ),
-    Bootstrapping(
+    Connecting(
         progressBarVisible = true,
         progress = 0,
         backButtonVisible = false,
@@ -85,7 +85,6 @@ enum class ConnectAssistUiState(
         torBootstrapButton2Visible = true,
         torBootstrapButton2TextStringResource = R.string.btn_cancel,
         torBootstrapButton2ShouldOpenSettings = false,
-        wordmarkLogoVisible = false,
     ),
     InternetError(
         progressBarVisible = true,
@@ -109,7 +108,6 @@ enum class ConnectAssistUiState(
         torBootstrapButton2Visible = true,
         torBootstrapButton2TextStringResource = R.string.connection_assist_configure_connection_button,
         torBootstrapButton2ShouldOpenSettings = true,
-        wordmarkLogoVisible = false,
     ),
     TryingAgain(
         progressBarVisible = true,
@@ -132,9 +130,8 @@ enum class ConnectAssistUiState(
         torBootstrapButton2Visible = true,
         torBootstrapButton2TextStringResource = R.string.btn_cancel,
         torBootstrapButton2ShouldOpenSettings = false,
-        wordmarkLogoVisible = false,
     ),
-    TryABridge(
+    ConnectionAssist(
         progressBarVisible = true,
         progress = 100,
         progressTintColorResource = R.color.warning_yellow,
@@ -157,7 +154,6 @@ enum class ConnectAssistUiState(
         torBootstrapButton2Visible = false,
         torBootstrapButton2TextStringResource = null,
         torBootstrapButton2ShouldOpenSettings = true,
-        wordmarkLogoVisible = false,
     ),
     TryingABridge(
         progressBarVisible = true,
@@ -180,7 +176,6 @@ enum class ConnectAssistUiState(
         torBootstrapButton2Visible = true,
         torBootstrapButton2TextStringResource = R.string.btn_cancel,
         torBootstrapButton2ShouldOpenSettings = false,
-        wordmarkLogoVisible = false,
     ),
     LocationError(
         progressBarVisible = true,
@@ -207,7 +202,6 @@ enum class ConnectAssistUiState(
         torBootstrapButton2Visible = false,
         torBootstrapButton2TextStringResource = null,
         torBootstrapButton2ShouldOpenSettings = true,
-        wordmarkLogoVisible = false,
     ),
     LocationCheck(
         progressBarVisible = true,
@@ -234,7 +228,6 @@ enum class ConnectAssistUiState(
         torBootstrapButton2Visible = false,
         torBootstrapButton2TextStringResource = null,
         torBootstrapButton2ShouldOpenSettings = true,
-        wordmarkLogoVisible = false,
     ),
     LastTry(
         progressBarVisible = true,
@@ -258,7 +251,6 @@ enum class ConnectAssistUiState(
         torBootstrapButton2Visible = true,
         torBootstrapButton2TextStringResource = R.string.btn_cancel,
         torBootstrapButton2ShouldOpenSettings = false,
-        wordmarkLogoVisible = false,
     ),
     FinalError(
         progressBarVisible = true,
@@ -279,10 +271,10 @@ enum class ConnectAssistUiState(
         unblockTheInternetInCountryDescriptionVisible = false,
         countryDropDownVisible = false,
         torBootstrapButton1Visible = true,
-        torBootstrapButton1TextStringResource = R.string.connection_assist_internet_error_try_again,
+        torBootstrapButton1TextStringResource = R.string.connection_assist_configure_connection_button,
         torBootstrapButton2Visible = true,
-        torBootstrapButton2TextStringResource = R.string.connection_assist_configure_connection_button,
-        torBootstrapButton2ShouldOpenSettings = true,
-        wordmarkLogoVisible = false,
+        torBootstrapButton2TextStringResource = R.string.mozac_lib_crash_dialog_button_restart,
+        torBootstrapButton2ShouldOpenSettings = false,
+        torBootstrapButton2ShouldRestartApp = true,
     )
 }


=====================================
fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistFragment.kt
=====================================
@@ -4,6 +4,7 @@
 
 package org.mozilla.fenix.tor
 
+import android.content.Intent
 import android.graphics.Color
 import android.os.Build
 import android.os.Bundle
@@ -25,6 +26,7 @@ import androidx.lifecycle.repeatOnLifecycle
 import androidx.navigation.fragment.findNavController
 import kotlinx.coroutines.launch
 import mozilla.components.support.base.feature.UserInteractionHandler
+import org.mozilla.fenix.HomeActivity
 import org.mozilla.fenix.R
 import org.mozilla.fenix.databinding.FragmentTorConnectionAssistBinding
 import org.mozilla.fenix.ext.hideToolbar
@@ -33,14 +35,15 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
 
     private val TAG = "TorConnectionAssistFrag"
     private val viewModel: TorConnectionAssistViewModel by viewModels()
-    private lateinit var binding: FragmentTorConnectionAssistBinding
+    private var _binding: FragmentTorConnectionAssistBinding? = null
+    private val binding get() = _binding!!
 
     override fun onCreateView(
         inflater: LayoutInflater,
         container: ViewGroup?,
         savedInstanceState: Bundle?,
     ): View {
-        binding = FragmentTorConnectionAssistBinding.inflate(
+        _binding = FragmentTorConnectionAssistBinding.inflate(
             inflater, container, false,
         )
 
@@ -90,96 +93,148 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
 
     }
 
-    override fun onDestroyView() {
-        super.onDestroyView()
-    }
-
     private fun showScreen(screen: ConnectAssistUiState) {
-        binding.apply {
-            torBootstrapProgressBar.visibility = if (screen.progressBarVisible) View.VISIBLE else View.GONE
-            torBootstrapProgressBar.progress = screen.progress
-            torBootstrapProgressBar.progressTintList =
-                screen.progressTintColorResource?.let {
-                    AppCompatResources.getColorStateList(requireContext(),
-                        it
-                    )
-                }
-
-            settingsButton.visibility = if (screen.settingsButtonVisible) View.VISIBLE else View.GONE
-            settingsButton.setOnClickListener {
-                viewModel.cancelTorBootstrap()
-                openSettings()
-            }
+        setProgressBar(screen)
+        setSettingsButton(screen)
+        setBackButton(screen)
+        setTorConnectImage(screen)
+        setTitle(screen)
+        setQuickStart(screen)
+        setCountryDropDown(screen)
+        setButton1(screen)
+        setButton2(screen)
+        setSplashLogo(screen)
+    }
 
-            backButton.visibility = if (screen.backButtonVisible) View.VISIBLE else View.INVISIBLE
-            backButton.setOnClickListener {
-                viewModel.handleBackButtonPressed()
+    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,
+                )
             }
+    }
 
-            torConnectImage.visibility = if (screen.torConnectImageVisible) View.VISIBLE else View.GONE
-            torConnectImage.setImageResource(screen.torConnectImageResource)
-
-            titleLargeTextView.visibility = if (screen.titleLargeTextViewVisible) View.VISIBLE else View.GONE
-            titleLargeTextView.text = getString(screen.titleLargeTextViewTextStringResource)
-            titleDescription.visibility = if (screen.titleDescriptionVisible) View.VISIBLE else View.GONE
-            if (screen.learnMoreStringResource != null && screen.internetErrorDescription != null) {
-                val learnMore: String = getString(screen.learnMoreStringResource)
-                val internetErrorDescription: String =
-                    if (screen.internetErrorDescription1 == null) {
-                        getString(
-                            screen.internetErrorDescription,
-                            learnMore,
-                        )
-                    } else if (screen.internetErrorDescription2 == null) {
-                        getString(
-                            screen.internetErrorDescription,
-                            getString(screen.internetErrorDescription1),
-                            learnMore,
-                        )
-                    } else {
-                        getString(
-                            screen.internetErrorDescription,
-                            getString(screen.internetErrorDescription1),
-                            getString(screen.internetErrorDescription2),
-                            learnMore,
-                        )
-                    }
-                handleDescriptionWithClickable(internetErrorDescription, learnMore)
-            } else if (screen.titleDescriptionTextStringResource != null) {
-                titleDescription.text = getString(screen.titleDescriptionTextStringResource)
-            }
-            quickstartSwitch.visibility = if (screen.quickstartSwitchVisible) View.VISIBLE else View.GONE
-            quickstartSwitch.isChecked = viewModel.quickstartToggle().value == true
-            quickstartSwitch.setOnCheckedChangeListener { _, isChecked ->
-                viewModel.handleQuickstartChecked(isChecked)
-            }
+    private fun setSettingsButton(screen: ConnectAssistUiState) {
+        binding.settingsButton.visibility = if (screen.settingsButtonVisible) View.VISIBLE else View.GONE
+        binding.settingsButton.setOnClickListener {
+            viewModel.cancelTorBootstrap()
+            openSettings()
+        }
+    }
 
-            unblockTheInternetInCountryDescription.visibility = if (screen.unblockTheInternetInCountryDescriptionVisible) View.VISIBLE else View.GONE
-            countryDropDown.visibility = if (screen.countryDropDownVisible) View.VISIBLE else View.GONE
+    private fun setBackButton(screen: ConnectAssistUiState) {
+        binding.backButton.visibility = if (screen.backButtonVisible) View.VISIBLE else View.INVISIBLE
+        binding.backButton.setOnClickListener {
+            viewModel.handleBackButtonPressed()
+        }
+    }
 
-            torBootstrapButton1.visibility = if (screen.torBootstrapButton1Visible) View.VISIBLE else View.GONE
-            torBootstrapButton1.text = getString(screen.torBootstrapButton1TextStringResource)
-            torBootstrapButton1.setOnClickListener { viewModel.handleButton1Pressed(screen, lifecycleScope) }
+    private fun setTorConnectImage(screen: ConnectAssistUiState) {
+        binding.torConnectImage.visibility = if (screen.torConnectImageVisible) View.VISIBLE else View.GONE
+        binding.torConnectImage.setImageResource(screen.torConnectImageResource)
+    }
 
-            torBootstrapButton2.visibility = if (screen.torBootstrapButton2Visible) View.VISIBLE else View.GONE
-            torBootstrapButton2.text = screen.torBootstrapButton2TextStringResource?.let {
-                getString(
-                    it
-                )
-            }
-            torBootstrapButton2.setOnClickListener {
-                viewModel.cancelTorBootstrap()
-                if (screen.torBootstrapButton2ShouldOpenSettings){
-                    openTorConnectionSettings()
+    private fun setTitle(screen: ConnectAssistUiState) {
+        binding.titleLargeTextView.visibility =
+            if (screen.titleLargeTextViewVisible) View.VISIBLE else View.GONE
+        binding.titleLargeTextView.text = getString(screen.titleLargeTextViewTextStringResource)
+        binding.titleDescription.visibility =
+            if (screen.titleDescriptionVisible) View.VISIBLE else View.GONE
+        if (screen.learnMoreStringResource != null && screen.internetErrorDescription != null) {
+            val learnMore: String = getString(screen.learnMoreStringResource)
+            val internetErrorDescription: String =
+                if (screen.internetErrorDescription1 == null) {
+                    getString(
+                        screen.internetErrorDescription,
+                        learnMore,
+                    )
+                } else if (screen.internetErrorDescription2 == null) {
+                    getString(
+                        screen.internetErrorDescription,
+                        getString(screen.internetErrorDescription1),
+                        learnMore,
+                    )
                 } else {
-                    showScreen(ConnectAssistUiState.Configuring)
+                    getString(
+                        screen.internetErrorDescription,
+                        getString(screen.internetErrorDescription1),
+                        getString(screen.internetErrorDescription2),
+                        learnMore,
+                    )
                 }
-            }
+            handleDescriptionWithClickable(internetErrorDescription, learnMore)
+        } else if (screen.titleDescriptionTextStringResource != null) {
+            binding.titleDescription.text = getString(screen.titleDescriptionTextStringResource)
+        }
+    }
+
+    private fun setQuickStart(screen: ConnectAssistUiState) {
+        binding.quickstartSwitch.visibility =
+            if (screen.quickstartSwitchVisible) View.VISIBLE else View.GONE
+        binding.quickstartSwitch.isChecked = viewModel.quickstartToggle().value == true
+        binding.quickstartSwitch.setOnCheckedChangeListener { _, isChecked ->
+            viewModel.handleQuickstartChecked(isChecked)
+        }
+    }
+
+    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
+    }
 
-            wordmarkLogo.visibility = if(screen.wordmarkLogoVisible) View.VISIBLE else View.GONE
+    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 {
+            viewModel.handleButton1Pressed(
+                screen,
+                lifecycleScope,
+            )
+        }
+    }
+
+    private fun setButton2(screen: ConnectAssistUiState) {
+        binding.torBootstrapButton2.visibility =
+            if (screen.torBootstrapButton2Visible) View.VISIBLE else View.GONE
+        if (screen.torBootstrapButton2ShouldRestartApp) {
+            binding.torBootstrapButton2.text =
+                screen.torBootstrapButton2TextStringResource?.let {
+                    getString(
+                        it,
+                        getString(R.string.app_name),
+                    )
+                }
+        } else {
+            binding.torBootstrapButton2.text =
+                screen.torBootstrapButton2TextStringResource?.let {
+                    getString(
+                        it,
+                    )
+                }
+        }
+        binding.torBootstrapButton2.setOnClickListener {
+            viewModel.cancelTorBootstrap()
+            if (screen.torBootstrapButton2ShouldOpenSettings) {
+                openTorConnectionSettings()
+            } else if (screen.torBootstrapButton2ShouldRestartApp) {
+                restartApplication()
+            } else {
+                showScreen(ConnectAssistUiState.Configuring)
+            }
         }
     }
 
+    private fun setSplashLogo(screen: ConnectAssistUiState) {
+        binding.wordmarkLogo.visibility = if (screen.wordmarkLogoVisible) View.VISIBLE else View.GONE
+    }
+
     /**
      * from https://stackoverflow.com/questions/10696986/how-to-set-the-part-of-the-text-view-is-clickable
      */
@@ -207,6 +262,7 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
     }
 
     private fun showLearnMore() {
+        Log.d(TAG, "showLearnMore() tapped")
         //TODO("Not yet implemented")
     }
 
@@ -231,6 +287,15 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
         )
     }
 
+    private fun restartApplication() {
+        startActivity(
+            Intent(requireContext(), HomeActivity::class.java).addFlags(
+                Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK,
+            ),
+        )
+        Runtime.getRuntime().exit(0)
+    }
+
     override fun onBackPressed(): Boolean {
         return viewModel.handleBackButtonPressed()
     }


=====================================
fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistViewModel.kt
=====================================
@@ -124,7 +124,7 @@ class TorConnectionAssistViewModel(
                 /** stay here */
             }
 
-            ConnectAssistUiState.TryABridge -> {
+            ConnectAssistUiState.ConnectionAssist -> {
                 _torConnectScreen.value = ConnectAssistUiState.TryingABridge
             }
 
@@ -144,7 +144,7 @@ class TorConnectionAssistViewModel(
                 /** stay here */
             }
 
-            else -> _torConnectScreen.value = ConnectAssistUiState.Bootstrapping
+            else -> _torConnectScreen.value = ConnectAssistUiState.Connecting
         }
     }
 
@@ -155,28 +155,58 @@ class TorConnectionAssistViewModel(
                 "TorError(message = $message, details = $details, phase = $phase, reason = $reason",
             )
             // TODO better error handling
-            _torConnectScreen.value = ConnectAssistUiState.InternetError
+            when (reason) {
+//                "noroute" -> handleNoRoute() TODO re-add when working better
+                else -> handleUnknownError()
+            }
+        }
+    }
+
+    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
         }
     }
 
+    private fun handleUnknownError() {
+        // TODO should we have a dedicated screen for unknown errors?
+        _torConnectScreen.value = ConnectAssistUiState.InternetError
+    }
+
     override fun onTorStopped() {
         Log.d(TAG, "onTorStopped()")
     }
 
     private fun tryABridge() {
+        if (!locationFound()) {
+            _torConnectScreen.value = ConnectAssistUiState.LocationError
+            return
+        }
         if (!_torController.bridgesEnabled) {
             _torController.bridgesEnabled = true
             _torController.bridgeTransport =
-                TorBridgeTransportConfig.BUILTIN_OBFS4 // TODO select based on country
+                TorBridgeTransportConfig.BUILTIN_SNOWFLAKE // TODO select based on country
         }
         handleConnect(withDebugLogging = true)
     }
 
+    private fun locationFound(): Boolean {
+        // TODO try to find location
+        return true
+    }
+
     fun handleBackButtonPressed(): Boolean {
         when (torConnectScreen.value) {
             ConnectAssistUiState.Splash -> return false
             ConnectAssistUiState.Configuring -> return false
-            ConnectAssistUiState.Bootstrapping -> cancelTorBootstrap()
+            ConnectAssistUiState.Connecting -> cancelTorBootstrap()
             ConnectAssistUiState.InternetError -> {
                 _torController.lastKnownError = null
                 _torConnectScreen.value = ConnectAssistUiState.Configuring
@@ -186,18 +216,18 @@ class TorConnectionAssistViewModel(
                 cancelTorBootstrap()
             }
 
-            ConnectAssistUiState.TryABridge -> {
+            ConnectAssistUiState.ConnectionAssist -> {
                 _torController.lastKnownError = null
                 _torConnectScreen.value = ConnectAssistUiState.Configuring
             }
 
             ConnectAssistUiState.TryingABridge -> {
                 _torController.stopTor()
-                _torConnectScreen.value = ConnectAssistUiState.TryABridge
+                _torConnectScreen.value = ConnectAssistUiState.ConnectionAssist
             }
 
             ConnectAssistUiState.LocationError -> {
-                _torConnectScreen.value = ConnectAssistUiState.TryABridge
+                _torConnectScreen.value = ConnectAssistUiState.ConnectionAssist
             }
 
             ConnectAssistUiState.LocationCheck -> {


=====================================
fenix/app/src/main/res/layout/fragment_tor_connection_assist.xml
=====================================
@@ -126,7 +126,6 @@
         android:layout_marginTop="8dp"
         android:layout_marginEnd="24dp"
         android:textColor="@color/photonLightGrey05"
-        android:tooltipText="@string/connection_assist_share_my_location_country_or_region"
         android:visibility="gone"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"



View it on GitLab: https://gitlab.torproject.org/tpo/applications/firefox-android/-/commit/a00861a6231a712389ae1776c00e1ef1a9a313c9

-- 
This project does not include diff previews in email notifications.
View it on GitLab: https://gitlab.torproject.org/tpo/applications/firefox-android/-/commit/a00861a6231a712389ae1776c00e1ef1a9a313c9
You're receiving this email because of your account on gitlab.torproject.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.torproject.org/pipermail/tbb-commits/attachments/20240509/b20ee9c2/attachment-0001.htm>


More information about the tbb-commits mailing list